我想在文本文件中找到双引号或单引号内的字符串(文本文件是多行),
例如:
I have a
test "foo bar1" test2 "foo\"bar2",
"foo 'bar3",
'foo bar4', 'foo \'bar5', 'foo "bar6',
它将输出
foo bar1
foo\"bar2
foo 'bar3
foo bar4
foo \'bar5
foo "bar6
难点是:
- 文本文件是多行的。
- 它可能在引号内转义了双引号或单引号。
- 双引号内可以有单引号。
- 单引号里面可以有双引号。
- 引号必须成对匹配。
答案1
我们可以使用 Perl 的匹配时间代码插值功能(??{ match time regex })
来解决这个问题。本质上,它的作用是,根据匹配的引用,将相应的有效正则表达式放置在该引用中,以便正则表达式引擎将捕获该引用对。
$ perl -lne '
print substr($&, 1, -2+length($&))
while
/(?:(["'\''])(??{q<(?:[^\\\\>.$1.q<]|\\\\.)*>.$1}))/gx;
' file
结果:
foo bar1
foo\"bar2
foo 'bar3
foo bar4
foo \'bar5
foo "bar6
上面的更平滑的重写如下:
$ perl -lne '
BEGIN {
$genRE = sub {
my $openingQ = shift;
# look in the Notes below for why
qq<(?:[^\\\\${openingQ}]|\\\\.)*>
};
}
print $2
while
/
(["'\'']) (?#: opening quote)
((??{ $genRE->($1) })) (?#: run of in between quote pair stuff)
\1 (?#: corresponding closing quote)
/gx;
' file
笔记::
"........"
匹配的是/"[^"]*"/
"...... \"......"
匹配的是/"(?:[^\\"]|\\.)*"/
- 单引号也类似。
答案2
另一种perl
方法:
perl -lne 'print $2 while m{(["'\''])((?:\\.|(?!\1).)*+)\1}g'
这里使用负向前看运算符来(?!\1).
匹配除第一个捕获组所匹配的字符之外的字符。您还可以简单地分别覆盖'...'
和"..."
案例:
perl -lne 'print $1 while m{(?|"((?:\\.|[^"])*+)"|'"'((?:\\\.|[^'])*+)')}g"
答案3
这是困难的。我没有解决办法。我什至不确定完成这项任务的最佳工具是什么。
我已经接近了:
$ grep -oP '((?<!\\)"\K.*?(?=(?<!\\)"))|'"((?<!\\\\)'\K.*?(?=(?<!\\\\)'))" input
foo bar1
foo\"bar2
foo 'bar3
foo bar4
,
foo \'bar5
,
foo "bar6
每行多个匹配的问题在于,较早字符串的结束引号与中间文本的起始引号相匹配。我不能用偶数个引号的正后视来阻止它,因为后视必须具有固定的长度。至少对于grep
.
'
此外,至少可以说,内部的几个匹配"
(或相反)很有趣。
也许awk
是更好的工具。有了它,您可以检查哪种引用类型最先出现,跳到下一个并检查它前面是否有反斜杠。