【问题标题】:How to split a text lines by fixed width C#如何按固定宽度 C# 分割文本行
【发布时间】:2013-11-13 13:21:06
【问题描述】:

有谁知道如何分割这个文件

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,但它在性能方面存在一些问题。

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

谢谢。

【问题讨论】:

  • 试试这个 Bing 搜索看看有没有什么适合你的 bing.com/…
  • (.{4})(.{22})(.{6})(.+)? 之类的东西应该可以正常工作。你试过什么没用?
  • 以上建议是以正则表达式为例 List result = new List(Regex.Split(target, @"(.{4})(.{22} )(.{6})(.+)?", RegexOptions.Singleline));

标签: c# regex split fixed-width textfieldparser


【解决方案1】:

没有任何理由不这样做,我可能只会使用Substring

话虽如此,Regex 也应该可以工作。

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

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   

【讨论】:

    【解决方案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));
    

    【讨论】:

    • 抱歉迟到了!!但我尝试了你的建议,但它把这条线分成了 9 块!!
    【解决方案3】:

    这个函数式方法怎么样?

    从这些数组开始:

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

    我得到的结果是:

    【讨论】:

      猜你喜欢
      • 2010-10-26
      • 2010-10-31
      • 2017-10-12
      • 1970-01-01
      • 1970-01-01
      • 2021-04-07
      • 1970-01-01
      • 2014-02-22
      • 1970-01-01
      相关资源
      最近更新 更多