How to read a text file and loop while repeating sections?

I have an ANSI 835 (text) file. For simplicity, it looks like this:

ISA*00
GS*Foo*12345
ST*835*000001
LX*1
CLP*123456
NM1*Lastname
REF*010101
DTM*20120512
SVC*393939
LQ*19
LX*2
CLP*23456
NM1*Smith
REF*58774
DTM*20120601
SVC*985146
LX*3
CLP*34567
NM1*Doe
REF*985432
DTM*20121102
SVC*864253
LQ*19
LQ*84

      

The records are split into LX segments. Everything after LX * 1 is one entry, everything after LX * 2 is another entry, and so on. I need to get certain elements from each row, assign them to variables, and eventually add them as a row in the datagridview. Again for simplicity, I have the following variables, and this is what should go in each:

string ItemNumber

there must be a group of characters after the * in the CLP line there
string LastName

must be a group of characters after the line * in the NM1 line there
string Date

must be a group of characters after the * in the REF line there
string Error

must be a group of characters after the * in the LQ line

The biggest problem I am facing is that there can be more than one LQ row in each LX segment. In this case, the second error can only be appended to the end of the first error, separated by a comma.

I tried to load the file into a string array and step through it, but I'm not sure how to say "start at LX * 1 and do something until you hit LX * 2".

string[] lines = File.ReadAllLines(MyFile);

foreach (string line in lines)
{
    string[] splitline = line.Split('*');

    if (splitline[0] = "LX")
    {
        //this is where i need to loop through the next lines 
        //until i hit the next line starting with LX.
    }
}

      

Any ideas? As always, thanks for your time!

+3


source to share


1 answer


Start with a simple data model:

public class LXRecord
{
    public string ItemNumber { get; set; }
    public string LastName { get; set; }
    public string Date { get; set; }
    public List<string> Errors { get; set; }

    public LXRecord()
    {
        Errors = new List<String>();
    }
}

      

Define your important tokens:

public static class Tokens
{
    public const string TOKEN_SPLITTER = "*";
    public const string NEW_RECORD = "LX";
    public const string ITEM_NUMBER = "CLP";
    public const string LAST_NAME = "NM1";
    public const string DATE = "REF";
    public const string ERROR = "LQ";
}

      

Loop through the lines, make a switch / register on tokens and just start a new LXRecord

one when you see the "LX" flag:

List<LXRecord> records = new List<LXRecord>();
LXRecord currentRecord = null;

foreach(string line in lines)
{
    int tokenIndex = line.IndexOf(Tokens.TOKEN_SPLITTER);
    if (tokenIndex < 1 || tokenIndex == line.Length - 1) //no token or no value?
        continue;

    string token = line.Substring(0, tokenIndex);
    string value = line.Substring(tokenIndex + 1);

    switch(token)
    {
        case(Tokens.NEW_RECORD) :
            currentRecord = new LXRecord();
            records.Add(currentRecord);
            break;
        case(Tokens.ITEM_NUMBER) :
            currentRecord.ItemNumber = value;
            break;
        case(Tokens.LAST_NAME) :
            currentRecord.LastName = value;
            break;
        case(Tokens.DATE) :
            currentRecord.Date = value;
            break;
        case(Tokens.ERROR) :
            currentRecord.Errors.Add(value);
            break;
    }
}

      



Note that you can relatively easily ignore unsupported flags, add new flags, or add parsing (for example, ItemNumber

can use Int32.Parse

and store it as an integer, or "Date" can store DateTime

) In this case, I decided to keep the errors as List<String>

, but if you like, you could separate it with a comma. I also avoided splitting on a character *

if the content contained a second asterisk.

EDIT. From your comment, you can have more complex / specialized parsing in, case

or move it to another method. Instead of the case I have above for "LAST_NAME", you could:

case(Tokens.LAST_NAME) :
    ParseName(currentRecord, value);
    break;

      

Where ParseName

:

public static void ParseName(LXRecord record, string value)
{
    int tokenIndex = value.IndexOf(Tokens.TOKEN_SPLITTER);
    if (tokenIndex < 1 || tokenIndex == value.Length - 1) //no last name and first name?
    {
        record.LastName = value;
    }
    else
    {
        record.LastName = value.Substring(0, tokenIndex);
        record.FirstName = value.Substring(tokenIndex + 1);
    }
}

      

The marker check can be changed there, but it should give you a good idea.

+5


source







All Articles