Regex 匹配一行中图案的最后K个引用

Regex 匹配一行中图案的最后K个引用,regex,csv,awk,sed,Regex,Csv,Awk,Sed,是否可以使用sed/awk匹配一行中某个图案的最后k次出现 为简单起见,假设我只想匹配每行中的最后3个逗号,例如(请注意,这两行的总逗号数不同): 我只想匹配从299开始到两个碱基中行尾的逗号 动机:我正在尝试将一个在其中一个字段中有多余逗号的CSV文件转换为以制表符分隔的文件。由于正确列的数量是固定的,所以我的想法是用制表符替换前两个逗号,直到出现麻烦的字段(这很简单),然后从行尾向后再替换。这应该将所有正确的分隔符逗号转换为制表符,同时在有问题的字段中保留逗号不变 也许有一种更聪明的方法可以

是否可以使用sed/awk匹配一行中某个图案的最后k次出现

为简单起见,假设我只想匹配每行中的最后3个逗号,例如(请注意,这两行的总逗号数不同):

我只想匹配从
299
开始到两个碱基中行尾的逗号

动机:我正在尝试将一个在其中一个字段中有多余逗号的CSV文件转换为以制表符分隔的文件。由于正确列的数量是固定的,所以我的想法是用制表符替换前两个逗号,直到出现麻烦的字段(这很简单),然后从行尾向后再替换。这应该将所有正确的分隔符逗号转换为制表符,同时在有问题的字段中保留逗号不变


也许有一种更聪明的方法可以做到这一点,但我认为这无论如何都是一个很好的sed/awk教学点。

一个分别匹配最后三个逗号的正则表达式需要一个负前瞻,而sed不支持。 您可以使用以下sed regex一次匹配最后三个字段及其前面的逗号:

,[^,]*,[^,]*,[^,]*$
$
匹配行的末尾

[^,]
匹配除
以外的任何内容

组允许您重新使用sed中的字段值:

sed -r 's/,([^,]*),([^,]*),([^,]*)$/\t\1\t\2\t\3/'
对于awk,请看一看

也许有一种更聪明的方法可以做到这一点

如果所有需要的逗号后面都有空格,而不需要的逗号后面没有空格,那么

sed 's/,[^ ]/./g'

这将
a,b,12,3,c
转换为
a,b,12.3,c
嗨,我想这就是工作

echo 'a,b,c,d,e,f' | awk -F',' '{i=3; for (--i;i>=0;i--) {printf "%s\t", $(NF-i) } print ""}'
返回

d    e    f

但是您需要确保有3个以上的参数

您可以使用Perl将缺少的双引号添加到每一行中:

perl -aF, -ne '$F[-5] .= q("); print join ",", @F' < input > output
perl-aF,-ne'$F[-5].=q(“);print join”,“,@F'output
或者,将逗号转换为制表符:

 perl -aF'/,\s/' -ne 'splice @F, 2, -4, join ", ", @F[ 2 .. $#F - 4 ]; print join "\t", @F' < input > output
perl-aF'/,\s/'-ne'拼接@F,2,-4,连接“,”,@F[2..$#F-4];打印连接“\t”,@F'output
  • -n
    逐行读取输入
  • -a
    -F
    指定的模式将输入拆分到@F数组中
  • 第一种解决方案将缺少的引号添加到右侧的第五个字段中;第二种解决方案将第三个字段到第五个字段中的项目替换为由“,”连接的元素,并用制表符分隔生成的数组

另一个
sed
备选方案。将最后3个逗号替换为制表符

$ rev file | sed 's/,/\t/;s/,/\t/;s/,/\t/' | rev

10, 5, "Sally went to the store, and then , 299  ABD     F       10
使用GNU sed,您可以简单地编写

$ sed 's/,/\t/g5' file

10, 5, "Sally went to the store, and then , 299  ABD     F       10

从5号开始全部更换。

要修复CSV,我将执行以下操作:

echo '10, 5, "Sally went to the store, and then , 299, ABD, F, 10' |
  perl -lne '
    @F = split /, /;             # field separator is comma and space
    @start = splice @F, 0, 2;    # first 2 fields
    @end = splice @F, -4, 4;     # last 4 fields
    $string = join ", ", @F;     # the stuff in the middle
    $string =~ s/"/""/g;         # any double quotes get doubled
    print join(",", @start, "\"$string\"", @end);
  '
输出

10,5,"""Sally went to the store, and then ",299,ABD,F,10

这将满足您对GNU awk的要求,以匹配第三个参数()

但我不相信你所要求的是一个好方法


无论如何,请注意第一个gsub(),确保输入行上没有制表符-如果要将一些逗号转换为制表符,以将制表符用作输出字段分隔符,而不是放置制表符,这一点至关重要!

(这可能最终将问题转化为其他问题),您应该在需要时在正确的位置添加双引号。是的,这听起来像是一个典型的XY问题。修复生成无效CSV的代码。肯定是XY问题。向我们展示您正在尝试做什么,而不是如何尝试,否则您将被展示如何实现一个坏主意。@glennjackman:我无法控制如何实现CSV是生成的。数据集是公共的,它们只是与此问题一起出现。@gogurt:这取决于它们是否转义(使用其他双引号或反斜杠)。您应该添加几个有问题的行(真实示例)对于您的问题,否则无法给出有用的答案。如果您在第一个sed命令中未使用
-r
-E
标志,则应将括号转义,
([^,])
可能简化为
(.*)
在这种情况下。此外,三个逗号中的第一个被删除,而不是替换。我建议您编写
sed-r/,(.*),(.*),(.*),(.*)$/\t\1\t\2\t\3\t\4/'文件
@Kenavoz您对
()
的看法是正确的。我已经修复了它。但是
[^,]*在这种情况下,
不能替换为
*
。正则表达式的第一个逗号将始终与行的第一个逗号匹配。第一个
*
将尽可能匹配,包括除最后三个逗号以外的每个逗号。请使用
a、b、c、d、e、f、g、h、i
。你是对的。
sed-r的/(.*)、(.*)、(.*)、(.*)、(.*)、(.*))$/\1\t\2\t\3\t\4/'文件
应能更好地工作。请注意,三个逗号中的第一个仍然没有被替换,但已被sed删除。感谢第二次提醒。修复了此问题。新版本的命令看起来不错。
10,5,"""Sally went to the store, and then ",299,ABD,F,10
$ cat tst.awk
{
    gsub(/\t/," ")
    match($0,/^(([^,]+,){2})(.*)((,[^,]+){3})$/,a)
    gsub(/,/,"\t",a[1])
    gsub(/,/,"\t",a[4])
    print a[1] a[3] a[4]
}

$ awk -f tst.awk file
10       5       "Sally went to the store, and then , 299        ABD     F       10
10       6       If this is the case, and also this happened, then, 299  A       F       9