如何在文件中添加永久行号?


22

我有一个这样的文本文件(在Windows上使用gVim)

foo bar baz quux 
corge grault garply 
waldo fred plugh 
[...150 more lines...]
xyzzy thud

我想在文件的每一行中添加一个数字。不使用:set number,而是将数字添加为每行之前的文本,如下所示,因此该数字是文件的一部分。

1. foo bar baz quux 
2. corge grault garply 
3. waldo fred plugh 
[...~150 more lines...]
155. xyzzy thud

awk可能是这项工作的工具。但是我在Windows上(叹气)。
roblogic

已经在这里回答,没关系:)
roblogic 2015年

1
也许..或者这更笼统?
muru

相似,但是我不知道什么是永久行号。其次,另一个问题是专门针对Windows上gVim的所有行(答案就是这样做),这是仅在普通vim中一个段落的简单编号列表。
kenorb

3
好吧,我想post使用的是“永久”字样,它表示要修改缓冲区,并且数字不是纯粹的视觉对象(与您相同)。在Windows上指定gvim的原因是为了避免使用诸如cat或的外部实用程序nl,它们可以执行数字行,但通常在Windows上不可用(如OP从其关于的评论中所指出的awk)。前两个解决方案是纯Vim。最后,所有行与一个参数仅是范围选择的问题。显然不是大问题。
muru

Answers:


37

以纯Vim方式:

:%s/^/\=line('.').". "

说明:

:%s/^/            " the substitution will be applied to the beginning of every line
\=                " the rest of the replacement part is an expression
line('.').". "    " the expression returns the current line number concatenated with a dot and a space

请参阅:help \=:help line()

在替换部分中使用表达式非常强大,FWIW是vimscript的一个很好的切入点。


如何在vimrc的键盘映射中添加这个非常有用的命令?
cosmicraga

要获得替代的vim帮助主题::help sub-replace-expression
akurtser

9

Vim宏的一件好事是它们可以递归(它们可以调用自己):

  1. 清除寄存器q: qqq
  2. 将数字添加到第一行:(ggI1.不要忘记空格!)
  3. 返回到行首并开始记录宏: 0qq
  4. 复制号码: yW
  5. 向下移动一行并粘贴数字: +P
  6. 返回到行的开头并增加数字: 0<c-a>
  7. 返回到行的开头(因此宏在加倍时不会中断!): 0
  8. 调用一次宏,以使其递归。此时,寄存器q中仍然没有任何内容,因此将不会发生任何事情:@q
  9. 保存宏: q
  10. 再调用一次宏,然后观察火花飞扬!: @@

然后,宏将继续调用自身,直到到达文件末尾。

您可以将递归宏技巧用于许多其他类似的问题,因此这是一个很好的认识。

如果由于某种原因不想使用递归宏,则可以省略第1步和第8步,并使用计数多次100@q运行宏,例如将运行q100次宏。


1
功能强大的东西,我向您致意。宏对我来说就像是黑魔法……
roblogic 2015年

1
@ropata,宏只是(通常是)正常模式命令的序列。
romainl 2015年

1
@romainl我认为最好将其视为一系列击键
Rich

2
@Rich,它可以是许多事情的序列,包括ex命令。
romainl 2015年

2
@romainl是的,这就是为什么我认为最好将其视为击键。它会精确播放您在键盘上键入的内容(包括您所说的ex命令),就像您手动键入所有内容一样。
丰富

7

我喜欢使用vim global命令来完成这样的任务。这适用于将迭代添加到行的开头或修改文本中的符号。它看起来比其他解决方案更为复杂,但是在您方便使用时它是一种非常灵活的模式,并且无需过多考虑即可轻松进行修改。

首先,选择您的范围(您要应用哪些行)。我通常使用标记(例如ma,在第一行和mb第二行上,但是您也可以使用行号或视觉选择),然后输入以下命令的修改(当前针对您的用例进行了调整)

:let i=1|'a,'bg/^/s/^/\=i.". "/|let i=i+1

解构

:let i=1

这将设置i一个具有初始值的变量。通常列表以1开头,因此我将i设置为1。

|

栏开始一个新命令

'a,'b

这将设置下一个命令的范围。我要逐个a标记b,它将设置在列表的第一行和最后一行。

g/^/

这是全局命令。它在文件(或范围)中搜索给定的正则表达式,然后在匹配的每一行上执行其余命令行。我通过搜索“行的开头”来匹配每一行。如果您有类似的文字

Item some txt
other text

Item second item
whatever
Item third

并且只想将这些标签放在Item其他行的前面,而忽略其他行,则执行g/Item/g/^Item/改为(假设原义Item文本)

s/^/\=i.". "/

这将执行正则表达式,以用i串联的值替换行的开头.。通常,您可以对任何内容执行此操作(Item例如,将标签替换为数字)。

|let i=i+1

即使该栏启动了一个新命令,它仍将第二条命令设置为在全局命令中运行,而不是在完成全局操作之后运行。结果是我们i在g处理下一行之前递增。这是另一个灵活性的地方。i的修改可以是任何值(递增2,则调用生成斐波那契数列的下一个元素的函数)。


7

将数字添加到所有行

可以使用:%!nl -ba:%!cat -n命令将行号添加到所有行。

在Windows上,您必须安装Cygwin / MSYS / SUA。

将数字添加到所选行

要仅为选定的行添加数字,请在可视模式(v和光标)中选择它们,然后在完成时-执行命令:(:%!nl忽略空行)或:%!cat -n(包括空行)。

格式化

要删除多余的空格,请在可视块(Ctrl+ v)中选择它们,然后删除它们(x)。

添加一些字符(.:)数字之后),在视觉块(选择它们Ctrl+ v),然后附加的字符(A键入字符,然后用完成Esc)。


2
这与问题中给出的格式不同。但是,我喜欢该解决方案的简单性。
Karl YngveLervåg2015年

@KarlYngveLervåg谢谢,将其包含在答案中。
kenorb

5

romainl答案的修改:

:%s/^\(\d\+\. \)\?/\=line('.').". "

这不仅会添加行号,还会替换已经存在的现有行号。当然,如果您在中间插入一行,它将按预期重新编号所有内容。

这可以通过替换任意数字后跟来实现。在行的开头有一个新数字的空格。如果您已经有一行以该模式开头的行,则显然会中断,因此请谨慎使用。

增加的部分:

  • ^ -行首
  • \( -开始新的小组
  • \d\+ -一次或多次匹配一个数字
  • \. -匹配一个点(.)和一个空格
  • \) -结束子组
  • \? -使该组为可选,以便该行上没有数字时也能像以前一样工作。

温馨提示:
删除行号,可以使用相同的模式,将空白部分为空:

:%s/^\(\d\+\. \)\?//

5
I1. <esc>^qqyWjP^<C-a>q

这将对前两行@q进行编号,您可以按以便对随后的行编号(或者18@q如果要对总共20行编号,请键入ex。)。

说明:

I1. <esc>  Number the first line
hqq        Go back to the start of the line and start recording a macro
yWjP       Copy the line number to the next line
^<C-a>     Increment the next line's line number
q          Finish recording

这样做的好处是它不需要任何外部命令,例如,如果您在Windows上使用Vim,这将很有用。


输入后,1. <esc>h您位于第二列,而不是第一列。我将取代h0,在这之后,我认为您的解决方案应该是非常好的。
Karl YngveLervåg2015年

@KarlYngveLervåg糟糕,这是一个错字。谢谢,修复它。
Doorknob

没问题。但是,您仍然没有更新说明。另外:在许多键盘上,^等待第二个字符以允许输入诸如之类的组合^a -> â。我仍然同意其最佳解决方案,但我认为也应提及这一点。
Karl YngveLervåg2015年

3

我认为选择的答案是最好的,但是出于多样性的考虑,我将使用外部程序提供一个替代方案:

:%!cat -n

这将%通过外部程序过滤您的整个缓冲区(用表示)cat,其中-n标志在输入的每一行前都添加了行号。

当然,这要求您已经cat安装了(对于(可能)所有类似Unix的系统都是如此)。

请查看:help :range!有关通过外部程序进行过滤的更多详细信息。


1
我意识到,问问者在Windows上使用的是gVim,因此该解决方案可能在那里不起作用。但是,我认为它仍然为其他人提供了一些学习的机会。
tommcdo 2015年

如果您已将其安装msysgit并添加到PATH(IIRC是安装选项)中,则该解决方案也应在Windows上运行。
马丁·图尔诺伊

4
cat -n不是POSIX,而是nl,所以可能是一个更好的选择。
muru 2015年

2

以下可能是一个有点棘手的解决方案(在<和>之间编写的所有内容都应在按Ctrl+ 之后插入v):

:%normal :redir @"<Enter>:-=<Enter>:redir END<Enter>I<C-R>".<Tab><Esc>kdd

解构

:%normal {commands}

在范围指定的每一行上运行普通模式命令,在这种情况下,每行

:redir @"

将ex命令产生的每个输出重定向到未命名的缓冲区。

:.=

是输出当前行号的ex命令(不幸的是,前面有换行符)

:redir END

停止重定向到未命名的缓冲区

I<C-R>".<Tab><Esc>

用插入未命名缓冲区的内容。以及每行前面的标签,并退出插入模式。

kdd

向上一行并删除换行符,该换行符是:。=命令的结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.