使用正则表达式解析结构化文本
本文关键字:结构化 文本 正则表达式 | 更新日期: 2023-09-27 18:12:01
我正在寻找2个正则表达式,将正确解析以下行块(每行是76个字符的固定长度):
给你一个结构的概念:
- 前5个字符是位置(例如00250)
- 后3个字符是标签(例如SPS)
- 下面的文本是描述标签功能的自由文本(可以拆分为一行或多行,例如:功能统计汇总抽样参数
- 下一个1字符可以是C(必选)或M(必选)
- 下一个或更多的数字是基数(例如9999)。
- 加号和管道字符表示分组。
我要求将所有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;
}