2

我想在双引号中转换一个全逗号分隔的字符串文字,例如:

"hello,world,stack,overflow"

进入Cassandra 列表格式

"['hello','world','stack','overflow']"

其中每个元素都包含在单引号中,整个原始字符串包含在方括号和双引号中。我怎样才能在 Vim 中做到这一点?

在我的输入中,这种引用的逗号分隔字符串是 CSV 格式表中行的一部分。下面是一个例子:

other,fields,123,456,"hello,world,stack,overflow"
second,row,567,890,"another,comma,separated,string"
...

我想将其转换为:

other,fields,123,456,"['hello','world','stack','overflow']"
second,row,567,890,"['another','comma','separated','string']"
...

我的目标字符串都没有跨越多行。

4

2 回答 2

6

试试这个

:%s/\v(".*)@<=\s*([^,"]+)\s*(.*")@=/'\2'/g
:%s/"/"[
:%s/"\[\@!/]"

或一次全部

:%s/\v(".*)@<=\s*([^,"]+)\s*(.*")@=/'\2'/ge | %s/"/"[/e | %s/"\[\@!/]"

这适用于示例。如果行中有超过一对引号,这将不起作用。

解释

:%s/\v(".*)@<=\s*([^,"]+)\s*(.*")@=/'\2'/g

这会查找一个字符串,该字符串在其前后都有一个引号,并带有前瞻和后瞻。然后我们捕获不是逗号或引号的所有内容,并将其替换为捕获的单引号部分。这会抛出任何前导或尾随空格。

:%s/"/"[

:s如果您以前使用过,这应该是不言自明的

:%s/"\[\@!/]"

这使用负前瞻来查找未跟在左括号后的第一个引号,并将其替换为右括号和引号。


在考虑了更多之后,我认为无论天气如何,您都可以一次完成整个文件,在线上的报价不止一对。

第一个函数只是一个辅助函数,它使替换命令更易于键入。(您可以在一行中完成三个替代命令,但这会很难看)。它和上面的东西做同样的事情。

function! ReplaceCommaSeperated(string)
    let l:tmp = substitute(a:string, '[^,"]\+', "'\\0'", 'g')
    let l:tmp = substitute(l:tmp, '"', '"[', '')
    return substitute(l:tmp, '"\[\@!', ']"', '')
endfunction

function! RunCommaReplace()
    %s/".\{-}"/\=ReplaceCommaSeperated(submatch(0))/g 
endfunction

第二个函数查找所有带引号的字符串并将其传递给函数,并立即将其全部替换。而且您知道哪一个是开头和结尾引号,因为保证只有一对引号。

这样做的原因和正则表达式解析器不会混淆的原因是模式匹配在第一个匹配结束后开始。因此,如果您有该字符串,则该字符串" A " B " C "
" A "将是第一个匹配项,并且" C "将是第二个匹配项,因为当解析器尝试在 B 之后进行匹配时,它会看到B " C "并且不匹配。

要在你的 vim 中运行它,只需将这两个函数复制到你的 vimrc 中。并在您要运行的文件中运行以下命令。

:call RunCommaReplace()
于 2013-08-09T23:21:27.663 回答
1

1. 单次运行 :substitute命令即可解决问题。我可以看到这种方法的两种略有不同的实现。它们都遵循相同的表达式替换模式:

:%s/"\([^"]*\)"/\='"['.Q.']"'/g

根据上面的命令,用双引号括起来的所有零个或多个字符的序列都将替换为 \=符号后指定的表达式的评估结果(请参阅 参考资料:help sub-replace-\=)。与周围的括号和引号连接的是替换表达式 Q,表示将逗号分隔列表的文本(由模式的(仅)子匹配捕获)转换为相同列表的字符串,其中项目包含在单引号。

表达式的两个版本 Q如下。

2. 第一个版本很简单:

  1. 使用逗号作为分隔符将匹配的文本分解为元素列表:

    split(submatch(1), ',', 1)
    

    (最后一个参数在这里是可选的,只有在双引号字段的开头或结尾可能有空元素时才需要。)

  2. 用引号括起来:

    map(‹…›, '"''".v:val."''"')
    
  3. 并将它们按顺序连接回来,与分隔逗号交替:

    join(‹…›, ',')
    

结合这些步骤,我们得到表达式

join(map(split(submatch(1), ',', 1), '"''".v:val."''"'), ',')

3. 第二个,性能更高一点的表达式 Q 也不是很复杂;它只是一个替换(在替换内部,就像Q 在:s 命令内部一样)。在我们的处置中,我们拥有substitute()与同名命令等效的功能。

表达方式

substitute(submatch(1), '[^,]\+', "'&'", 'g')

导致所有出现的不包含逗号的非空字符序列被更改为它们本身,并在两端附加单引号。

如果我们希望这个表达式也处理空列表项(就像上面的第一个变体一样),我们需要做的就是将最后一个模式更改为

'\%(^\|,\)\zs[^,]*'

这样,它也允许空序列,但受限于它们位于开头或逗号之后。(查看:help \zs:help \%(更好地理解更改后的模式是如何工作的。)

4. 因此,我们可以使用以下两个命令之一:

:%s/"\([^"]*\)"/\='"['.join(map(split(submatch(1),',',1),'"''".v:val."''"'),',').']"'/g

或者

:%s/"\([^"]*\)"/\='"['.substitute(submatch(1),'\%(^\|,\)\zs[^,]*',"'&'",'g').']"'/g

它们都适用于一行中的所有引用字段。

于 2013-08-23T10:43:33.673 回答