如何读取文本文件并在重复的部分中循环

本文关键字:循环 取文本 何读 文件 | 更新日期: 2023-09-27 17:57:56

我有一个ANSI 835(文本)文件。为了简单起见,它看起来像这样:

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

这些记录被分解为LX段。LX*1之后的所有内容都是一条记录,LX*2之后的所有信息都是另一条记录等等。我需要从每一行中获取某些项目,将它们分配给变量,并最终将它们作为一行添加到数据网格视图中。同样,为了简单起见,我有以下变量,下面是每个变量中应该包含的内容:

string ItemNumber应该是CLP行中*后面的一组字符
string LastName应该是NM1行中*后面的一组字符
string Date应该是REF行中*之后的一组字符
string Error应该是LQ行中*之后的一组字符

我面临的最大问题是,在每个LX段中可能有不止一条LQ线。在这种情况下,第二个错误可以添加到第一个错误的末尾,用逗号分隔。

我试着将文件加载到一个字符串数组中,并逐行进行,但我不知道如何说"从LX*1开始,做一些事情,直到达到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.
    }
}

有什么想法吗?一如既往,感谢您抽出时间!

如何读取文本文件并在重复的部分中循环

从一个简单的数据模型开始:

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

定义您的重要代币:

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

循环遍历行,对令牌进行切换/大小写,当您看到"LX"标志时,只需启动一个新的LXRecord

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

注意,通过这种方式,您可以相对容易地忽略不支持的标志、添加新标志或添加解析(例如,ItemNumber可以使用Int32.Parse并将其存储为整数,或者"Date"可以存储DateTime)。在这种情况下,我选择将错误存储为List<String>,但如果您愿意,可以用逗号分隔。我还避免在*字符上进行拆分,以防内容也包含第二个星号。

编辑:根据您的评论,您可以在case中进行一些更复杂/更专业的解析,或者转移到另一种方法中。与我上面提到的"LAST_NAME"不同,您可以使用:

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

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

令牌检查可能会在那里进行调整,但它应该会给你一个好主意。