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

本文关键字:结构化 文本 正则表达式 | 更新日期: 2023-09-27 18:12:01

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

给你一个结构的概念:

  1. 前5个字符是位置(例如00250)
  2. 后3个字符是标签(例如SPS)
  3. 下面的文本是描述标签功能的自由文本(可以拆分为一行或多行,例如:功能统计汇总抽样参数
  4. 下一个1字符可以是C(必选)或M(必选)
  5. 下一个或更多的数字是基数(例如9999)。
  6. 加号和管道字符表示分组。

我要求将所有6个元素捕获到组中。

<>之前00250 SPS采样参数汇总|功能 |统计学C 99 |00260 SPS采样参数汇总|功能 |statistics c9999 -------------+00270 SPS采样参数汇总|功能 |统计c9 ---------------+|00280 SPS采样参数汇总|功能 |统计C 1--------------+||之前如上面的示例所示,

可以有多于1个加号,因此正则表达式需要考虑到这一点。此外,管道字符(|)可以在行尾出现多次。

上面给出的示例都显示了一个跨越三行的描述"抽样参数用于摘要函数统计",每个"拆分"描述需要追加以形成单个字符串。

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

第2行是这样的:

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

和第三行:

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

问题是Regex (A)匹配第2行&3、当只匹配第2行时。有人能告诉我正确的正则表达式,请让我能正确解析这些行吗?

如果它有帮助,这里是全文的一个例子,我正在解析(节4.3.1段表)。

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

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

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

您可以为这三行编写一个模式,并且使用命名捕获可以从块中提取您想要的所有信息。此模式使用详细模式以提高可读性:

(?<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}

我不会使用单一的正则表达式,但我更喜欢以每行为基础。Regex应该检查我们是否有前导数字,然后在状态机中换行。状态机继续吃行,直到满足新的前导数字模式或EOF。当状态机完成"吃"行时,您知道最后一行必须在列位置包含"C"或"M",但您基本上可以以固定列长度的方式解析该行。

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

下面的代码可供感兴趣的人使用:

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