Vim 替换一行子串中的几个相似元素

Vim replace several similar elements in a substring of a line

我需要格式化很多 JSON 条目。目标是用已经存在的文本的 Title Cased 版本替换值部分,并使用空格而不是下划线。这些条目看起来类似于:

"ASDF": "TUOJKLDSF",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG_IKEH",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG_IKEH_SDF",

我试图让它们看起来像这样:

"ASDF": "Tuojkldsf",
"ASDF": "Tuojkldsf Fugzihojkowg",
"ASDF": "Tuojkldsf Fugzihojkowg Ikeh",
"ASDF": "Tuojkldsf Fugzihojkowg Ikeh Sdf",

我遇到的问题是我需要保持第一部分不变,所以我不能只替换所有的 A-Z 实例,而且我无法找到一种在一个命令中完成所有操作的好方法。

我最后做的是为值中的每个“单词”数量编写一个单独的命令:

:%s/\([^:\n]\+: "\)\([A-Z]\)\([A-Z]\+\)"/\L"

"ASDF": "Tuojkldsf",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG_IKEH",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG_IKEH_SDF",

:%s/\([^:\n]\+: "\)\([A-Z]\)\([A-Z]\+\)_\([A-Z]\)\([A-Z]\+\)"/\L\E \L"

"ASDF": "Tuojkldsf",
"ASDF": "Tuojkldsf Fugzihojkowg",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG_IKEH",
"ASDF": "TUOJKLDSF_FUGZIHOJKOWG_IKEH_SDF",

这工作正常,但我想用一个命令完成这个?

一种方法是先将下划线更改为 spaces 然后 运行 a 所有行的正常模式命令:

:%s/_/ /g | %norm f:gu[=10=]Wl~W.W.W.W.W.
  • :%s/_/ /g 将每一行的每个下划线替换为 space。
  • | 应用另一个命令。
  • :%norm 对每一行应用以下正常模式命令。
  • f: 将光标移动到冒号。
  • gu$ 小写该行的其余部分。
  • 0Wl 将光标移动到值的开头(就在引用部分内)。
  • ~ 首字母小写。
  • W. 移动到下一个单词(在 whitespace 之后)并重复小写命令。

只要你输入足够多W.它就会给你你想要的输出(因为 对于 :%normW 动作不会移动到下一行)。

结果:

"ASDF": "Tuojkldsf",
"ASDF": "Tuojkldsf Fugzihojkowg",
"ASDF": "Tuojkldsf Fugzihojkowg Ikeh",
"ASDF": "Tuojkldsf Fugzihojkowg Ikeh Sdf",

Vim 可以处理正则表达式中的大小写。 \U 匹配大写,\L 匹配小写。您也可以在替换时使用这些来更改大小写。

要使用 |“另一个命令”管道在一行中执行此操作,我会这样处理:

:%s/_/ /g |
Whole file, substitute "_" with " ", all occurences; + more command

%s/\(\a\)\(\a*\)/\L/g |
%s/             /      /g |  Whole file, substitute, all occurences; + more
   \(\a\)                    Backref capture 1 (first letter of a word)
         \(\a*\)             Backref capture 2 (remaining letters of word)
                 \L      Backref 1; lowercase the next; backref 2

This would leave us with  Asdf  at the front, but we want  ASDF  so:
%s/^\("\a\{4\}": \)/\U/
%s/                /    /    Whole file, subst. (no "g"; should be 1 per line)
   ^                         Start of line
    \("\a\{4\}": \)          Backref is quote, 4 letters, quote, colon, space
                    \U     Uppercase the next; backref 1    

全部放在一条线上: :%s/_/ /g|%s/\(\a\)\(\a*\)/\L/g|%s/^\("\a\{4\}": \)/\U/

其他方法也是可能的——不是将第一个字母作为单独的反向引用捕获组,而是将整行小写,然后将大写应用于每个单词的第一个字母;等等