views:

57

answers:

5

I receive a tab-delimited text file that must be parsed. Once parsed, the parts must be assigned to specific columns.

Here is an example of the code I'm using to do this:

    string path = "C:\\Users\\Robert\\Desktop\\Test.txt";

    FileInfo fileInfo = new FileInfo(path);

    using (StreamReader streamReader = fileInfo.OpenText())
    {
        string line = "";

        while ((line = streamReader.ReadLine()) != null)
        {
            string[] columns = line.Split('\t');

            Output0Buffer.AddRow();
            Output0Buffer.Column0 = columns[0];
            Output0Buffer.Column1 = columns[1];
            Output0Buffer.Column2 = columns[2];
            Output0Buffer.Column3 = columns[3];
            Output0Buffer.Column4 = columns[4];
        }
    }

The problem with this is that some of the lines in the text file don't have 5 columns and this code fails when it tries to assign the Column4 the value of columns[4] (in actuality, my real file has 21 parts, so this is more prone to failure).

How can a re-write this to only assign values to the Column4 (5,6 - 21) if there is actual data? Also, can this be written into a for or foreach loop to make it a bit tidier, so I don't have to have a line for all 21 columns?

Any help is greatly appreciated!

A: 

You can use the FileHelpers library.

SLaks
+1  A: 

You did not tell us the definition of Output0Buffer, but the most obvious solution would be to change its definition to allow indexing like this:

int maxCol = Math.Min(columns.Length,Output0Buffer.Columns.Count);
for(int colIDx=0;colIdx<maxCol;++colIdx)
   Output0Buffer.Columns[colIdx]=columns[colIdx];

(Beware, that was "air code".)

Doc Brown
A: 

You can do something like

Output0Buffer.AddRow()
for (ix = 0; ix < column.Length; ix++)
{
    switch(ix)
    { case 0:
        Output0Buffer.Column0 = columns[ix];
        break;
      case 1:
        ...
    }
}

Are the columns in your OutputBuffer named as Column0, Column1, etc? Is there any reason that they couldn't be an array? If they could be, then you could substitute

    Output0Buffer.Columns[ix] = columns[ix];

for the whole switch statement above. If they do have to be individually named, then I think you would have to use reflection to get the instance member for each column that corresponded to columns[ix].

I'm not sure, but it's possible that AutoMapper might handles something like that. You might investigate that.

Dave Hanna
A: 

There is an overload on .Split(...) that you could try to use.

string[] columns = (line + "\t\t\t\t").Split(new [] {'\t'}, 5);

Output0Buffer.AddRow();
Output0Buffer.Column0 = columns[0];
Output0Buffer.Column1 = columns[1];
Output0Buffer.Column2 = columns[2];
Output0Buffer.Column3 = columns[3];

// The trim is needed on this line because the split method
// will stop processing after it has hit the number of elements
// listed in the count value
Output0Buffer.Column4 = columns[4].TrimEnd('\t'); 

... Another option would be something like this ...

var parts = line.Split('\t');
var columns = new string[5];
if (parts.Length > columns.Length)
    throw new InvalidOperationException();
Array.Copy(parts, columns, parts.Length);

// the rest of your code goes here
Matthew Whited
+2  A: 

The simplest thing would be to add a Columns collection to the output buffer type and then set the values in a for loop e.g.

for(int i = 0; i < columns.Length; i++)
{
    Output0Buffer.Columns[i] = columns[i];
}

If you can't change that type and the property naming convention is consistent (i.e. column1,2,3...n) then you could set each property though reflection:

Type buffType = Output0Buffer.GetType();
for(int i = 0; i < columns.Length; i++)
{
    string propertyName = String.Format("Column{0}", i);
    PropertyInfo pi = buffType.GetProperty(propertyName);
    pi.SetValue(buffer, columns[i], null);
}
Lee
Thank you, Lee... this worked perfectly!
Robert