Reputation: 13
I want to split my text that is read from a txt file using /
and *
.
However, I browse for the file and 'Index was outside the bounds of the array.'
comes up!
Any help would be appreciated
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string fileToOpen = ofd.FileName;
System.IO.StreamReader sr = new System.IO.StreamReader(fileToOpen);
string fileContent = sr.ReadLine();
string[] items = fileContent.Split('*').ToArray<string>();
string[] buffer;
foreach (string s in items)
{
ListViewItem lv = new ListViewItem();
buffer = s.Split('/').ToArray<string>();
lv.Text = items[0].ToString();
lv.SubItems.Add(items[1].ToString());
lv.SubItems.Add(items[2].ToString());
lv.SubItems.Add(items[3].ToString());
listView1.Items.Add(lv);
lv.Text = buffer[0];
lv.SubItems.Add(buffer[1]);
lv.SubItems.Add(buffer[2]);
listView1.Items.Add(lv);
}
}
is my code, thanks!
Upvotes: 0
Views: 2508
Reputation: 31576
To avoid index out of range, refactor your code in a linq fashion to handle the dynamic nature of the data and never index again. Also by using Regular expressions to parse the data it can be beneficial situation.
Here is an approximation of the process using regular expressions and Linq and the removal of most of the indexing when adding sub items into the listview. Also the data extraction from the the regular expression pattern uses named match groups and no indexing.
The overall concept is using a foreach instead of a for; per-se.
string data = "*Alpha/Beta/Gamma*Zebra/Delta/Tango";
string pattern = @"\*((?<SlashValues>[^/*]+)/?)+";
Regex.Matches(data, pattern, RegexOptions.IgnorePatternWhitespace)
.OfType<Match>()
.Select (mt => new {
LVI = new ListViewItem(),
SubItems = mt.Groups["SlashValues"].Captures
.OfType<Capture>()
.Select (cp => cp.Value),
}
)
.ToList()
.ForEach(item => {
item.LVI.Text = item.SubItems.FirstOrDefault(),
LVI.AddRange(item.SubItems.Skip(1));
listView1.Items.Add(item);
});
Taking out the ListViewItem code creates a better way to understand regex processing. Each match is a new ListView item where the pattern seeks a * as an anchor point. After that is found it then looks for multiple values divided by a / or * ultimately because it signals the end of the match and the start of a new match.
Try this is as a console app:
string data = "*Alpha/Beta/Gamma*Zebra/Delta/Tango";
// Put into group "SlashValues" into its capture collection
// [] is a set
// [^] ^ in the set means NOT
// [^*/] Not a * or /
// [^*/]+ + means one or more
// which gets all characters up to a / or *
string pattern = @"\*((?<SlashValues>[^/*]+)/?)+";
Regex.Matches(data, pattern, RegexOptions.IgnorePatternWhitespace)
.OfType<Match>()
.Select (mt => mt.Groups["SlashValues"].Captures
.OfType<Capture>()
.Select (cp => cp.Value))
.ToList()
.ForEach(rs => Console.WriteLine (string.Join(", ", rs)));
/*
Alpha, Beta, Gamma
Zebra, Delta, Tango
*/
Upvotes: 0
Reputation: 33809
Within the foreach loop, you are splitting the string again with '/'
as
buffer = s.Split('/').ToArray<string>();
If no '/'
found in above string (s
) your buffer may not have 2/3
items and so
lv.SubItems.Add(buffer[1]);
lv.SubItems.Add(buffer[2]);
above lines could cause index out of bound error.
Or it could be due to:
lv.SubItems.Add(items[1].ToString());
lv.SubItems.Add(items[2].ToString());
lv.SubItems.Add(items[3].ToString());
Array Index should always be less than the length (or number of items) in the array.
You can split your string using both '/'
and '*'
at the same time as;
fileContent.Split(new [] {"*", "/"}, StringSplitOptions.None).ToArray<string>();
Upvotes: 2
Reputation: 3899
Use items.Count to make sure how many element exists in items array. And use the index starting from 0 to less than items.Count.
i.e. code should be like this:
foreach (string s in items)
{
ListViewItem lv = new ListViewItem();
buffer = s.Split('/').ToArray<string>();
lv.Text = items[0].ToString();
for(int i=0; i<items.Count; i++)
{
lv.SubItems.Add(items[i].ToString());
}
listView1.Items.Add(lv);
lv.Text = buffer[0];
lv.SubItems.Add(buffer[1]);
lv.SubItems.Add(buffer[2]);
listView1.Items.Add(lv);
}
Upvotes: 2
Reputation: 13003
Your code made unsafe assumptions regarding arrays index size!
You should add few if
conditions in order to get protection against IndexOutOfRangeException
or to use loops instead.
For example, how do you know that you have 4 elements here:
lv.Text = items[0].ToString();
lv.SubItems.Add(items[1].ToString());
lv.SubItems.Add(items[2].ToString());
lv.SubItems.Add(items[3].ToString());
Do it like that to be on the safe side:
for(int i = 0; i < items.Length; i++)
{
lv.SubItems.Add(items[i])
}
Or at least check if you have 4 elements first:
if(items.Length == 4)
{
lv.Text = items[0].ToString();
lv.SubItems.Add(items[1].ToString());
lv.SubItems.Add(items[2].ToString());
lv.SubItems.Add(items[3].ToString());
}
Upvotes: 0