如何按固定宽度C#拆分文本行

时间:2013-10-29 04:22:43

标签: c# regex split fixed-width textfieldparser

有谁知道如何拆分此文件

1 TESTAAA      SERNUM    A DESCRIPTION
2 TESTBBB      ANOTHR    ANOTHER DESCRIPTION
3 TESTXXX      BLAHBL

每列有一个固定的宽度,我打算用正则表达式来做,但我不知道该怎么做。

{id} {firsttext} {serialhere} {description}
 4    22          6            30+

有人建议使用这样的模式(。{4})(。{22})(。{6})(。+)?然后用split('')拆分它,但是用户表示这不适用于列没有价值,但即便如此,他也没有做任何例子。

我也听说过TextFieldParser,但它有一些关于性能的问题。

有谁能告诉我如何按固定宽度分割?

感谢。

3 个答案:

答案 0 :(得分:4)

没有看到任何理由,我可能只会使用Substring

话虽如此,正则表达式也应该起作用。

以下示例适用于所显示的输入(而不是您给出的数字),并假设序列号是必填字段,但可能不占用其全部长度+描述是可选的。如果这些假设不正确,请按照该原则进行调整。

string input = @"1 TESTAAA      SERNUM    A DESCRIPTION
2 TESTBBB      ANOTHR    ANOTHER DESCRIPTION
3 TESTXXX      BLAHBL";

var split = input.Split('\n').Select(s => new {
        Id = s.Substring(0, 2),
        FirstText = s.Substring(2, 13),
        Serial = s.Substring(15, Math.Min(s.Length-15, 10)),
        Description = s.Length > 25 ? s.Substring(25) : String.Empty
 });

或者作为一个解释性示例,更明显的命名和更清晰的序列长度示例:

int idStart = 0;
int idLength = 2;
int firstTextStart = idStart + idLength;
int firstTextLength = 13;
int serialStart = firstTextStart + firstTextLength;
int serialLength = 10;
int descriptionStart = serialStart + serialLength;

var verboseSplit = input.Split('\n').Select(s => new {
    Id = s.Substring(idStart, idLength),
    FirstText = s.Substring(firstTextStart, firstTextLength),
    Serial = s.Length > descriptionStart
               ? s.Substring(serialStart, serialLength)
               : s.Substring(serialStart) 
    Description = s.Length > descriptionStart 
                    ? s.Substring(descriptionStart) 
                    : String.Empty
});

以下任一项的输出:

Id FirstText     Serial     Description 
1  TESTAAA       SERNUM     A DESCRIPTION

2  TESTBBB       ANOTHR     ANOTHER DESCRIPTION

3  TESTXXX       BLAHBL   

答案 1 :(得分:2)

根据你的样本试试这个,每个项目之间只有一个空格

{id} {firsttext} {serialhere} {description}
 4    22          6            30+

string target = "1    TESTAAA                SERNUM A DESCRIPTION";
List<string> result = new List<string>(Regex.Split(target, @"(.{4})(.{1})(.{22})(.{1})(.{6})(.{1})(.+)?", RegexOptions.Singleline));

答案 2 :(得分:1)

这种功能方法怎么样?

从这些数组开始:

var lines = new []
{
    "1 TESTAAA      SERNUM    A DESCRIPTION",
    "2 TESTBBB      ANOTHR    ANOTHER DESCRIPTION",
    "3 TESTXXX      BLAHBL",
};

var splits = new [] { 2, 13, 10, };

我使用的splits与您的问题不同,因为每个示例行中字段的长度与您的分割不匹配。

现在定义一个递归函数来分割每一行:

Func<string, IEnumerable<int>, IEnumerable<string>> f = null;
f =
    (t, ns) =>
    {
        if (ns.Any())
        {
            var n = ns.First();
            var i = System.Math.Min(n, t.Length);
            var t0 = t.Substring(0, i);
            var t1 = t.Substring(i);
            return new [] { t0.Trim(), }.Concat(f(t1, ns.Skip(1)));
        }
        else
            return new [] { t.Trim(), };
    };

最后,我们可以编写一个相当简单的linq查询来将它们整合在一起:

var query =
    from line in lines
    let fields = f(line, splits).ToArray()
    select new
    {
        id = fields[0],
        firsttext = fields[1],
        serialhere = fields[2],
        description = fields[3],
    };

我得到的结果是:

Results