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

时间:2011-12-26 17:31:51

标签: 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

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

SectionName1,TitleName1,1111,SubSectionNameA
SectionName1,TitleName1,222,SubSectionNameB
SectionName1,TitleName1,3333,SubSectionNameC
SectionName2,TitleName2,444,SubSectionNameD
SectionName2,TitleName2,55555,SubSectionNameE
SectionName2,TitleName2,66,SubSectionNameF

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

任何帮助我精神上的人都会非常感激。

3 个答案:

答案 0 :(得分: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

在顶层,这是枚举行的:global命令 以零个或多个空白字符开头,后跟拉丁字母或 下划线(见:help /\h)。假设符合此模式的线条 是包含节名和标题名称的标题行。剩下的 在描述标题行的模式之后,命令是指令 为每一行执行。

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

  1. 删除当前标题行,同时提取部分 和它的标题名称。

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

    首先,删除当前行,将其保存到未命名的寄存器中, 使用:delete命令。然后,更新它的内容 注册(简称@";请参阅:help @r:help "") 替换结果改变了包围的单词and 空白字符,单个逗号。实际的更换是 由substitute()函数执行。

    但是,输入不是包含整个标题的确切字符串 行,但它的前缀留下了最后一个字符,即 换行符号。 [:-2]符号是一种简短形式 [0:-2]下标表达式,用于指定子字符串 从结尾算起的第二个字节的第一个字节(参见:help expr-[:])。这样,未命名的寄存器保存了部分和 标题名称以逗号分隔。

  2. 确定从属分段行的范围。

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

    在第一步之后,子部分记录属于正义 解析的标题行位于从当前行开始(一个 按照标题)直到下一个标题行,如果没有 下面的这一行,缓冲区的结尾。这些行的数量是 分别存储在标记ij中。 (有关标记的说明,请参阅:helpg ^A mark is。)

    使用设置指定标记的:k命令放置标记 在给定范围的最后一行,即当前行,by 默认。因此,与所考虑的块的第一行不同,最后一行 一个需要特定的行范围来指出其位置。 一种特定形式的范围,表示给定的下一行 在这种情况下使用模式匹配(请参阅:help :range)。该 定义要找到的行的位置的模式,由 这样一种方式,它匹配标题前面的一行(a 从可能的空格开始,后跟字母顺序排列 字符),或最后一行。 (有关详细信息,请参阅:help pattern 关于Vim正则表达式的语法。)

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

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

    此步骤由运行的两个:substitute命令组成 在由标记的位置划分的行范围内 标记为ij(请参阅:help [range])。

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

    在第一个:substitute中构造替换字符串 命令,使用替换表达式功能(参见:help sub-replace-\=)。命令的替换部分应该开始 使用\=让Vim解释不在常规文本中的剩余文本 方式,但作为表达(见:help expression)。的结果 表达式的评估成为替换字符串。注意 在替换表达式中使用submatch()函数 通过编号检索子匹配的文本。


  4. 1 该命令被包装以获得更好的可读性,它的单行 下面列出了版本,以便于复制粘贴到Vim命令行。注意 包装的命令可以在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
    

答案 1 :(得分:2)

我能想到的最简单/最快的方法是一个简单的宏。做一次,冲洗,重复。 假设您的光标最初位于第一行的第一个字符(SectionName的S)上,只要该文档与上面发布的格式完全相同,该宏就应该有效。

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

答案 2 :(得分:1)

我认为问题不是那么清楚。为什么在你的演示输入中,在“ - ”之后,文本就像:

  

55555 - SubSectionName E

但是在预期的输出中,它变成了:

55555,SubSectionNameE

删除了所有空格,这没关系,但为什么“The”也被删除了? “the”有什么模式吗?

我写了一个awk oneliner,它删除了输出中的所有空格,但是将那些“The”留在那里,你可以改变它以获得你需要的正确输出。

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

测试您的示例输入:

kent$  cat v
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

kent$  awk -F' and '  -vOFS="," 'NF>1{s=$1;t=$2;next;}$1{gsub(/\s+/,"");gsub(/-/,",");print s,t,$0} ' v
SectionName1,TitleName1,1111,TheSubSectionNameA
SectionName1,TitleName1,222,TheSubSectionNameB
SectionName1,TitleName1,3333,TheSubSectionNameC
SectionName2,TitleName2,444,TheSubSectionNameD
SectionName2,TitleName2,55555,TheSubSectionNameE
SectionName2,TitleName2,66,TheSubSectionNameF