正则表达式(或bash),获取引号之间的管道(perl)

xiozqbni  于 2022-11-15  发布在  Perl
关注(0)|答案(6)|浏览(123)

更新:请记住,正则表达式是我唯一的选择。
更新2:实际上,我也可以使用基于bash的解决方案。
尝试在perl regex中用逗号替换双引号之间的管道(可以不止一个)
范例

continuer|"First, Name"|123|12412|10/21/2020|"3|7"||Yes|No|No|

预期输出(3和7由逗号分隔)

continuer|"First, Name"|123|12412|10/21/2020|"3,7"||Yes|No|No|

可能有更多的数字,可能不只是两个d\|d。它可能是"3|7|2",正确的输出必须是"3,7,2"

cat <filename> | perl -pi -e 's/"\d+\|[\|\d]+/\d+,[\|\d]+/g'

但它只是把实际的字符串d+等等...
我真的很感激你的帮助

cpjpxq1n

cpjpxq1n1#

如果它必须是一个正则表达式,这里有一个更简单的正则表达式

perl -wpe's/("[^"]+")/ $1 =~ s{\|}{,}gr /eg' file

不是防弹的,但它应该适用于所示的用例。†
解释。使用/e修饰符,替换端被计算为代码。在这里,一个正则表达式在/r下的$1上运行,所以原始的($1)不变; $N是只读的,所以我们不能改变$1,也就不能在它上面运行一个“普通的”s///。用这个修饰符返回被改变的字符串,或者如果没有改变的话返回原始的字符串。
一旦测试足够好,如果需要,添加-i以“就地”更改输入文件。
我必须补充,我看不出有任何理由,至少这部分工作不能使用CSV解析器完成...
感谢池上提供的改进版本

perl -wpe's/"[^"]+"/ $& =~ tr{|}{,}r /eg' file

更简单,无需捕获,而且tr速度更快
†使用问题中的字符串进行测试,仅扩展到以下范围

con|"F, N"|12|10/21|"3|7"||Yes|"2||4|12"|"a|b"|No|""|end|
5lhxktic

5lhxktic2#

我会使用a CSV parser,而不是正则表达式:

#!/usr/bin/env perl
use warnings;
use strict;
use Text::CSV_XS;

my $csv = Text::CSV_XS->new({ binary => 1, sep_char => "|"});

while (my $row = $csv->getline(*ARGV)) {
    @$row = map { tr/|/,/r } @$row;
    $csv->say(*STDOUT, $row);
}

例如:

$ perl demo.pl input.txt
continuer|"First, Name"|123|12412|10/21/2020|3,7||Yes|No|No|

更详细,但也更健壮,更容易理解。

pjngdqdw

pjngdqdw3#

如果你不能安装模块,Text::ParseWords是一个可以尝试的核心模块,它可以拆分字符串和处理带引号的分隔符。

use Text::ParseWords;

my $q = q(continuer|"First, Name"|123|12412|10/21/2020|"3|7"||Yes|No|No|);
print join "|", map { tr/|/,/; $_ } quotewords('\|', 1, $q);

作为一句俏皮话,它将是:

perl -MText::ParseWords -pe'$_ = join "|", map { tr/|/,/; $_ } quotewords('\|', 1, $_);' yourfile.txt
rkue9o1l

rkue9o1l4#

你说的是Update 2: Actually, I can use a bash based solution as well.,虽然这个脚本不是bash,但你可以从bash(或任何其他shell)调用它,我认为这就是你所说的“基于bash”,所以--在每个Unix系统的任何shell中使用任何awk都可以工作:

$ awk 'BEGIN{FS=OFS="\""} {for (i=2; i<=NF; i+=2) gsub(/\|/,",",$i)} 1' file
continuer|"First, Name"|123|12412|10/21/2020|"3,7"||Yes|No|No|

想象一下,您必须调试或增强上面清晰、简单的循环,而不是您在your answer中发布的regexp咒语:

's/(?:(?<=")|\G(?!^))(\s*[^"|\s]+(?:\s+[^"|\s]+)*)\s*\|\s*(?=[^"]*")/$1,/g'

记住-Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
我相信,如果你试图修改一个perl脚本来添加这个功能,你可以在perl中做我在上面用awk做的事情。

5tmbdcev

5tmbdcev5#

我会使用Text::CSV_XS。

perl -MText::CSV_XS=csv -e'
   csv
      in       => \*ARGV,
      sep_char => "|",
      on_in    => sub { tr/|/,/ for @{ $_[1] } };
'

您可以将文件名作为参数提供,也可以通过STDIN提供数据。

xam8gpfp

xam8gpfp6#

现在就可以了

's/(?:(?<=")|\G(?!^))(\s*[^"|\s]+(?:\s+[^"|\s]+)*)\s*\|\s*(?=[^"]*")/$1,/g'

这要归功于我的老板
感谢大家的关注。
我希望你们中的一些人意识到,一些项目需要某些方式和复杂化一个已经非常复杂的预先存在的结构并不总是一个选项在工作中。我知道会有一个班轮为这一点,不要恨,因为你不喜欢。

相关问题