在 perl 中使用 lookaheads 将空格替换为下划线
substituting spaces for underscores using lookaheads in perl
我有很多行如下形式的文件:
word -0.15636028 -0.2953045 0.29853472 ....
(数百个浮点数前面的一个单词,由空格分隔)
由于一些我无法控制的错误,这个词有时会有空格。
a bbb c -0.15636028 -0.2953045 0.29853472 .... (several hundreds floats)
我希望用下划线代替,以便得到:
a_bbb_c -0.15636028 -0.2953045 0.29853472 .... (several hundreds floats)
已经为每一行尝试了以下替换代码:
s/\s(?=(\s-?\d\.\d+)+)/_/g;
因此环顾四周显然不是解决方案。
如果能提供任何线索,我将不胜感激。
这样的东西对你有用吗:
s/\s+/_/g;
s/_(-?\d+\.)/ /g;
根据您的评论假设您的文件如下所示:
name float1 float2 float3
a bbb c -0.15636028 -0.2953045 0.29853472
abbb c -0.15636028 -0.2953045 0.29853472
a bbbc -0.15636028 -0.2953045 0.29853472
ab bbc -0.15636028 -0.2953045 0.29853472
abbbc -0.15636028 -0.2953045 0.29853472
既然你在评论中说第一个字段可能包含数字,那么你不能使用搜索第一个浮点数的前瞻来解决问题。 (尽管如此,您仍然可以使用前瞻来计算行尾之前的浮点数,但它不是很方便)。
我建议的是基于 header 第一行定义的字段编号的解决方案。
您可以使用header行知道字段数并替换其他行开头的空格,直到字段数相同。
你可以像这样使用 perl 命令行作为 awk:
perl -MEnglish -pae'$c=scalar @F if ($NR==1);for($i=0;$i<scalar(@F)-$c;$i++){s/\s+/_/}' file
for 循环计算第一行(存储在 $c
中)和当前行(由 scalar(@F)
给出,其中 @F
是字段数组),并重复替换。
a
将 perl 命令行切换为自动拆分模式,-MEnglish 使 number row 变量可用作 $NR
(类似于 NR 变量在 awk 中)。
可以这样缩短:
perl -pae'$c=@F if $.<2;$i=@F-$c;s/\s+/_/ while $i--' file
你的前瞻想法很好,但问题是如何只替换前瞻之前匹配的部分中的 space ,当它们与其他东西(即单词)混合时。
一种方法是捕获第一个浮点数之前的内容(由 lookahead 给出),并在替换部分 运行 捕获内容的另一个正则表达式,以替换 spaces
s{ (.*?) (?=\s+-?[0-9]+\.[0-9]) }{ =~ s/\s+/_/gr }ex
备注
修饰符/e
使替换部分被评估为代码;任何有效的 Perl 代码都可以
使用 s{}{}
个分隔符,我们可以在替换部分的正则表达式中使用 s///
个分隔符
替换部分中的正则表达式,将捕获的文本中的 spaces 更改为 _
,具有 /r
修饰符,因此 return修改后的字符串并保持原始不变。因此,我们不会尝试更改 </code>(它是只读的),并且修改后的字符串(被 returned)可用作替换 </p></li>
<li><p>修饰符 <code>/x
允许在模式中使用 space,以提高可读性
这里必须做一些假设。最关键的一点是要处理的文本后跟给定格式的数字 -?[0-9]+\.[0-9]+
,而文本本身并没有这样的数字。这遵循了 OP 的样本,更明确地说,是尝试的解决方案
几个带有假设的细节。 (1) [0-9]+\.
需要前导数字——如果您可以使用 .123
之类的数字,则使用 [0-9]*\.
(2) 内部正则表达式中的 \s+
折叠多个连续 spaces 变成一个 _
,所以 a b c
变成 a_b_c
(而不是 a__b_c
)
在前瞻中,我用 \s+
收集了第一个浮点数之前的所有 space——因此它们将留在第一个浮点数的前面。这是一个 space 想要的,但多个可能会很尴尬
如果它们包含在 .*?
捕获中(如果前瞻只有一个 space、\s
),那么我们会得到一个 _
尾随单词(s).我认为那会 more 尴尬。理想的解决方案是 运行 另一个正则表达式并清理它,如果这种情况是可能的并且它很麻烦
一个例子
echo "a bbb c -0.15636028 -0.2953045" |
perl -wpe's{(.*?)(?=\s+-?[0-9]+\.[0-9])}{ =~ s/\s+/_/gr }e'
打印
a_bbb_c -0.15636028 -0.2953045
然后要处理文件中的所有行,您可以执行以下任一操作
perl -wpe'...' file > new_file
并得到一个 new_file
的变化,或者
perl -i.bak -wpe'...' file
更改file
就地(即-i
),其中.bak
使其保存备份。
使用否定先行替换任何未跟随浮点数的空格:
echo "a bbb cc -0.123232 -0.3232" | perl -wpe 's/ +(?! *-?\d+\.)/_/g'
我有很多行如下形式的文件:
word -0.15636028 -0.2953045 0.29853472 ....
(数百个浮点数前面的一个单词,由空格分隔)
由于一些我无法控制的错误,这个词有时会有空格。
a bbb c -0.15636028 -0.2953045 0.29853472 .... (several hundreds floats)
我希望用下划线代替,以便得到:
a_bbb_c -0.15636028 -0.2953045 0.29853472 .... (several hundreds floats)
已经为每一行尝试了以下替换代码:
s/\s(?=(\s-?\d\.\d+)+)/_/g;
因此环顾四周显然不是解决方案。 如果能提供任何线索,我将不胜感激。
这样的东西对你有用吗:
s/\s+/_/g;
s/_(-?\d+\.)/ /g;
根据您的评论假设您的文件如下所示:
name float1 float2 float3
a bbb c -0.15636028 -0.2953045 0.29853472
abbb c -0.15636028 -0.2953045 0.29853472
a bbbc -0.15636028 -0.2953045 0.29853472
ab bbc -0.15636028 -0.2953045 0.29853472
abbbc -0.15636028 -0.2953045 0.29853472
既然你在评论中说第一个字段可能包含数字,那么你不能使用搜索第一个浮点数的前瞻来解决问题。 (尽管如此,您仍然可以使用前瞻来计算行尾之前的浮点数,但它不是很方便)。
我建议的是基于 header 第一行定义的字段编号的解决方案。
您可以使用header行知道字段数并替换其他行开头的空格,直到字段数相同。
你可以像这样使用 perl 命令行作为 awk:
perl -MEnglish -pae'$c=scalar @F if ($NR==1);for($i=0;$i<scalar(@F)-$c;$i++){s/\s+/_/}' file
for 循环计算第一行(存储在 $c
中)和当前行(由 scalar(@F)
给出,其中 @F
是字段数组),并重复替换。
a
将 perl 命令行切换为自动拆分模式,-MEnglish 使 number row 变量可用作 $NR
(类似于 NR 变量在 awk 中)。
可以这样缩短:
perl -pae'$c=@F if $.<2;$i=@F-$c;s/\s+/_/ while $i--' file
你的前瞻想法很好,但问题是如何只替换前瞻之前匹配的部分中的 space ,当它们与其他东西(即单词)混合时。
一种方法是捕获第一个浮点数之前的内容(由 lookahead 给出),并在替换部分 运行 捕获内容的另一个正则表达式,以替换 spaces
s{ (.*?) (?=\s+-?[0-9]+\.[0-9]) }{ =~ s/\s+/_/gr }ex
备注
修饰符
/e
使替换部分被评估为代码;任何有效的 Perl 代码都可以使用
s{}{}
个分隔符,我们可以在替换部分的正则表达式中使用s///
个分隔符替换部分中的正则表达式,将捕获的文本中的 spaces 更改为
_
,具有/r
修饰符,因此 return修改后的字符串并保持原始不变。因此,我们不会尝试更改</code>(它是只读的),并且修改后的字符串(被 returned)可用作替换 </p></li> <li><p>修饰符 <code>/x
允许在模式中使用 space,以提高可读性这里必须做一些假设。最关键的一点是要处理的文本后跟给定格式的数字
-?[0-9]+\.[0-9]+
,而文本本身并没有这样的数字。这遵循了 OP 的样本,更明确地说,是尝试的解决方案几个带有假设的细节。 (1)
[0-9]+\.
需要前导数字——如果您可以使用.123
之类的数字,则使用[0-9]*\.
(2) 内部正则表达式中的\s+
折叠多个连续 spaces 变成一个_
,所以a b c
变成a_b_c
(而不是a__b_c
)在前瞻中,我用
\s+
收集了第一个浮点数之前的所有 space——因此它们将留在第一个浮点数的前面。这是一个 space 想要的,但多个可能会很尴尬如果它们包含在
.*?
捕获中(如果前瞻只有一个 space、\s
),那么我们会得到一个_
尾随单词(s).我认为那会 more 尴尬。理想的解决方案是 运行 另一个正则表达式并清理它,如果这种情况是可能的并且它很麻烦
一个例子
echo "a bbb c -0.15636028 -0.2953045" |
perl -wpe's{(.*?)(?=\s+-?[0-9]+\.[0-9])}{ =~ s/\s+/_/gr }e'
打印
a_bbb_c -0.15636028 -0.2953045
然后要处理文件中的所有行,您可以执行以下任一操作
perl -wpe'...' file > new_file
并得到一个 new_file
的变化,或者
perl -i.bak -wpe'...' file
更改file
就地(即-i
),其中.bak
使其保存备份。
使用否定先行替换任何未跟随浮点数的空格:
echo "a bbb cc -0.123232 -0.3232" | perl -wpe 's/ +(?! *-?\d+\.)/_/g'