使用正则表达式解析结构化文本

时间:2014-07-08 10:08:10

标签: c# .net regex multiline string-matching

我正在寻找2个正则表达式来正确解析下面的行块(每行是76个字符的固定长度):

让您了解结构:

  1. 前5个字符是位置(例如00250)
  2. 接下来3个字符是标记(例如SPS)
  3. 此后的文字是描述标签功能的自由文本(可以将其拆分为一行或多行,例如'摘要功能统计的采样参数)
  4. 接下来的1个字符可以是C(有条件)或M(强制)
  5. 接下来的1位数或更多位数是基数(例如9999)。
  6. 加号和竖线字符表示分组。
  7. 我要求将所有6个元素捕获到组中。

    示例

    00250   SPS Sampling parameters for summary                               |
                   functional                                                 |
                   statistics                            C   99               |
    
    00260   SPS Sampling parameters for summary                               |
                   functional                                                 |
                   statistics                            C   9999-------------+
    
    00270   SPS Sampling parameters for summary                               |
                   functional                                                 |
                   statistics                            C   9---------------+|
    
    00280   SPS Sampling parameters for summary                               |
                   functional                                                 |
                   statistics                            C   1--------------+||
    

    如上面的示例所示,可以有超过1个加号(+),因此正则表达式需要考虑到这一点。此外,管道符号(|)可以在该行的末尾出现多次。

    上面给出的示例都显示了一个描述,其中包含三行'采样参数,用于汇总功能统计数据'并且每个人分开'需要附加描述以形成单个字符串。

    我已经有一个正常工作的正则表达式用于解析第一行,所以我只需要另外2个来解析第2行和第2行。 3。

    这就是我对第2行的看法:

    (A) ^\s{1,}(.+)\s{1,}\|$

    第3行:

    (B) ^\s{1,}(.+)\s(C|M)\s{1,}(\d+)(?:\s{1,}\|*|-{1,}(\+{1,})\|*)*$

    问题是正则表达式(A)匹配第2行和第2行。 3,它应该只匹配第2行。有人能告诉我正确的正则表达式,以便我能正确解析这些行吗?

    如果有帮助here是我正在解析的全文的示例(第4.3.1节段表)。

4 个答案:

答案 0 :(得分:0)

您可以为这三行编写单个模式,使用命名捕获,您可以从块中提取所需的所有信息。此模式使用详细模式更具可读性:

(?<num>\d{5}) \s+ (?<letters>[A-Z]{3}) \s+ (?<desc>.+\S) \s+ \|
\s+
(?<func>\w+) \s+ \|
\s+
(?<stat>\w+) \s+ (?<letter>[A-Z]) \s+ (?<num2>\d+) [-\s]+ \+? \|{0,2}

demo

答案 1 :(得分:0)

你可以试试下面的正则表达式,

([0-9]{5})\s*([\w\s]*\w)\s*[^\w]*(\w*)[^\w]*(\w*)\s*(C|M)\s*(\d+).*

DEMO

答案 2 :(得分:0)

我不会使用单个getcha正则表达式,但我更喜欢按行进行工作。 正则表达式应检查我们是否具有前导数字,然后以状态机的形式输入该行。州立大学继续吃线,直到满足新的领先数字模式或EOF。 当状态机完成&#34;吃&#34;你知道最后一行的行必须包含在列位置&#34; C&#34;或者&#34; M&#34;,但你基本上可以用固定的列长度方式解析该行。

答案 3 :(得分:0)

我设法通过为我想要捕获的每一行创建一个正则表达式来解决这个问题,并且基本上编写了一个名为getLineType(string line)的函数,该函数计算每个正则表达式,直到找到匹配并返回LineType枚举标识匹配的行的类型。

以下是感兴趣的人的代码:

private Regex _segmentTableLabel;
Regex SegmentTableLabel
{
    get
    {
        if (_segmentTableLabel == null)
            _segmentTableLabel = new Regex( @"^4.3.1\s{1,}Segment table$" );

        return _segmentTableLabel;
    }
}

private Regex _headerOrDetailOrSummarySection;
Regex HeaderOrDetailOrSummarySection
{
    get
    {
        if (_headerOrDetailOrSummarySection == null)
            _headerOrDetailOrSummarySection = new Regex( @"^\s+(?:HEADER SECTION|DETAIL SECTION|SUMMARY SECTION)$" );

        return _headerOrDetailOrSummarySection;
    }
}

private Regex _blankLines;
Regex BlankLines
{
    get
    {
        if (_blankLines == null)
            _blankLines = new Regex( @"^\s+\|*" );

        return _blankLines;
    }
}

private Regex _segmentTableHeader;
Regex SegmentTableHeader
{
    get
    {
        if (_segmentTableHeader == null)
            _segmentTableHeader = new Regex( @"^Pos\s{1,}Tag\s{1,}Name\s{1,}S\s{1,}R$" );

        return _segmentTableHeader;
    }
}

// reads: 00190       ---- Segment group 5  ------------------ C   200--------------+
private Regex _segmentGroupHeader;
Regex SegmentGroupHeader
{
    get
    {
        if (_segmentGroupHeader == null)
            _segmentGroupHeader = new Regex( @"^(\d{1,5})\s{1,8}-{1,4}\sSegment group\s(\d{1,2})\s{1,}-{1,}\s(C|M)\s{1,3}(\d{1,})-{1,}(\++)\|*$" );

        return _segmentGroupHeader;
    }
}

// reads: 01420   DTM Date/time/period                         C   2------------+++++
private Regex _segmentLineFull;
Regex SegmentLineFull
{
    get
    {
        if (_segmentLineFull == null)
            _segmentLineFull = new Regex( @"^(\d{1,5})\s{1,4}(\w+)\s(.{1,38})\s{1,3}(C|M)\s{1,3}(\d+)(?:-{0,}|\s*)(\+{0,})\|*$" );

        return _segmentLineFull;
    }
}

// reads: 00250   SPS Sampling parameters for summary                               |
private Regex _segmentLineFragmentFirst;
Regex SegmentLineFragmentFirst
{
    get
    {
        if (_segmentLineFragmentFirst == null)
            _segmentLineFragmentFirst = new Regex( @"^(\d{1,5})\s{1,4}(\w+)\s(.{1,38})\s{1,24}\|$" );

        return _segmentLineFragmentFirst;
    }
}

// reads:                statistics                            C   1                |
private Regex _segmentLineFragmentLast;
Regex SegmentLineFragmentLast
{
    get
    {
        if (_segmentLineFragmentLast == null)
            _segmentLineFragmentLast = new Regex( @"^\s{1,15}(.{1,38})(C|M)\s{1,4}(\d{1,})\s{1,}\|+$" );

        return _segmentLineFragmentLast;
    }
}

...
...

switch (getLineType( line ))
{
    case Common.LineType.SegmentGroupHeader:
        // process segment group header
        break;

    case Common.LineType.SegmentLine:
        // process segment table line...
        break;

    case Common.LineType.SegmentLineBrokenFirst:
        // process a fragmented segment line...
        readNextLine = true;
        break;

    case Common.LineType.Ignore:
        // ignore line...
        break;

    default:
        if (!string.IsNullOrEmpty( line ))
            errors.Add( string.Format( "Unknown line type {0}", line ) );

        break;
}

...
...

Common.LineType getLineType( string line )
{
    Common.LineType lineType = Common.LineType.Unknown;

    if (SegmentGroupHeader.IsMatch( line ))
        lineType = Common.LineType.SegmentGroupHeader;

    else if (SegmentLineFull.IsMatch( line ))
        lineType = Common.LineType.SegmentLine;

    else if (SegmentLineFragmentFirst.IsMatch( line ))
        lineType = Common.LineType.SegmentLineBrokenFirst;

    else if (SegmentLineFragmentLast.IsMatch( line ))
        lineType = Common.LineType.SegmentLineBrokenLast;

    else if (MessageStructureLabel.IsMatch( line ))
        lineType = Common.LineType.Ignore;

    else if (SegmentTableLabel.IsMatch( line ))
        lineType = Common.LineType.Ignore;

    else if (SegmentTableHeader.IsMatch( line ))
        lineType = Common.LineType.Ignore;

    else if (HeaderOrDetailOrSummarySection.IsMatch( line ))
        lineType = Common.LineType.Ignore;

    else if (BlankLines.IsMatch( line ))
        lineType = Common.LineType.Ignore;

    return lineType;
}
相关问题