【问题标题】:How to convert comma-separated strings to Cassandra list format in Vim?如何在 Vim 中将逗号分隔的字符串转换为 Cassandra 列表格式?
【发布时间】:2013-08-11 23:27:50
【问题描述】:

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

"hello,world,stack,overflow"

进入Cassandra list format

"['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']"
...

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

【问题讨论】:

  • 你有多少这样的?他们是每行一个吗?正则表达式不能很好地匹配引号(即无法区分第一个和最后一个),尽管如果行中没有其他引号,您可能会侥幸逃脱。
  • 每行至少有一个。实际上它是一个 csv 文件。
  • 能给个示例文件吗?
  • 给你。如果有不清楚的地方请告诉我:)
  • 如果您知道字符串中正好有四个字段,我建议您使用宏。

标签: regex list vim replace cassandra


【解决方案1】:

试试这个

:%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()

【讨论】:

  • 现在是我检查每个符号的含义的时候了,希望我能找到如何处理一对以上的引号。
  • @keelar 正则表达式确实是错误的工作。它无法区分" (this) " (that) ",因为它所做的只是模式匹配。它无法判断哪个是正确配对的。例如,正确引用的是这个还是那个。但是大多数符号都可以在:h pattern-searches 中找到
  • 如果我可以一次替换一列这些字符串,那会让事情变得更容易吗? (例如,我要替换的列是第 3 列和第 5 列,我可以发出两个不同的查找/替换来转换它们)
  • @keelar 听起来应该没问题,如果你能让它工作的话
  • @keelar 我添加了一个函数,该函数应该一次完成整个文件,而不管行中有多少对引号。不确定你是否需要它,但我认为它值得分享。
【解决方案2】:

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

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

【讨论】:

  • 非常感谢!这也有效!你介意再解释一下吗?
  • @keelar:当然。我已经添加了详细的解释以及相同的单通道方法的第二个实现。请让我知道您对此有何看法。希望对您有所帮助。
猜你喜欢
  • 2023-04-02
  • 2013-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 2012-09-04
相关资源
最近更新 更多