在Vim中这种替代的最佳方法是什么?

Ric*_*sin 2 regex vim substitution

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

SectionName1 and TitleName1
     1111 - The SubSectionName A

     222 - The SubSectionName B

     3333 - The SubSectionName C

SectionName2 and TitleName2
     444 - The SubSectionName D

     55555 - The SubSectionName E

     66 - The SubSectionName F
Run Code Online (Sandbox Code Playgroud)

重复几百次.我需要的是产生类似的东西:

SectionName1,TitleName1,1111,SubSectionNameA
SectionName1,TitleName1,222,SubSectionNameB
SectionName1,TitleName1,3333,SubSectionNameC
SectionName2,TitleName2,444,SubSectionNameD
SectionName2,TitleName2,55555,SubSectionNameE
SectionName2,TitleName2,66,SubSectionNameF
Run Code Online (Sandbox Code Playgroud)

我意识到这个解决方案有多种方法,但是我很难在任何一种方法上触发触发器.我理解子匹配,连接和getline但我不擅长在这种情况下实际使用它们.

任何让我精神上开始的帮助将不胜感激.

ib.*_*ib. 5

让我提出以下非常一般的Ex命令来解决这个问题.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
Run Code Online (Sandbox Code Playgroud)

在顶层,这是一个:global命令,它枚举以零个或多个空格字符开头的行,后跟拉丁字母或下划线(请参阅参考资料:help /\h).匹配此模式的行应该是包含节名和标题名称的标题行.在描述标题行的模式之后,命令的其余部分是要为每个行执行的指令.

要对标题执行的操作可以分为三个步骤.

  1. 删除当前标题行,同时从中提取节标题和标题名称.

    :d|let@"=substitute(@"[:-2],'\s\+and\s\+',',','')
    
    Run Code Online (Sandbox Code Playgroud)

    首先,使用该:delete命令删除当前行,将其保存到未命名的寄存器中.然后,将该寄存器的内容(称为@";参见:help @r:help "")更新为替换的结果,and将由空白字符包围的单词改为单个逗号.实际更换由substitute()功能执行.

    但是,输入不是包含整个标题行的确切字符串,而是它的前缀省略了最后一个字符,即换行符号.该[:-2]表示法是短形式 [0:-2]标表达式,其指定从第一字节的子字符串以从端部(见第二个计数:help expr-[:]).这样,未命名的寄存器保存以逗号分隔的部分和标题名称.

  2. 确定从属分段线的范围.

    :ki|/\n\s*\h\|\%$/kj
    
    Run Code Online (Sandbox Code Playgroud)

    在第一步之后,属于刚刚解析的标题行的子部分记录位于从当前行(标题之后的那一行)开始直到下一个标题行,或者如果下面没有这样的行,则是缓冲区的结尾.这些线的编号存储在标记ij分别.(有关:helpg ^A mark is商标的说明,请参阅.)

    :k默认情况下,使用在给定范围的最后一行(即当前行)设置指定标记的命令放置标记.因此,与所考虑的块的第一行不同,最后一行需要特定的行范围来指出其位置.在这种情况下,使用特定形式的范围,表示给定模式匹配的下一行(请参阅参考资料:help :range).定义要找到的行的位置的模式以这样的方式组成:它匹配紧接在标题之前的行(以可能的空格开头,后跟字母字符开头的行)或最后一行.(有关:help patternVim正则表达式语法的详细信息,请参阅.)

  3. 根据所需的格式转换描绘的子部分行,在相应的标题行中找到前面的部分和标题名称.

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

    此步骤包含两个:substitute命令,这两个命令在由标记ij(参见:help [range])标记的位置分隔的行范围上运行.

    第一个替换命令匹配子部分行的开头 - 后跟连字符的标识符和单词The,全部浮动在空白中 - 并用未命名的寄存器的内容替换它,保持部分和标题名称用逗号连接,匹配的标识符和另一个逗号.第二个替换通过挤压线上的所有空白字符来完成转换,以将子部分名称和后面的字母拼接在一起.

    要在第一个:substitute 命令中构造替换字符串,请使用substitute-with-an-expression功能(请参阅参考资料:help sub-replace-\=).命令的替换部分应该从\=Vim 开始,不是以常规方式解释剩余的文本,而是作为表达式(参见参考资料:help expression).该表达式的评估结果成为替换字符串.请注意submatch(),在替换表达式中使用该函数可以按其编号检索子匹配的文本.


1 命令被包装以获得更好的可读性,下面列出了它的单行版本,以便于复制粘贴到Vim命令行.请注意,wrapped命令可以在Vim脚本中使用而不做任何更改.

: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
Run Code Online (Sandbox Code Playgroud)