vim 正则表达式搜索和替换

vim 正则表达式搜索和替换

我正在尝试替换文件中的部分字符串

例如我有一个 csv 文件。

r1,col1,col2,35,000,col4,col5
r2,col1,col2,1,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4,325.33,col4,col5

本质上我想替换上面的 col3 中的 ,​​ 。同时保留前 x 个数字,如下所示:

r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5

通常我会执行

:%s/\,[0-9]*\,/\,\1/g

但是当我跑的时候我得到了

r1,col1,col2,000,col4,col5
r2,col1,col2,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,325.33,col4,col5

我应该在替换的第二部分中使用什么,以便获得所需的输出。

答案1

可能:%s/\v(([^,]*,){3})([0-9]+),([0-9])/\1\3\4/

您的目标是从第四个字段中删除逗号(如果存在),而不删除其他地方的逗号,也不删除任何其他文本。复杂的因素是逗号也用作字段分隔符。要解决该问题,您必须考虑您对逗号可以出现在字段内的条件的了解。毕竟,如果没有任何进一步的限制,您的记录是不明确的。

人们很容易认为任何跨越两位数的逗号都会被删除,但这行不通。您的示例输入表明您一个字段以数字结尾,下一字段以一 ( col2,35,000) 开头。

如果您知道前三个字段本身不包含逗号,那么问题就会变得容易得多,因为在删除任何逗号之前可以跳过零个或多个非逗号后跟逗号的前三个序列。那么问题就变成了如何判断第四场何时结束。您应该问自己是否要删除多种的第四个字段中的逗号,或者是否始终没有逗号或一个逗号。

为了这个答案,我假设第四个字段最多包含一个应该删除的逗号。我将进一步假设逗号出现在一位或多位数字之后且至少一位数字之前。然后你可以在 Vim 中使用它:

:%s/\v(([^,]*,){3})([0-9]+),([0-9])/\1\3\4/

或者,如果您更喜欢使用 Sed:

sed -r 's/(([^,]*,){3})([0-9]+),([0-9])/\1\3\4/' filename.csv

怎么运行的

正则表达式(([^,]*,){3})匹配前三个字段及其后面的字段分隔符,所有这些字段您都希望保持相同。[^,]匹配除 之外的任何单个字符,。之后*它会导致零个或多个匹配,而不是恰好匹配一个。之后的内容,与该非逗号字段后面的实际逗号匹配。这全部被分组( )并且{3}应用于它导致它匹配三次而不是一次。然后整个事情被分组,以便可以使用 访问它\1。 (内部组还捕获和可以可以作为 访问\2。)

然后([0-9]+)匹配一个或多个 ( +) 数字 ( [0-9]) 并捕获匹配 ( ( )),以便可以将其访问为\3.该,字符与原义逗号匹配;这就是我们的一部分不是会保留。然后([0-9])捕获一个数字,以便可以将其作为 访问\4

您可以通过使用单个组 for\1\3,即 ,使正则表达式更简单一些(([^,]*,){3}[0-9]+)。我避免了这种情况,因为我觉得它隐藏了记录的结构——它们由用逗号分隔的字段组成——但这样做并没有什么错。如果你这样做了,\4就会变成\3,所以在替换模式中你会使用\1\3而不是\1\3\4

最后,\v在 Vim 正则表达式的开头并-r传递给sed服务以允许您使用扩展的正则表达式语法。这就是为什么我能够写(and)代替\(and \),and+代替\+

答案2

您可以使用以下正则表达式在 vim 中执行此操作:

 %s/\([^,]\+,\)\{3}[^,]*\zs,\ze[^,]*\(,[^,]\+\)\{2}//

这是一个解释:

  • \([^,]\+,\)\{3}精确匹配 3 个 csv 字段和后面的逗号。

  • \(,[^,]\+\)\{2}精确匹配 2 个 csv 字段和前面的逗号。

  • 这两个表达式中间的内容将捕获必须删除一个逗号的字段。

答案3

$ sed 's/,\([0-9]\+\),\([0-9]\+\)/,\1\2/' input
r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5

解释一下所使用的表达式:

  NODE                     EXPLANATION
  ,                        ','
  (                        group and capture to \1:
    [0-9]+                   any character of: '0' to '9' (1 or more
                             times (matching the most amount
                             possible))
  )                        end of \1
  ,                        ','
  (                        group and capture to \2:
    [0-9]+                   any character of: '0' to '9' (1 or more
                             times (matching the most amount
                             possible))
  )                        end of \2

然后我们将匹配项替换为,\1\2

答案4

您可以用于awk此任务。该脚本可以处理第四列中的多个逗号。我认为使用 , 来处理这种情况(多个逗号)很困难vim。但使用起来很容易awk

笔记:此解决方案仅适用于六列(我r1也在计算列)。

awk '
BEGIN {
    FS = ",";
    OFS = ",";
}
{
    accum = "";
    for(i = 4; i < NF - 1; i++) {
        accum = accum $i;       
    }

    print $1, $2, $3, accum, $(NF - 1), $NF;
}' input.txt

输入(添加目标字段中包含多个逗号的行以进行测试)

r1,col1,col2,35,000,col4,col5
r2,col1,col2,1,000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4,325.33,col4,col5
r5,col1,col2,4,325,250.33,col4,col5
r6,col1,col2,4,100,325,250.33,col4,col5

输出

r1,col1,col2,35000,col4,col5
r2,col1,col2,1000,col4,col5
r3,col1,col2,325.33,col4,col5
r4,col1,col2,4325.33,col4,col5
r5,col1,col2,4325250.33,col4,col5
r6,col1,col2,4100325250.33,col4,col5

相关内容