正则表达式替换文本提要

时间:2011-08-24 18:36:23

标签: .net regex

我有一个非常类似的文字Feed文件:

F00020000100008252011
H123400010000050008201975
D23451234567891234567800000000000000000000000000000500
D23461234567891234567800000000000000000000000000000500
H789100010000050008201975
D54321234567891234567800000000000000000000000000000500
D54331234567891234567800000000000000000000000000000500

...以F开头的行是文件头,以H开头的行是批头,以D开头的行是详细记录。我想运行一个正则表达式替换表达式(.NET),其中我的结果将是文件头,特定批处理标题,以及该特定批处理标题下的所有详细记录(没有其他标题)。这看起来相当简单,但事实证明它比我预期的要困难,因为当我开始搞乱正则表达式时总是如此。我在C#中使用MultiLine Regex对象。我可以为正则表达式字符串和替换字符串提供什么以产生下面的结果?

F00020000100008252011
H123400010000050008201975
D23451234567891234567800000000000000000000000000000500
D23461234567891234567800000000000000000000000000000500

F00020000100008252011
H789100010000050008201975
D54321234567891234567800000000000000000000000000000500
D54331234567891234567800000000000000000000000000000500

注意:

虽然此应用程序是内部开发的,并且可以更改为以更优雅的方式支持此应用程序,但这需要更改代码,单元测试,QA和更改控制流程,这将严重延迟此应用程序的可用性饲料。如果可能的话,我想使用内置的Regex替换机制,因此不需要将额外的资源用于此任务。

我尝试了以下内容:

正则表达式:

(?<fileheader>^F.*$)|(?<batchheader>^H1234.*$)|(^H1234.*$(?<detail>^D\d*$))

替换:

${fileheader}${batchheader}${detail}

并且无法找到所有细节

正则表达式:

(?<fileheader>^F.*$)|(?<batchheader>^H1234*.$)|(?<detail>^D.*$)

替换:

${fileheader}${batchheader}${detail}

并抓住所有细节,甚至是那些不在批次中的细节。

正则表达式:

(?<fileheader>^F.*$)|(?<batchheader>^H1234*.$)|^H1234*.$^[D0-9]*$(?<detail>^D.*$)

替换:

${fileheader}${batchheader}${detail}

并且只找到了文件头。

正则表达式:

(?<FileHeader>F\d+\r\n)(?<UnWanted>(?!H1234)[HD]\d*[\r\n]*)*(?<BatchHeader>H1234\d*\r\n)(?<Detail>D\d*[\r\n]*)*(?<UnWanted2>(?!H1234)[HD]\d*[\r\n]*)*

替换:

${FileHeader}${BatchHeader}${Detail}

这让我几乎我需要的东西,但只有一条细节记录。

......以及这些方面的许多变化。

5 个答案:

答案 0 :(得分:3)

使用这样的方法可以更好地解决您的问题。

尝试阅读文件的每一行而不是使用MultiLineRegex。这很简单 足够不要求这样的措施,因为每一行应该只以F,H或D开头。

读取以F开头的行后,下一行应以H或F开头(在System.String中使用StartsWith)。

  • 如果下一行以H开头,我们有第一批标题。
  • 如果下一行以F开头,我们有下一个文件头(没有批头标题)。

读取以H开头的行后,下一行应以D,H或F开头。

  • 如果下一行以D开头,我们会有第一个详细记录。
  • 如果下一行以H开头,我们有下一个批头。
  • 如果下一行以F开头,我们有下一个文件头(不再有批头)。

读取以D开头的行后,下一行应以D,H或F开头。

  • 如果下一行以D开头,我们会有下一个详细记录。
  • 如果下一行以H开头,我们有下一个批头标题(没有更多详细记录)。
  • 如果下一行以F开头,我们有下一个文件头(没有更多详细记录)。

此算法与状态机的工作方式类似。

答案 1 :(得分:1)

正则表达。 编辑测试的正则表达式

data = Regex.Replace(data, @"(?<F>^F\d+\s*)(?<FirstHD>^H\d+\s*(^D\d+\s*)*)(?<MoreHD>(^H\d+\s*(^D\d+\s*)*)*)", m => m.Groups["F"].Value + m.Groups["FirstHD"].Value + m.Groups["MoreHD"].Value.Replace("H", m.Groups["F"].Value + "H"), RegexOptions.Multiline);

不需要使用Regex,这样的事情就可以了。

public String parse(String data) 
{
    StringBuilder sb = new StringBuilder();
    String lastF = String.Empty;
    foreach(String line in data.Split('\r')) 
    {
        if(String.IsNullOrEmpty(line)) continue;
        line = line.Trim();
        if(line.StartsWith("F")) 
        {
            lastF = line;
        }
        else if(line.StartsWith("H"))
        {
            sb.Append(lastF + Environment.NewLine + line + Environment.NewLine); 
        }
        else //implied if(line.StartsWith("D"))
        {
            sb.Append(line + Environment.NewLine);
        }
    }
    return sb.ToString();
}

答案 2 :(得分:1)

以下是我在perl中所做的大致概述

^(F[^\r\n]+)
.*
(\r\nH1234[^\r\n]+)
((?:\r\nD[^\r\n]+)*)
.*
$

换句话说:F ...到行尾,后跟任意内容,然后在行的开头跟随H123,接着是任意数量的D ...记录,然后是更随意的东西。

第三项中的

(?: ...)是非绑定分组,允许您对RE进行分组,而不将它们绑定到\ N标记。它用于为D ...记录定义RE,然后用*。

重复

这可以在替换字符串中与\1\2\3连接。

(我没有测试RE,但方法应该有效。)

答案 3 :(得分:1)

你可以使用这样的表达式:

(?<=(?<FileHeader>^F.+\n)((^H.+)\n((^D.+)\n)+)+)(?<BatchHeader>(^H.+)\n)

和这样的替代:

${FileHeader}${BatchHeader}

当我使用您的示例测试时(我添加了另一批详细信息作为测试,以确保它为每个文件头处理超过2个批处理标头)

F00020000100008252011
H123400010000050008201975 
D23451234567891234567800000000000000000000000000000500 
D23461234567891234567800000000000000000000000000000500 
H789100010000050008201975
D54321234567891234567800000000000000000000000000000500
D54331234567891234567800000000000000000000000000000500
H789100010000050008201975
D54321234567891234567800000000000000000000000000000500
D54331234567891234567800000000000000000000000000000500

我得到了这些结果:

F00020000100008252011
H123400010000050008201975 
D23451234567891234567800000000000000000000000000000500 
D23461234567891234567800000000000000000000000000000500 
F00020000100008252011
H789100010000050008201975
D54321234567891234567800000000000000000000000000000500
D54331234567891234567800000000000000000000000000000500
F00020000100008252011
H789100010000050008201975
D54321234567891234567800000000000000000000000000000500
D54331234567891234567800000000000000000000000000000500

ETA:我误解了你想要的东西:

我认为这会更好:

这样的替代:

${FileHeader}${batch}

和本RegEx:

((?<FileHeader>^F.+\n))(H.+\n(^D.+\n)+)*(?<batch>H789.+\n(^D.+\n)+)(H.+\n(^D.+\n)+)*

给出这个:

F00020000100008252011 H789100010000050008201975 D54321234567891234567800000000000000000000000000000500 D54331234567891234567800000000000000000000000000000500

这个RegEx:

((?<FileHeader>^F.+\n))(H.+\n(^D.+\n)+)*(?<batch>H1234.+\n(^D.+\n)+)(H.+\n(^D.+\n)+)*

给出这个:

F00020000100008252011 H123400010000050008201975 D23451234567891234567800000000000000000000000000000500 D23461234567891234567800000000000000000000000000000500

我只是将后缀更改为中间的H以匹配特定批次。

答案 4 :(得分:0)

好的,我找到了答案。

正则表达式字符串

(?<FileHeader>F\d+\r\n)(?:(?!H1234)[HD]\d*[\r\n]*)*(?<BatchHeader>H1234\d*[\r\n]*)(?<Detail>(D\d*[\r\n]*)*)(?:(?!H1234)[HD]\d*[\r\n]*)*

替换字符串

${FileHeader}${BatchHeader}${Detail}

这将产生以下结果集:

F00020000100008252011
H123400010000050008201975
D23451234567891234567800000000000000000000000000000500
D23461234567891234567800000000000000000000000000000500

同样,将“1234”的实例替换为“7891”将为我提供我需要的另一组。感谢所有提供答案的人,它让我走上了正确的道路,获得了我想要的结果。