Cameron Howsonn
Cameron Howsonn

Reputation: 13

Index was outside the bounds of the array fix when splitting from a .txt file

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

Answers (4)

ΩmegaMan
ΩmegaMan

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.

ListView Example

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);
                     });

Regex Example

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

Kaf
Kaf

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

Banketeshvar Narayan
Banketeshvar Narayan

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

Yair Nevet
Yair Nevet

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

Related Questions