多行文档有一个标题/标题部分,然后每个部分下面有大约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,但在这种情况下我不擅长实际使用它们。
任何帮助我精神上的人都会非常感激。
答案 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
)。假设符合此模式的线条
是包含节名和标题名称的标题行。剩下的
在描述标题行的模式之后,命令是指令
为每一行执行。
要对标题执行的操作可分为三个步骤。
删除当前标题行,同时提取部分 和它的标题名称。
:d|let@"=substitute(@"[:-2],'\s\+and\s\+',',','')
首先,删除当前行,将其保存到未命名的寄存器中,
使用:delete
命令。然后,更新它的内容
注册(简称@"
;请参阅:help @r
和:help ""
)
替换结果改变了包围的单词and
空白字符,单个逗号。实际的更换是
由substitute()
函数执行。
但是,输入不是包含整个标题的确切字符串
行,但它的前缀留下了最后一个字符,即
换行符号。 [:-2]
符号是一种简短形式
[0:-2]
下标表达式,用于指定子字符串
从结尾算起的第二个字节的第一个字节(参见:help
expr-[:]
)。这样,未命名的寄存器保存了部分和
标题名称以逗号分隔。
确定从属分段行的范围。
:ki|/\n\s*\h\|\%$/kj
在第一步之后,子部分记录属于正义
解析的标题行位于从当前行开始(一个
按照标题)直到下一个标题行,如果没有
下面的这一行,缓冲区的结尾。这些行的数量是
分别存储在标记i
和j
中。 (有关标记的说明,请参阅:helpg ^A mark
is
。)
使用设置指定标记的:k
命令放置标记
在给定范围的最后一行,即当前行,by
默认。因此,与所考虑的块的第一行不同,最后一行
一个需要特定的行范围来指出其位置。
一种特定形式的范围,表示给定的下一行
在这种情况下使用模式匹配(请参阅:help :range
)。该
定义要找到的行的位置的模式,由
这样一种方式,它匹配标题前面的一行(a
从可能的空格开始,后跟字母顺序排列
字符),或最后一行。 (有关详细信息,请参阅:help pattern
关于Vim正则表达式的语法。)
根据所需格式转换描绘的子剖面线, 在相应标题中找到前缀部分和标题名称 线。
:'i,'js/^\s*\(\d\+\)\s\+-\s\+The/\=@".','.submatch(1).','/|'i,'js/\s\+//g
此步骤由运行的两个:substitute
命令组成
在由标记的位置划分的行范围内
标记为i
和j
(请参阅:help [range]
)。
第一个替换命令匹配子部分的开头
line - 一个标识符后跟一个连字符和单词The
,全部
漂浮在空白中 - 并用它的内容替换它
未命名的寄存器,持有部分和标题名称连接
用逗号,匹配的标识符和另一个逗号。第二
替换通过挤压所有空格来完成转换
行上的字符,以分段名称和以下字符
一起来信。
在第一个:substitute
中构造替换字符串
命令,使用替换表达式功能(参见:help
sub-replace-\=
)。命令的替换部分应该开始
使用\=
让Vim解释不在常规文本中的剩余文本
方式,但作为表达(见:help expression
)。的结果
表达式的评估成为替换字符串。注意
在替换表达式中使用submatch()
函数
通过编号检索子匹配的文本。
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