【问题标题】:Perl multiline regex in windowsWindows中的Perl多行正则表达式
【发布时间】:2022-01-27 16:49:11
【问题描述】:

我被这种情况困住了,我有这个regex

*为清楚起见,此处添加了输入:

181221533;MG;3;1476729;<vars>  <vint>    <name>mtest</name> <storedPrecedure>f_sc_mtest</SP>    <base>M_data</base>    <dataType>I</dataType>    <timeMS>17</timeMS>    <ttidr>abc</ttidr>  <base>S</base>    <valor>0</valor>  </vint>  </vars>;889;6;85;112;01/01/2019;29/05/2019 17:17:48
182652972;MG;6314429;740484;<vars>  <vint>    <name>mtest</name>    <sP>f_sc_mtest</sP> <base>sscy</base>    <dataType>I</dataType>    <timeMS>16</timeMS>    <ttidr>abc</Idtype>    <base>S</base>    <valor>4</valor>  </vint></vars>;-1;8;57217;57228;01/01/2019;06/06/2019 22:20:48
182652984;ModeloSP;6314429;740484;<vars>  <vint>     <name>tc_p_act</name>    <sP>rndom_name</sP>    <base>sscyo</base>    <dataType>I</dataType>    <timeMS>0</timeMS>    <Idtype>XYZ</Idtype>    <base>O</base>  </vint>
</vars>;0;;0;41;01/01/2019;06/06/2019 22:31:22

182652988;ModeloSP;6314429;740484;<vars>  <vint>     <name>tc_p_act</name>    <sP>rndom_name</sP>    <base>sscyo</base>    <dataType>I</dataType>    <timeProcess>1</timeProcess>    <Idtype>XYZ</Idtype>    <base>O</base>  </vint>
</vars>;0;;0;85;01/01/2019;06/06/2019 22:37:36

我想在 perl 中实现这个正则表达式并支持多行,因为正如您在示例中看到的那样,记录中有换行符,这个正则表达式搜索“不完整”行(以及额外的行)并修复它们(一条记录/line 应该以日期时间结束)

这就是我正在尝试使用 perl:

perl.exe -0777 -i -pe "s/(?m)^(.*)(>)([\n]+)(<)(.*)([\n]+)(\s*)$/$1$2    $4$5/igs" "sample.txt"

而且似乎不起作用,我不断得到相同的文本文件。我在便携式 GIT 安装 (v5.34.0) 中使用 perl

我有什么遗漏吗?

编辑:输出应该是这样的:

181221533;MG;3;1476729;<vars>  <vint>    <name>mtest</name> <storedPrecedure>f_sc_mtest</SP>    <base>M_data</base>    <dataType>I</dataType>    <timeMS>17</timeMS>    <ttidr>abc</ttidr>  <base>S</base>    <valor>0</valor>  </vint>  </vars>;889;6;85;112;01/01/2019;29/05/2019 17:17:48
182652972;MG;6314429;740484;<vars>  <vint>    <name>mtest</name>    <sP>f_sc_mtest</sP> <base>sscy</base>    <dataType>I</dataType>    <timeMS>16</timeMS>    <ttidr>abc</Idtype>    <base>S</base>    <valor>4</valor>  </vint></vars>;-1;8;57217;57228;01/01/2019;06/06/2019 22:20:48
182652984;ModeloSP;6314429;740484;<vars>  <vint>     <name>tc_p_act</name>    <sP>rndom_name</sP>    <base>sscyo</base>    <dataType>I</dataType>    <timeMS>0</timeMS>    <Idtype>XYZ</Idtype>    <base>O</base>  </vint>    </vars>;0;;0;41;01/01/2019;06/06/2019 22:31:22
182652988;ModeloSP;6314429;740484;<vars>  <vint>     <name>tc_p_act</name>    <sP>rndom_name</sP>    <base>sscyo</base>    <dataType>I</dataType>    <timeProcess>1</timeProcess>    <Idtype>XYZ</Idtype>    <base>O</base>  </vint>    </vars>;0;;0;85;01/01/2019;06/06/2019 22:37:36

【问题讨论】:

  • 请包括问题的输入,格式为代码。
  • @choroba 完成,添加输入
  • 您使用了错误的标志。在 regex101 链接中您使用的是/gm,但在 Perl 命令中您使用的是/igs/i 不需要,因为您的表达式不区分大小写。考虑到&lt;/vars&gt; 之前有换行符,/s 可能不是您需要的。 /gm 在我看来是正确的标志。
  • 帮我解析输入与期望的输出...你只是想删除多余的空行?
  • @Jesse 尝试了这个,仍然没有,我的意思是我一直得到与输入相同的内容: perl.exe -0777 -i -pe "s/(?m)^(.*)(> )([\n]+)(

标签: regex perl


【解决方案1】:

捕获整个记录并用空格替换其中的所有换行符,在替换部分使用另一个正则表达式(/e 修饰符提供)。然后用一个替换所有多个换行符

perl.exe -0777 -wpe'
    s{ (?:^|\R)\K (\d{9}; .*? \s+\d\d:\d\d:\d\d) }{$1 =~ s/\n+/ /r}segx; s{\n+}{\n}g
' file.txt

我认为“记录”是:[0-9]{9}; 位于行/文件开头,然后全部包含空格后的时间戳。记录开头和结尾的详细信息应防止意外匹配这些标签内可能出现的意外模式。

这很麻烦,但我希望它能够正确捕获记录,即使某些细节发生了变化。


显然,上述方法在 Windows 上失败了,而它被证实可以在 Linux 上工作(我现在唯一可以尝试的系统)。

问题必须出现在换行符中——因此请尝试将匹配项中的\n 替换为\R\r\n。特别是在替换部分中嵌入的正则表达式中。或者,为了安全和便携,将\n 替换为(\r?\n)(所以回车符是可选的,不需要为了匹配而存在)。

要么

s{ (?:^|\R)\K (\d{9}; .*? \s+\d\d:\d\d:\d\d) }{$1 =~ s/\R+/ /r}segx; s{\R+}{\r\n}g

s{ (?:^|\R)\K(\d{9};.*?\s+\d\d:\d\d:\d\d) }{$1 =~ s/(\r\n)+/ /r}segx; s{(\r\n)+}{\r\n}g

但是\R 应该在 Windows 上匹配它,因此您应该能够使用 \R 进行匹配,并在需要替换时使用 \r\n。在Misc in perlbackslash下查看


如果可行,更好的是使用PerlO layers。通常,Windows 构建的 Perl 默认会添加 :crlf 层,但这里似乎并非如此。

单线尝试:

perl.exe -0777 -Mopen=:std,IO,:crlf -wpe'...'

或者,使用“one-liner”作为普通程序,没有文件处理开关,并通过open pragma 设置并手动打开文件

perl -wE'use open IO => ":crlf"; $_ = do { local $/; <> }; s{...}{...}; say' file

对于像这样设置的层(无论哪种方式),使用带有\n 的正则表达式。

【讨论】:

  • 也许我迷路了,但这个命令只在 标记之前添加了一个空格,在输入的第 4 行和第 7 行,我做错了什么吗? perl.exe -0777 -wpe "s{ (?:^|\R)\K (\d{9}; .*? \s+\d\d:\d\d:\d\d) }{$1 =~ s/\n+/ /r}segx; s{\n+}{\n}g" "test.txt" > salso1.txt
  • 嗯,我只是将它复制粘贴到这里,它按预期进行,删除记录中所有额外的换行符,并折叠记录之间的多个换行符。所以它会在每一行打印:181221533;(或类似),然后是直到 `22:31:22`(或类似)的内容。我能想到的唯一问题是,这是在 linux 终端上。也许尝试将匹配中的所有\n 替换为\R 或类似的(对于Windows)?或添加\r,如\n\(r?)+
  • 是的,这确实是一个 Windows 问题,我在 linux 中尝试过,但不幸的是我需要在 Windows 中进行。您能否突出显示我应该尝试为 windows 上下文替换模式的哪些字符?
  • 我希望那是关于换行符。我在答案的末尾添加了评论,希望清楚吗?抱歉,我现在无法自己解决问题,因为我无法在此处使用 Windows 框
  • @NatyBizz 在答案中添加了显式代码
【解决方案2】:

如果问题是换行符出现在错误的位置,或者连续多个换行符,或者在 &lt; 之前,您可能会遇到这样的简单问题:

use strict;
use warnings;

my $str = do { local $/; <DATA> };

$str =~ s/\n(?=[<\n])//g;
print $str;

__DATA__
181221533;<valor>0</valor></vars>;889;6;85;112;01/01/2019;29/05/2019 17:17:48
182652972;</vars>;-1;8;57217;57228;01/01/2019;06/06/2019 22:20:48
182652984;</vint>
</vars>;0;;0;41;01/01/2019;06/06/2019 22:31:22

182652988; </vint>
</vars>;0;;0;85;01/01/2019;06/06/2019 22:37:36

(我缩短了输入以使其可读)

输出:

181221533;<valor>0</valor></vars>;889;6;85;112;01/01/2019;29/05/2019 17:17:48
182652972;</vars>;-1;8;57217;57228;01/01/2019;06/06/2019 22:20:48
182652984;</vint></vars>;0;;0;41;01/01/2019;06/06/2019 22:31:22
182652988; </vint></vars>;0;;0;85;01/01/2019;06/06/2019 22:37:36

【讨论】:

  • 就是这样,为了兼容windows,这个工作:s/\r\n(?=[
  • 这是在 Windows 中制作的。我不确定细节,但是关于行尾有一些 Perl 魔法。在 Windows 中,我永远不必提供超过 \n 的内容。
  • 您始终可以使用\R 来表示universal 的行尾概念。
【解决方案3】:

这似乎产生了想要的输出:

perl.exe -0777 -pe "s: *\n(?=</):    :g;s/\n+/\n/g"
  • 第一次替换用四个空格替换 &lt;/ 之前的空格和换行符。
  • 第二个替换用一个换行符替换多个换行符。您也可以将其替换为音译:tr/\n//s/s“挤压”换行符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多