在Vim中进行这种替换的最佳方法是什么?

uhry853o  于 2024-01-08  发布在  其他
关注(0)|答案(4)|浏览(236)

一个几行的文档有一个标题/标题部分,然后在每个下大约10个列表。我需要把标题/标题信息与每个列表,以便它们可以正确上传到一个网站(使用逗号和管道分隔符)。它看起来像这样:

  1. SectionName1 and TitleName1
  2. 1111 - The SubSectionName A
  3. 222 - The SubSectionName B
  4. 3333 - The SubSectionName C
  5. SectionName2 and TitleName2
  6. 444 - The SubSectionName D
  7. 55555 - The SubSectionName E
  8. 66 - The SubSectionName F

字符串
重复几百次。我需要的是产生这样的东西:

  1. SectionName1,TitleName1,1111,SubSectionNameA
  2. SectionName1,TitleName1,222,SubSectionNameB
  3. SectionName1,TitleName1,3333,SubSectionNameC
  4. SectionName2,TitleName2,444,SubSectionNameD
  5. SectionName2,TitleName2,55555,SubSectionNameE
  6. SectionName2,TitleName2,66,SubSectionNameF


我知道有多种方法可以解决这个问题,但我很难在任何一个方法上扣动扳机。我理解submatches,joins和getline,但我不擅长在这个场景中实际使用它们。
任何帮助我精神上开始将不胜感激。

du7egjpx

du7egjpx1#

让我提出以下相当通用的Ex命令来解决这个问题。

  1. :g/^\s*\h/d|let@"=substitute(@"[:-2],'\s\+and\s\+',',','')|ki|/\n\s*\h\|\%$/kj|
  2. \ 'i,'js/^\s*\(\d\+\)\s\+-\s\+The/\=@".','.submatch(1).','/|'i,'js/\s\+//g

字符串
在顶层,这是:global命令,它枚举以零个或多个空格字符开头,后跟拉丁字母或下划线的行(参见:help /\h)。与此模式匹配的行应该是包含节和标题名称的标题行。命令的其余部分,在描述标题行的模式之后,是要为这些行中的每一行执行的指令。
要对标头执行的操作可以分为三个步骤。
1.删除当前标题行,同时从中提取节和标题名称。

  1. :d|let@"=substitute(@"[:-2],'\s\+and\s\+',',','')


首先,删除当前行,使用:delete命令将其保存到未命名的寄存器中。(简称@";参见:help @r:help "")是替换改变由空白字符包围的单词and的结果,实际的替换是由substitute()函数执行的。
但是,输入的不是包含整个标题行的确切字符串,而是其前缀,但省略了最后一个字符,即换行符。[:-2]表示法是[0:-2]下标表达式的缩写形式,它指定从第一个字节到从末尾数第二个字节的子字符串(参见:help expr-[:])。这样,未命名寄存器保存由逗号分隔的部分和标题名称。
1.确定从属分段线的范围。

  1. :ki|/\n\s*\h\|\%$/kj


在第一步之后,属于刚刚解析的标题行的子段记录从当前行(标题之后的一行)开始定位,直到下一个标题行,或者,如果下面没有这样的行,则到缓冲区的末尾。这些行的编号分别存储在标记ij中。(有关标记的描述,请参见:helpg ^A mark is。)
标记是使用:k命令放置的,该命令在给定范围的最后一行(默认情况下为当前行)设置指定标记。因此,与所考虑块的第一行不同,最后一行需要特定的行范围来指出其位置。一种特定形式的范围,表示给定模式匹配的下一行,在此情况下使用(参见:help :range)。定义要查找的行的位置的模式是以这样一种方式组成的,即它与紧接在标题之前的行相匹配(一行可能以空格开头,后跟字母字符),(有关Vim正则表达式语法的详细信息,请参阅:help pattern。)
1.根据所需的格式,在相应的标题行中找到的前置部分和标题名称,转换所描绘的子部分行。

  1. :'i,'js/^\s*\(\d\+\)\s\+-\s\+The/\=@".','.submatch(1).','/|'i,'js/\s\+//g


此步骤由两个:substitute命令组成,这两个命令在由标记ij(参见:help [range])标记的位置分隔的行范围内运行。
第一个替换命令匹配子部分行的开头-一个标识符,后跟一个连字符和单词The,都在空格中浮动-并将其替换为未命名寄存器的内容,该寄存器保存与逗号连接的部分和标题名称,匹配的标识符,第二个替换通过压缩行上的所有空格字符将子部分名称和后面的字母粘在一起来完成转换。
为了在第一个:substitute命令中构造替换字符串,使用了substitute-with-an-expression特性(参见:help sub-replace-\=)。命令的替换部分应该以\=开始,以便Vim以不规则的方式解释剩余的文本,而是作为一种表达(见:help expression)。该表达式的计算结果将成为替换字符串。请注意,在替换表达式中使用submatch()函数来检索子匹配的文本的编号。
1为了更好的可读性,命令被 Package ,下面列出了它的单行版本,以便于复制粘贴到Vim命令行。注意, Package 后的命令可以在Vim脚本中使用,而无需任何更改。

  1. :g/^\s*\h/d|let@"=substitute(@"[:-2],'\s\+and\s\+',',','')|ki|/\n\s*\h\|\%$/kj|'i,'js/^\s*\(\d\+\)\s\+-\s\+The/\=@".','.submatch(1).','/|'i,'js/\s\+//g

展开查看全部
7cjasjjr

7cjasjjr2#

我能想到的最简单/最快的方法是一个简单的宏。做一次,冲洗,重复。假设你的光标最初是在第一行的第一个字符(S的SectionName),这个宏应该工作,只要文档是完全相同的格式如上所述。

  1. f ctT,<Esc>yyjpjjpjddkkkddkkkJr,f ctS,<Esc>f xjJr,f ctS,f xjJr,f ctS,<Esc>f xjdd

字符串

n53p2ov0

n53p2ov03#

好吧,我认为问题不是那么清楚。为什么在你的演示输入中,在“-"之后,文本是这样的:
55555 -款E
但在你的预期输出中,它变成了:

  1. 55555,SubSectionNameE

字符串
所有的空格都被删除了,这是可以的,但是为什么“The”也被删除了呢?“the”有什么模式吗?
我写了一个awk oneliner,它删除了输出中的所有空格,但保留那些“The”在那里,你可以改变它以获得你需要的正确输出。

  1. awk -F' and ' -vOFS="," 'NF>1{s=$1;t=$2;next;}$1{gsub(/\s+/,"");gsub(/-/,",");print s,t,$0} ' input


测试您的示例输入:

  1. kent$ cat v
  2. SectionName1 and TitleName1
  3. 1111 - The SubSectionName A
  4. 222 - The SubSectionName B
  5. 3333 - The SubSectionName C
  6. SectionName2 and TitleName2
  7. 444 - The SubSectionName D
  8. 55555 - The SubSectionName E
  9. 66 - The SubSectionName F
  10. kent$ awk -F' and ' -vOFS="," 'NF>1{s=$1;t=$2;next;}$1{gsub(/\s+/,"");gsub(/-/,",");print s,t,$0} ' v
  11. SectionName1,TitleName1,1111,TheSubSectionNameA
  12. SectionName1,TitleName1,222,TheSubSectionNameB
  13. SectionName1,TitleName1,3333,TheSubSectionNameC
  14. SectionName2,TitleName2,444,TheSubSectionNameD
  15. SectionName2,TitleName2,55555,TheSubSectionNameE
  16. SectionName2,TitleName2,66,TheSubSectionNameF

展开查看全部
nwwlzxa7

nwwlzxa74#

两个正则表达式可以完成这个任务:

  1. :%s/\(Sec.*and.*\)\n\(^.*\)\n\(^.*\)\n\(^.*\)/\1,\2\r\1,\3\r\1,\4
  2. :%s/ and \|,\s\{5}\| - The /,/g

字符串

相关问题