本笔记有特殊目录,点击开启:
在Linux系统中编辑文本总是离不开一位老帮手——Vi。而因为其诞生的年代有些久远,有些操作在现在看来可能有点“反直觉”。
于是我决定写这样一篇小笔记,记录一下我记忆Vi的这些这些 常用 操作和指令的方法(主要靠的是英语和图示了)。
当然,正如“好记性不如烂笔头”所言,多实践才是熟练掌握技能的王道。

Vi代表的可能是Visual Instrument(可视工具),Visual Interface(可视界面)亦或就是Visual(可视化的)单词本身。
Vim倒是很明显,代表的是Vi IMproved(Vi改进版)。
对Vi而言:
命令模式 (Command mode) ,刚进入Vi编辑器时的默认模式。在此模式下能输入Vi的合法指令(通常是一些字符)来进行操作。
插入(输入)模式 (Insert mode) ,顾名思义,是直截了当地编辑文本的模式。
末行(底线)模式 (Last-line mode) ,也是接受指令的一种模式,更偏向于文件的管理。
之所以叫 “末行” 是因为指令的输入在窗口最下方:

Vim是Vi的增强版,因此Vim完全可以兼容Vi的操作,不过它的模式名可能略有差别:
正常模式 (Normal mode) ,是启动Vim后的默认模式。
插入模式 (Insert mode) ,这个和Vi的一致。
命令(行)模式 (Command-line mode),类似Vi的末行模式。
可视模式 (Visual mode) ,和普通模式类似,不同的是可以高光选择文字的一部分进行命令操作。
包括上述几种模式,Vim总共有12种模式(这个说法来自Wikipedia)。但最常用的还是上面这四种,一般情况下够用了。

就Vi的三种模式而言,要在其间互相切换,需要借默认模式——命令模式作为一个中转(对于Vim来说就是正常模式)。
命令模式
作为进入Vi的默认模式,我们可以把这儿当作一个小家,
无论在哪个模式下按Esc(可以记成Escape [v.]逃离。逃回家了)都可以回到命令模式。(家是永远的港湾啊)

再提一嘴,在Vim里这个模式称为正常模式。
插入模式
通过以下按键指令能进入插入模式。这些按键通常是大小写成对的,小写对应的指令略显 “温和” ,大写对应的就比较 “走极端” 了。

i / I 插入 (记作Insert [v]插入;嵌入;(在文章中)添加 )
小写的i,就是在光标所在位置进入插入模式:

大写的I则有点“极端”,会跳转到当前光标所在行的开头进入插入模式:

a / A 附加 (记作Append [v]增补,追加)
小写的a,就是在光标所在位置之后一位进入插入模式,即所谓的“附加”:

显而易见,大写的A就会跳转到当前光标所在行的末尾进入插入模式(在尾部附加):

o / O 新增一行 (暂且记作Open new line)
都是新增行,差别无非是往下新增还是往上新增了。
小写的o,就是在光标所在行的之后新建一行,然后进入插入模式:

大写的O呢,就比较蛮横,会把光标所在行及其以下的行整体往下顶,在原位置新建一行(相对来说就是在光标所在行上面新建一行),然后进入插入模式:

s / S 剪切(取代) (可以直接记删的拼音Shan,也可以记成Substitute [v]取代)
小写的s,会剪切光标所在位置右边的字符,然后进入插入模式:

大写的S就很暴躁了,会剪切光标所在行,然后进入插入模式:

记
Substitute的话,可以理解为这个操作是剪切原有字符/行后进行插入,取代原有的字符/行。
💡 S 和 cc 指令的功能是相同的!这个在下面的“剪切一行”这一节中也提到了。
上面这些按键指令都是在命令模式/正常模式下输入的,通过在指令前附加数字,能重复这个指令的操作:
3s -> 删除光标后3个字符

4a -> 在光标下一位附加内容,操作结束后(Esc)把这个内容重复4次

替换模式
这个模式和插入模式一样是用于编辑文本的,不同的是替换模式下输入所至之处会覆盖一切。
记作 Replace [v]取代;代替。
输入大写R进入替换模式:

有了大写R,那么小写r呢?小写r的功能是将光标后面的字符替换为指定字符。比如我们先输入r,再输入a:

当然,这些操作一样是可以重复进行的:

(指令分别是3rp,3R->hello->Esc)
末行模式(Vim中的命令行模式)
输入 Shift+: 进入末行模式(事实上我们一般都是这样打出冒号来的)
末行模式在Vim中被称为命令(行)模式。
可视模式
可视模式是Vim中新引入的模式,支持在高光选择下进行指令操作。
可视模式即 Visual Mode,进入可视模式的按键则是v / V,
其中 v 键能普通地进入可视模式:

而 V 在进入可视模式后会保持高光选择到光标所在行(可视行模式):

还有一种便是使用 Ctrl+V/v 进入可视模式了,这种模式叫可视块模式,如字面所言,是选择“一块文字”:

这是进入Vi/Vim后所处的默认模式,和文本处理是靠的非常近的,我决定先记一下这部分。
基本光标移动(上下左右)
在交互式字符界面下没有能动的鼠标,只有闪烁的光标来标注位置。要编辑文本,真得先掌握移动光标的指令。
PS: 下方涉及的光标上下移动都是针对换行符n分隔的行的,光标会从一行移动到上方或下方的行:

👆 j、k、↓、↑将光标从一行的指定位置移动到另一行的指定位置。

👆 Enter、-,将光标移动到下一行或上一行的行首!
“经典”款
h 向左移动光标
l 向右移动光标
k 向上移动光标
j 向下移动光标
之所以这样设计,因为在早期终端机上,键盘是这个样的:

记忆方法:现代键盘上↓、↑键在中间,而←、→键在两侧,而HJKL也正是两侧的K、L代表横向移动,中间的J、K代表纵向移动(HJKL四个键连在一起的设计一直保留到了今天)。

现代款
← 向左移动光标
→ 向右移动光标
↑ 向上移动光标
↓ 向下移动光标
“隐藏”款
Backspace(退格键) 向左移动光标
Space(空格键) 向右移动光标
👆记忆方法:在插入模式下,
Backspace和Space是分别用于删去字符和增加空格的,与之对应的光标分别是往左移动和往右移动的。
Enter 把光标移动到下一行行首
+ 把光标移动到下一行行首
- 把光标移动到上一行行首
👆记忆方法:插入模式下
Enter起到了换行作用,换行后光标会处于行首。
-就记成“减去一行”(老实说我也不太知道这个怎么记了,JK其实也够用了),
+和-记在一起就行。
PS: 光标移动是在命令模式/正常模式下的指令,当然是支持重复的啦!
在上面所有指令的前面加上重复的次数(数字)即可,比如我要往下翻233行: 233j或233↓。
行内光标移动
这里记录一下在一行文本中光标的移动指令:
屏幕可见行中移动光标
上面所说的“行”取决于换行符n的位置,但这里的屏幕可见行是取决于窗口大小的:

(图中标注的即为一个屏幕可见行)
针对这样的一个行,也是有指令可用于行内移动的:
g^ 跳转到行首第一个不是空白符的字符处
👆记忆方法:
g^可以记成go ^,去往当前的顶头部分。
g0 跳转到行首字符处
gm 跳转到行中间的字符处
👆记忆方法:
gm可以记成go midst,去往当前的中间部分。
g$ 跳转到行尾字符处
完全是可以结合前面的0,^,$一起记的。当然,g + Home,g + End也是可以的。
再展开想想,既然g开头的指令在这里看的是屏幕可见行,我是不是可以用gj(或g↓)指令从一个屏幕中的行移动到另一个行呢?

👆这个主要是用j(↓)指令进行的光标移动

👆而这个主要是用gj(g↓)指令进行的光标移动。
利用
g+移动指令,可以实现在屏幕中的行之间移动。当然,3gj这种移动多行的指令也是支持的哦~
针对屏幕可见行,还有几个指令:
大写 H 用于将光标移动到屏幕可见的最上面一行(这个判定有点怪,可能不太准确!)
👆记忆方法:
H可以记成Head,去往可见区的头部。
大写 M 用于将光标移动到屏幕可见的最中间行(同样判定很怪)
👆记忆方法:
M可以记成Middle,去往可见区的中间部分。
大写 L 用于将光标移动到屏幕可见的最下面一行
👆记忆方法:
L可以记成Last,去往可见区的最后一行。
文件内光标移动
除了上述几种移动外,还有几个常用指令能按词、按句、按段等移动光标:
按词移动
w 跳转到下一个词的开头部分(考虑标点符号),演示如下👇

W 跳转到下一个词的开头部分(只考虑空格分隔) ,演示如下👇

b 跳转到上一个词的开头部分(考虑标点符号)。
B 跳转到上一个词的开头部分(只考虑空格分隔)。
这里的b、B和w、W的操作就是反着来的。
👆记忆方法:
w可以记成word next,下一个单词;
b则记成behind word,后一个单词。
W和B则比较“激进”,不会考虑标点符号。
e 跳转到当前光标所在词的词尾,如果光标在词尾,就跳转到下一个词的词尾 (考虑标点符号)。
E 同上👆,但是只考虑空格分隔。
👆记忆方法:
e可以记成end of word,词尾。
按句移动
( 移动到光标所在句子的开头部分,如果已经在开头,就移动到上一个句子的开头部分
) 移动到下一个句子的开头部分
👆记忆方法:写代码的时候会用半角括号
()把表达式括起来,可以理解为“一句”。
按段落移动
{ 移动到光标所在段落的开头部分,如果已经在开头,就移动到上一个段落的开头部分
} 移动到下一个段落的开头部分
👆记忆方法:写代码的时候会用半角大括号
{}把代码块括起来,也就成为了一“段”代码,可以理解为一个段落。
值得注意的是,上面这些按词、句、段落移动光标的操作,主要是针对英文句式的。如果操作对象是中文的话,可能并不能如愿。
(最明显的就是按词移动 W,中文里一般是不会用空格分隔每个词的,所以会直接跳转到一段的末尾!)
自定义跳转标记
在这一坨文本中进行移动时,我经常要使用指令反复横跳。有一个地方我经常跳转过去,如果一步步来实在很麻烦!此时就可以用自定义跳转标记来解决。
设置标记
指令非常简单:
ma
这样就设置了一个名为a的标记!
经过测试发现 m 后面可以接 a-zA-Z0-9 之中的任意字符!
也就是说
m1,mA,mO,mz这些都是合法的指令!
👆记忆方法:
m可以直接记忆为Mark [v]标示,标记。
跳转到标记处
`a
这样就能跳转到a标记处~
标记名是大小写敏感的。
💡 可以和下面的
y,c,d指令联合使用。
跳转到标记所在行的行首
'a
使用单引号 ' 加标记名能跳转到该标记所在行的行首第一个不为空白符的字符处!

这样是不是就相当于标记了一个行的行首呢?
删除标记
ma可以设置名为a的标记,删除的话其实在前面加个del即可:
delm a 或 delmarks a
( Delete Marks )
同时支持删除多个标记。比如我要删除a,b,c三个标记,可以写成:
delm abc 或 delmarks a b c
也就是说删除标记的时候标记名之间有没有间隔是无所谓的。
💡 在进行了缩进操作后,标记的位置仍然在文本的相应位置,无需担心。
末行模式/命令行模式下可以查看标记信息::marks
翻页
后空翻
Ctrl + b 往回翻一整页
PageUp 往回翻一整页
Ctrl + u 往回翻半页
👆记忆方法:
b代表Backward,也就是往回;
u则代表Up,也就是往上(在界面上往回翻就如同往上翻);
PageUp不多赘述,在现在的大多系统中都是支持的。
前空翻
Ctrl + f 往前翻一整页
PageDown 往前翻一整页
Ctrl + d 往前翻半页
👆记忆方法:
f代表Forward,也就是往前;
d则代表Down,也就是往下(在界面上往前翻就如同往下翻)。
Ctrl键代表Control,在这里可以理解为控制页面滚动。
PS: 再记忆一下,命令模式/正常模式下的指令是可以重复的。在这里如果我要向前翻3整页:3PageDown或3Ctrl+f。
复制
复制是文本的经典便捷操作之一。
复制一行
yy 复制光标所在行(到寄存器中)
Y 同上
👆记忆方法:
yy中的y代表yank [v]猛拉;猛拽,复制其实就是把对应的文本给“拽走”了。
多行复制
2yy 复制光标所在行以及下一行,共2行
2Y 同上
10yy 复制光标所在行以及下面9行,共10行
本质上其实就是
命令模式/正常模式下的指令重复(在指令前面加数字前缀)。
打一套复制组合拳!(选区复制)
y其实可以和很多光标移动指令结合使用!利用好这套“组合拳”,效率倍增有木有!

比如 ye 就能复制光标所在位置到单词尾部的部分,
而 y^ 能复制光标所在位置到行首第一个非空白符的单词部分,
再者 y↓ 或 yj 能实现同2yy一样的功能:复制光标所在行以及下一行,
甚至 y( 都是可以的,复制光标所在位置到光标所在句开头。
也就是说
y{,y},yk,yl,y$,yg^,y`a等等其实都是可以的!
另外还可以复制多行,比如3ygj。
🤩 总结一下就是:
y 可以和很多光标移动指令结合使用达成部分复制的效果,
通常以光标所在位置为起点,直到光标移动指令执行后的光标位置为止作为选区进行复制。

👆示例ygj的效果。光标所在位置是语字前面,执行gj后光标位于草字前面,所以复制选区就是:
语人曰:“此当有大手笔事。”俄而帝崩,哀册说议皆淘所。
剪切
有了复制,就不得不提到复制并移除——剪切了。通过这些指令移除的文本会被存进寄存器,可用于粘贴。
剪切一行
dd 剪切光标所在行
cc 剪切光标所在行,并且进入插入模式
S 同上👆,这个其实在模式切换这一节里面写了
👆记忆方法:
dd中的d记为delete [v]删除;
cc中的c记为change[v]改变,亦可记成Chi 吃;
S可以记成Shift [v]转移,剪切就像是在转移文本的位置,亦可记成Shan 删。
剪切多行
这一部分和复制多行真的相差无几,可以说会用多行复制了,肯定是会剪切多行的。
2dd 剪切光标所在行和下面一行,共2行
6dd 剪切光标所在行及下面5行,共6行
3cc 剪切光标所在行及下面2行,并进入插入模式
3S 同上👆
剪切一个字符
x 剪切光标右边的一个字符 (相当于dl或d→)
s 剪切光标右边的一个字符,并且进入插入模式
👆记忆方法:
x中的x记为画个叉叉删掉字符;
s则可以记为shan 删。
行内剪切
和复制不同的是,大写 D 和 C 的作用并不等同于 dd 和 cc!
D 剪切从光标所在位置到光标所在行的行末部分
C 完成上述操作👆,并且进入插入模式
C相对D仍然是多出了个进入插入模式的功能
组合剪切(选区剪切)
复制y能打组合拳,我堂堂剪切为什么不行!
确实,剪切 d 或 c 都是可以和光标操作指令进行结合的,
他俩不同的地方在于 d 只是普通的剪切,c 还附赠了切换到插入模式服务!

dj 或 d↓ 的效果就等同于2dd
cj 或 c↓ 的效果就等同于2cc,会进入插入模式
5x 能剪切光标右边的5个字符
5s 能剪切光标右边5个字符且进入插入模式
3cgj 能剪切从当前光标到3个屏幕可见行后的相应位置部分
dw 剪切从当前光标位置到下一个单词前的部分
cw 在进行上面的操作👆后,会进入插入模式
d^ 剪切从当前光标位置到本行头部一个不是空白符的字符的部分
c^ 在进行上面的操作👆后,会进入插入模式
d`a 剪切从当前光标位置到a标记位置的部分

👆演示了一下 3dgj 。从光标所在位置 语 开始,到执行了3gj之后的光标位置为止作为选区,进行剪切。也就是这一部分:
语人曰:“此当有大手 笔事。”俄而帝崩,哀册说议皆淘所草。释义用来赞誉写作才 能极高,并用来称颂着名的作家和作品、故事东晋的文士王玖 从小才思敏捷,胆量很大,散文和诗
🤩 总结一下就是:
d / c 可以和很多光标移动指令结合使用达成部分剪切的效果,
通常以光标所在位置为起点,直到光标移动指令执行后的光标位置为止作为选区进行剪切。
💡 应该和复制结合记忆!
粘贴
Copy和Cut都记了,接下来咱要想办法粘贴(Paste)内容咯~
粘贴的指令其实非常简单!( ´・ω・)ノ
p (向下一行/向右)粘贴文本一次(记为 Paste [v]粘贴)
大写 P (向上一行/向左)粘贴文本一次(记为 Upper(大写,同时也有“上边”的意思) Paste)
当寄存器中的内容是一行或多行文本(开头有换行符n)时:
p会把文本粘贴到当前光标所在行的下一行,
大写 P 会把文本粘贴到当前光标所在行的上一行!
👇举例:先利用yy复制一行文本,然后分别用 p 和 P 进行粘贴

很明显能看到使用
p时,该行文本会被粘贴到line2这一行的下方;
而使用P时,该行文本会被粘贴到line2这一行的上方。
当寄存器中的内容是不足一行的文本(开头没有换行符n)时:
p会把文本粘贴到当前光标所指字符的右边,
大写 P 会把文本粘贴到当前光标所指字符的左边!
👇举例:先执行ygj复制开头没有换行符的一段文本,然后分别用 p 和 P 进行粘贴

粘贴的时候光标指向的字符是
n。使用p粘贴时,内容粘贴在n的右方;而使用P粘贴时,内容粘贴在n的左方。
🌟 命令模式/正常模式下指令可加数字前缀重复执行,这里也是一样的。比如 4p 就是把寄存器的文本在光标右边/下一行粘贴四次。
Vim上下文编辑
Vim在复制y,剪切c、d这些操作的基础上复用了i和a指令用于对上下文进行操作:
👆记忆方法:
i记作inner 内部的,在下面使用这个指令,操作不会考虑周围的空格或符号;
a记作around 周围,这个则会考虑周围的空格或符号。具体视情况而定。
针对单词(w / W)
⭐ 记作
Word
diw 剪切光标所在的单词(不考虑标点符号)
diW 剪切光标所在的单词(考虑周围的标点符号,也就是说剪切的时候会把周围的标点符号一并算进去)
i(inner) 在这里的意思是操作的部分不包括单词周围的空格
daw 和 daW 也是剪切光标所在的单词(w和W区别不再多赘述)
a(around) 在这里的意思是操作的部分包括单词周围的空格
※ 下面演示一下diw和daw的区别:

很明显能看到,使用diw时,剪切后遗留的文本是 of ,(单词左边的空格保留了);
使用daw时,剪切后遗留的文本是 of,(单词左边的空格被一同剪切了) 。
💡 像
y,c,d一类指令都是可以这样用的,比如yaw,yiW,ciw,caW等等...
针对句子(s)
⭐ 记作
Sentence。注意,这里没有大写S的用法!
其上下文操作和上面单词的是一样的!比如:
yis 复制光标所在的句子(i不考虑周围的空格)
yas 复制光标所在的句子(a考虑周围的空格)
cas 剪切光标所在的句子,并进入插入模式
dis 剪切光标所在的句子
...and so on
💡 注意,这里只有小写
s的用法。
针对段落(p)
⭐ 记作
Paragraph。注意,这里没有大写P的用法!
这里的用法也和上面是一样的:
cap 剪切光标所在的段落,并进入插入模式(a考虑周围的空格)
dip 剪切光标所在的段落(i不考虑周围的空格)
yip 复制光标所在的段落
...and so on
💡 这里同样只有小写
p的用法。
针对单/双引号( ' / " )
很明显,这一部分操作是用来处理单/双引号引起来的文本的,和上面的w、W、s、p不同的是这里的 i / a 的含义:
i(inner) 在这里的意思是操作的部分不包括两侧的引号
a(around) 在这里的意思是操作的部分包括两侧的引号
di" 剪切光标所在的双引号引起来的同行文本(i代表不包括两侧的引号) 👇

da' 剪切光标所在的单引号引起来的同行文本(a代表包括两侧的引号) 👇

💡
y和c也是一样的用法
针对括号 ( ( ), < >, [ ], { } )
这一部分用来处理括号括起来的文本。
📌 注意,这里的操作是支持多行的!!
i(inner) 在这里的意思是操作的部分不包括两侧的括号
a(around) 在这里的意思是操作的部分包括两侧的括号
di( 或 di) 剪切光标所在的小括号( )括起来的文本(i代表不包括两侧的括号) 👇

da{ 或 da} 剪切光标所在的大括号{ }括起来的文本(a代表包括两侧的括号) 👇

💡
y和c也是一样的用法,适用于多种括号。

双引号指定寄存器
默认情况下使用复制粘贴似乎用的都是一个公共的寄存器(Registers),Vim实际上是支持寄存多条文本的,只需要在指令前指定寄存器名即可。
默认的寄存器被系统记为 " ,一个双引号
当执行yy时,会将目标文本行存入 " 寄存器,操作等同于:
""yy
💡 其中第一个单引号代表引用寄存器,第二个单引号便是默认寄存器的名字。
于是粘贴指令也可以改写成:""p,即从默认寄存器中取出文本进行粘贴。
指定寄存器名字
把上述第二个单引号改成 a-z 的小写字母即可!这样就相当于得到了26个“剪贴板”呢~
比如将当前行复制到a寄存器中:
"ayy
那么粘贴该内容的指令也显而易见了:
"ap
往已命名寄存器中附加内容
上面写到用小写字母对寄存器进行命名,那么大写字母呢?
大写字母用于往寄存器中附加内容:
"Ay$ 将光标位置到行尾的文本附加到a寄存器中 👇

这里主要记录一下复制粘贴方面的寄存器引用方法。Vim寄存器储存的内容远不止这么一点,还包括操作历史记录等信息,就不多赘述了。
👆记忆方法:
"就像个图钉📌,标明是哪一个寄存器。
末行模式/命令行模式下可以查看寄存器信息::reg[isters]
缩进
Vi/Vim 常用于编辑代码,缩进功能当然也是比较常用的了。
👆记忆方法:
>可以看作在往右边“顶”文本,而<可以看作在往左边“拉”文本。
撤销与重做
文本编辑必不可少的便是撤销与重做功能了!
数字快捷增减
要修改一个数值的时候可以进入插入模式,移动光标到数值上进行修改,这样可能略嫌麻烦了。在命令模式/普通模式下提供了对数值进行递增或递减的操作指令:
Ctrl + a (Ctrl + A亦可)
对光标指针所在数值进行递增操作。如果光标没有指向数值,会向右查找数值进行递增。
💡 记忆方法:
a可以记成ascend [v]上升;升高;递增,
亦可以记成accumulate [v]积累;逐渐增加。
之所以有Ctrl,是因为a已经被用于切换插入模式了!

👆 示例中可以看到,当光标右方没有数值时会屏幕闪烁提示指令无效。
Ctrl + x (Ctrl + X亦可)
对光标指针所在数值进行递减操作。如果光标没有指向数值,同样会向右查找数值进行递减。
💡 记忆方法:
x可以记成叉掉数字,从而让数值减小。
之所以有Ctrl也是因为x已经被用于剪切字符。
重复指令
设想一下,如果我要增加10亿,难不成要一直按住按键不动?当然不是这样啦!
再记忆一遍,命令模式/普通模式下的指令可以加数字前缀进行重复,在这里只需要这样输入指令即可快速递增指定数值:
1000000000Ctrl + a

Ctrl + x 同理!

进制的表示方法
0x 前缀表示十六进制 (记hex的x)
0 前缀表示八进制 (记octal的o,虽然这里是0)
0b 或 0B 前缀表示 二进制
💡 Vim支持进行增减的类型有:二进制数(binary),八进制数(octal),十进制数(decimal,默认支持),十六进制数(hexadecimal),无符号数(unsigned),字母(alphabetical)。
👆 具体配置见末行模式/命令(行)模式的nrformats配置一节。
字母大小写切换
数字增减有了,字母其实也是可以“随地大小变”的!
gu后接光标移动指令
将从光标位置开始到执行移动指令后光标的位置结束作为选区,
将选区内的所有字母转换为小写!

👆 演示了guiw指令。iw选择的是光标所在单词FLOOD,执行gu后将单词所有字母转换为小写:flood。
gU后接光标移动指令
将选区内的所有字母转换为大写
💡 记忆方法:
gu和gU中一个是小写u,一个是大写U。大写U可以记成UPPERCASE 大写,而当U小写成为u,对应的便是小写了。g仍然可以记成go,go UPPERCASE。
~ (波浪符)
波浪符将光标所在的字母进行大小写交换,比如b会转换为B;B则会转换为b。
这个指令可以用于转换光标所在行中自光标往后的字母大小写。

👆 50~,将本行中自光标往后50个字符进行大小写转换。
g~后接光标移动指令
上面的~只能对光标所在行的字符进行大小写交换,如果要涉及一个选区呢?
于是,g~他来了!在g~后接光标移动指令,将光标当前位置开始到执行指令后光标所在位置为止之间的字符进行大小写交换。

👆 g~2gj,将从光标位置开始到光标向下移动两个屏幕行为止的部分字符大小写交换。
💡 记忆方法:波浪线可以看作是一条波,随着波的传播,同一个地方的波峰波谷交替出现,在这里就记成大写小写的交替。

重复最后一次更改
.
↑ 没错,指令就是一个句点!
比如,之前我输入了3dd剪切了三行文本,之后再按一次 . 会再往下剪切三行;
又比如,我先输入了x剪切光标所指的字符,之后输入 3. 就会再剪切三个字符。
但是!,如果我先输入3j让光标往下移动3行,再按 . 是没有效果的,因为j指令仅仅是移动了光标,没有对文本造成任何更改!
⚠ 注意!
.重复的是最后一次更改,而不是最后一次指令!
👆 记忆方法:硬背
行连接指令
在这个模式中涉及到了最基础的文件保存 / 退出编辑器等操作。
:w 写入 (记为Write [v]写文件)
顾名思义,这个指令会将自开始编辑以来的更改写入文件中:


文件保存总让人想起桌面软件的“另存为”功能,当然Vi也是能实现的:
⭐ :w filename


除了上面针对单一文件的写入指令,还可以同时写入多个文件,下面这个指令适用于Vim编辑器中同时打开多个文件的情况:
⭐ :wa
👆 将所有打开的文件进行写入保存。(可以记成Write All 写入所有)

如果只想保存文本中的一部分呢?Vim还支持指定行写入文件:
⭐ :<line1>,<line2> w filename
👇 比如我想写入第13至第14行的文本(共两行)::13,14 w filename

输出文件效果:

:q 退出(记为Quit [v]离开;关闭)
文本编辑器必备操作——关闭文件。
⭐ :q 指令会退出当前编辑的文件(如果只打开了一个文件,这个操作同时会退出编辑器)
⭐ :qa 指令则会退出所有打开的文件,并且退出编辑器。
👆 记忆方法:
Quit All 关闭所有
注意:上面这两个指令执行的前提是当前文件自打开后没有任何更改,也就是说文件缓冲区中没有任何新增内容,不然编辑器会警告文件尚未保存更改。
怎么强制退出呢?下面就将写到.....
联合使用 w 与 q

既然未保存没法正常关闭文件,那么保存后关闭不就行了嘛!
⭐ :wq
👆 双剑合璧,数据无忧!这个指令会将缓冲区中的编辑内容写入文件,并且关闭文件。(针对当前正在编辑的文件)
当然,也有针对编辑器所有打开的文件的操作:
⭐ :wqa
👆 写入并关闭所有打开的文件,这之后会退出编辑器。(Write & Quit All)
感叹号 ! 强制执行指令
有些情况下末行模式指令的执行可能会被拒绝,比如上面的:q在文件保存前不能直接执行。(这一点其实也是为了数据安全)
⭐ 然而,只需要在这些指令末尾加上感叹号 ! 就可以强制执行了!
比如未保存强制退出编辑器就可以用: :qa!

此外我觉得文件强制写入也是值得记录一下的::w!
👆 这个指令会尝试强制写入文件,即使文件是只读(Read-only)的,前提是用户得要有修改文件属性的权限。
⚠ 强制执行指令时一定要再三考虑,防止数据丢失等问题。
:r 在下一行读入文件 (记成 Read [v]读取)
在编辑文本的时候可能需要将另一个文件的文本插入进来,这个时候就轮到:r大显身手了。
⭐ :r 能够读取一个文件,并将文件内容追加到光标所在行的后一行。

👆 左边是待读取的文件,右边是正在编辑的文件。利用:r指令,成功将文件内容插入到光标后一行。
:e 开启新buffer或刷新当前buffer (记成 Edit [v]编辑)
当编辑文件编辑到一半,想要推翻当前的所有修改,该怎么办呢?
一种方法是使用 :q!,强制退出文件;
另一种方法则是 :e!,强制刷新当前buffer:
Vi/Vim中未经修改但是在别的软件中受到了修改时,可以直接使用 :e 在编辑器里刷新(重新载入)文件内容。文件做了修改后,也可以用 :e! 强制重载文件内容,也就是抛弃文件开启以来的所有修改。

buffer可以理解为文件的一个缓冲区,文件在编辑器中被编辑实际上也就是临时buffer在被编辑。当写入文件的时候就是把buffer中的内容写入到原文件中,以达成编辑的效果。
除了刷新当前文件的buffer外,:e指令还可以用于打开新的文件并创建buffer。
:e <filename> 打开文件(如果存在的话),创建buffer
👆 前提:目前在编辑的文件已经保存或者未经修改。
:e! <filename> 无论当前文件是否保存(写入),强制打开新文件
:buffers 和 :bn, :bp 以及 :b<id>
:buffers
列出所有buffer,在这里可以看到buffer的ID(最左边一列)。

:b<id>
既然拿到了buffer的ID,我就可以在buffer之间跳转了,比如:
:b2 跳转到id为2的buffer
注意:这个操作也要求当前编辑的文件不能有未保存的修改。
:bn 和 :bp
就算不知道buffer的ID,利用这两个指令也能在buffer间互相切换:
:bn ---> 切换到下一个buffer
:bp ---> 切换到上一个buffer
注意:这个操作也要求当前编辑的文件不能有未保存的修改。
💡 记忆方法:
bn即buffer next 下一个buffer;bp即buffer previous 上一个buffer。
:help [command] 或 :h [command] 查看帮助
就像Linux中的man指令一样,Vi/Vim的末行模式/命令行模式也有个:help指令用来查看编辑器使用帮助。
⭐ 直接使用 :help 时会打开帮助文件,并将光标移动到顶部。
⭐ 指令形如:help [command]时,在开启文件后光标会跳转到对应的指令帮助信息。
比如要看寄存器指令的帮助信息:
:help reg或:help registers(:h reg当然也行 )
:registers 或 :reg 查看寄存器
这一部分可以结合命令模式/正常模式的双引号指定寄存器一起记。
⭐ 直接使用 :registers 或 :reg 时会展示所有的寄存器 👇

也可以在后面指定要查询的寄存器名,这些名称可以用空格分开,也可以连在一起写:
⭐ 比如 :reg[isters] a b c 或 :reg[isters] abc ,可以筛选查询a,b,c寄存器 👇

关于寄存器更多用法这里就不多赘述了~

:marks 查看标记信息
这个和上面:reg[isters]的使用很类似,所以咱给写一起了。
这一部分可以结合命令模式/正常模式的自定义跳转标记一起记。
⭐ 直接使用 :marks 能查询到所有现有的标记
和:registers一样,在后接查询的标记名,能筛选结果:
⭐ 比如 :marks a b c 或 :marks abc ,可以筛选查询a,b,c标记
:set 设置项
这个指令用于设置一些配置项,这里就记几个比较常用的(因为配置项有非常多,全写出来太累赘了)
开启/关闭行号显示
:set number 或 :set nu 可以在行首显示行号
而 :set nonumber 或 :set nonu 则可以关闭行号显示
此外 :set nu! 或 :set number! 可以在开启行号/关闭行号之间互相切换。

👆 记忆方法:直接记
Number [n]号码 [v]标号
开启/关闭相对行号显示
:set relativenumber 或 :set rnu 可以显示相对行号
而 :set nonumber 或 :set nonu 则可以关闭相对行号显示
此外 :set rnu! 或 :set relativenumber! 可以在开启相对行号/关闭相对行号之间互相切换。

👆 可以看到,相对行号以光标所在行为第0行,向上或向下标出相对的行号。
这个特性可以用于辅助涉及多行的指令,比如 y3j, 3k, 2>> 等。
💡 记忆方法:按字面记,即
Relative Number 相对 号码
识别数字进制的配置
上面已经写过Ctrl+x和Ctrl+a能快捷减增数字。不过呢,Vim怎么识别增减的类型呢? 这就涉及到下面这个配置项了:
:set nrformats 或 :set nf
直接输入这个指令会查询该配置项当前的设置:

上面也写过Vim能识别的增减类型。这里的octal,hex 则代表会尝试识别八进制,十六进制以及十进制的数字。(十进制是默认支持的)
使用 += 或 -= 进行配置
这种写法其实可以理解为一些编程语言中的运算赋值操作:
:set nrformats+=bin
:set nrformats+=bin,alpha
(或者 set nf+= ... )
👆 多个可以用逗号分隔,会“附加”到配置项的末尾。
然而,移除的时候就要注意了,一定要按配置中的顺序来:
:set nrformats-=bin
:set nrformats-=bin,alpha
👆 这样写的话必须保证配置项中也有bin,alpha这个字串!

↑ 没有bin,alpha这个字串,无法移除。

↑ 移除octal,alpha的话就很成功,因为配置项中有这个字串。
直接使用 = 进行配置
同样可以按编程语言里的赋值来理解,会直接改变整个配置
:set nrformats=octal,hex
👆 多个值一样是用逗号分隔
💡 记忆方法:
nrformats可以看成number recognizing formats,也就是数字识别格式。取n和f即为nf,因此nf也可以简单记为number format!
执行Shell指令
编辑文本到一半突然想执行一个Shell指令。我之前可能会利用:wq先退出编辑器,输入执行指令后再重新打开编辑器。
实际上在末行模式/命令行模式下Vi/Vim也是能快捷切换到Shell执行指令的:
:! <command> 暂时离开编辑界面,在Shell下执行指令并打印结果

👆 临时跳转到命令行执行清屏及查询日期指令并展示结果。
💡
:!还有很多神奇的用法,比如执行指令后将返回的结果插入下一行::r! <command>;
又比如将文本21至25行进行升序排序,并替换原文本::21,25! sort,这里就很像Shell中管道符的用法了。

👆 演示 :21,25! sort
更多用法在这里就不多赘述了╮(╯3╰)╭
:sh 创建一个新的Shell会话
这个指令就比较简单粗暴了,在执行后会创建一个新的Shell会话,我可以直接在Shell中执行指令!

这个Shell是一个非登入Shell,所以需要使用 exit 指令退出。
该Shell退出后会回到编辑器界面!

👆 这个直接记忆
Shell就行
重复上一条指令
在命令模式/正常模式下我可以使用 . 句点来重复上一次更改。在末行模式/命令行模式下也有类似的方法。
经典方法
在末行模式/命令行模式下输入 : 后会进入末行模式/命令行模式,此时可以通过:
⭐ ↑, ↓, PageUp, PageDown
来浏览之前的输入历史

💡 可以在 : 后面输入一些字符以加快检索:

👆 输入:s后,能快速浏览:s开头的历史指令记录;:se则能快速浏览:se开头的历史指令。
@: 方法
在命令模式/正常模式下输入:
@:
能重新执行上一条末行模式/命令行模式指令。
👆 记忆方法:把
:末行指令给at出来!
虽然我把这个方法写在末行模式这里了,但实际上其可以算是命令模式/正常模式的指令。
因此也是可以重复执行的!

👆 演示:先用:d删除一行,然后再用3@:重复执行:d三次以继续删除下面三行。

有点累了...喝口水...
搜索
前提:在命令模式/正常模式下:
/<搜索模式> 自光标向后查找
?<搜索模式> 自光标向前查找
👆 其实
末行模式/命令行模式下也不是不行,也就多了个冒号::/<搜索模式>、:?<搜索模式>。不过接下来这两个指令就不行了 ↓
n ---> 这个指令会重复上一次搜索动作。
/....是往光标后搜索,按下n就是在光标后再次搜索?....是往光标前搜索,那么按下n就是在光标前再次搜索N ---> 这个指令和 n 的操作相反
👆 记忆方法:
n可以记成next [a]下一个的,正如o和O操作相反一样,N和n操作相反。
这里的搜索模式实际上就是正则表达式,不过和普通的正则略微有些不同!
当然是可以直接搜索字串的

大小写不敏感搜索
一般在写正则表达式时要进行大小写不敏感匹配我一般会在正则表达式末尾加上i标记,但是在搜索模式里是不行的,需要用到特殊转义标记:
c
这个标记写在搜索模式的中间(不要写在中括号[]里!)一般也是可以的,但我觉着还是放在末尾好辨别一些。


👆 记忆方法:这里的
c可以记成case [n]大小写。
利用正则表达式进行搜索
这里使用正则表达式和在编程语言里有些小区别,这里简单写一下:
采用 < 和 > 分别匹配单词词首和单词词尾,而不是b。
部分元字符要发挥元字符的作用的话,需要先转义。这里列个表格:
| 元字符 | 用途 | 在搜索模式中的用法 |
|---|---|---|
+ |
匹配1次或多次 | + |
? |
匹配0或1次(非贪婪模式在Vim中另有元字符) | ? |
{n,m} |
匹配n到m次 | {n,m} |
{n,} |
匹配n次或更多次 | {n,} |
{n} |
匹配n次 | {n} |
{,m} |
匹配0到m次 | {,m} |
< |
匹配单词首部 | < |
> |
匹配单词尾部 | > |
( |
子模式(捕获组)开始标志 | ( |
) |
子模式(捕获组)结束标志 | ) |
| |
两项之间任意匹配一个 | | |
% |
用于修饰非捕获组 | % |
@<=, @<!, @=, @! |
向后预查,向前预查 | @<=, @<!, @=, @! |
💡 这种情况是基于Vim默认设置的
magic模式的,其他模式不多赘述。
默认情况下这些元字符如果不转义,就代表匹配这个字符本身。
除此之外的元字符大多是可以直接使用的,下面是一些示例:

另外也是支持引用子模式的匹配结果(分组)的:

神奇的非贪婪模式
Vim这里的非贪婪模式用的就不是元字符 ? 了,取而代之借用了一下大括号 {...,...}
官方说明可以在
末行模式/命令行模式下输入:help non-greedy查看。
当 { 后面紧接了一个连字符(Hyphen) - 时,就相当于采用了非贪婪匹配,下面举些例子:
e{2,3} 匹配 ee 或 eee,如果有更长的eee就优先匹配(也就是贪婪匹配)
e{-2,3} 匹配 ee 或 eee,优先匹配更短的ee(也就是非贪婪匹配)

👆 这个例子中在
{后紧接-后会往少的匹配,也就是只匹配一个paprika。
如果在 - 后面没有指定匹配次数,就有点类似 * 了。形如:
{-} 匹配0-任意次,但是非贪婪匹配,匹配次数尽量少。
{-,3} 匹配0-3次,但是非贪婪匹配,匹配次数尽量少。
例:用模式
ke{-}匹配字串keep只匹配到了k,因为{-}代表匹配0-任意次,但是非贪婪匹配,所以这里e匹配了0次。
非捕获组与向前/向后预查
在编程语言使用的正则表达式中,非捕获组一般用(?:模式)来表示,这一个子模式不会参与分组。
然鹅在Vi/Vim搜索模式中,非捕获组用的是:
%(子模式) ( %(子模式) 也行)
来表示的。下面就是一个示例:

可以看到
accept并未参与分组,1引用的是第二个子模式的匹配结果after
接着,在编程语言中的向后/向前预查(?<=), (?=), (?<!), (?!) 在Vi/Vim中也是有特殊的表示方法的:
| 预查内容↓ 方向→ | 向后(左) | 向前(右) |
|---|---|---|
| 存在 | (子模式)@<= |
(子模式)@= |
| 不存在 | (子模式)@<! |
(子模式)@! |
💡 预查即预先检查子模式,看看这个子模式是否存在于待匹配的字串前/后,预查子模式是不会出现在最终的匹配结果中的。

👆 匹配
paprika,前提是paprika后面(左边)必须要匹配到paprika。

👆 匹配
paprika,前提是paprika前面(右边)必须要匹配到pepper。

👆 匹配
paprika,前提是paprika前面(右边)不能匹配到pepper。
搜索光标下的单词
这一小节的操作是在命令模式/正常模式下的:
* 往后搜索光标目前指向的单词,只匹配一整个单词
# 向前搜索光标目前指向的单词,只匹配一整个单词

从这个例子可以看到,实际上Vim是把光标指向的单词转换成了搜索语句。
*对应/<accept>,#对应?<accept>。
g* 往后搜索光标目前指向的单词,单词可作为字串的一部分被匹配。
g# 向前搜索光标目前指向的单词,单词可作为字串的一部分被匹配。

可以看到
*对应/accept,#对应?accept,单词可作为字串的一部分被匹配。
💡 因为这几个指令被转换为末行搜索操作了,所以在搜索中可以用的
n、N这一类指令也是可以用的。
开启搜索高光显示
上面的图示中搜索匹配项都会“黄的发光”,这种匹配结果高光显示是可以作为配置项使用 :set 进行设置的:
:set hlsearch 或 :set hls 开启搜索结果高光显示
:set nohlsearch 或 :set nohls 关闭搜索结果高光显示

虽然高亮显示一目了然,但是开了之后光标就不明显了,高光咱已经看够了,怎么关掉目前结果的高光展示呢?
:nohlsearch 或 :noh 关闭目前的高光显示

可以看到输入
:noh后会取消目前的高亮,但是这并不影响重新开始搜索时高亮展示匹配。
💡 记忆方法:
hlsearch即Highlight Search,Highlight [v]突出,强调,Search [n]搜索。

替换
替换的指令就是在末行模式/命令行模式下输入的了,
其完全可以结合搜索指令一起记忆:
:[作用范围] s/<搜索模式>/<替换模式>/<替换flag>
💡 很明显了,这里的
s代表的就是substitute [v]取代,替代
作用范围
这里直接列表直观展示一下:
| 作用范围 | 说明 |
|---|---|
| 不写 | 默认光标所在行 |
. |
同样代表光标所在行 |
n (整数) |
代表第n行。比如233代表第233行 |
$ |
代表最后一行 |
'a |
代表a标记所在行(关于设定跳转标记可以看自定义跳转标记) |
上述符号±相对行数 |
比如'a+2就代表a标记所在行的下面第二行;.-1 就代表光标所在行的上面一行 |
上述符号1,上述符号2 |
代表从上述符号1代表的行到上述符号2代表的行。比如.,$代表从光标所在行到最后一行。 |
% |
代表从第一行到最后一行(整个文本),和1,$相同 |
'<,'> |
在可视模式下输入 : 进入末行模式/命令行模式会自动填充,代表**可视模式下选定的范围 |

👆 示例:
.,+3代表从光标所在行到光标所在行的下面第三行作为范围进行替换
搜索模式
同上面的搜索模式。
💡 值得一提的是这里可以留空。如果留空的话默认使用上一次搜索的匹配。
比如我先搜索了
/<paprika>,然后我想替换全文的/<paprika>/为pepper,在替换时可以这样写::%s//pepper/g(省略了搜索模式)。
替换模式
替换模式当然可以是任意字符串,
同时也可以引用搜索结果中的部分:

👆 使用 1, 2 引用搜索结果中的第一个和第二个捕获组的内容。

👆 使用 & 引用搜索匹配结果

👆 很巧妙的用法,利用捕获组引用交换顺序
替换flag
一般正则表达式末尾会加上修饰符以控制匹配行为。而Vim这里的替换flag也是类似修饰符,控制替换行为的。
下面列出几个比较常用的:
| Flag | 说明 | 记忆 |
|---|---|---|
g |
全局替换(在指定的作用范围内) | global [a]全局的 |
i |
忽略大小写 | ignore-case 忽略大小写 |
I |
大小写敏感 | 和i意义相反 |
n |
并不会真正替换,而是显示匹配的数目 | numerate [v]计算 |
c |
在替换前让用户确认 | confirm [v]确认 |
💡 值得一提的是
i——忽略大小写。
之前在搜索模式中提到过在模式中加入c能忽略大小写进行搜索,而这里替换也是用的搜索模式,
因此在这里就算不用修饰符i,转而在搜索模式中加入c也是完全可以的。

👆 不使用修饰符而是在搜索模式中使用了
c

👆 使用
n修饰符,并不会实际替换文本,而是显示匹配的数量

👆 使用
c修饰符,在替换前让用户确认。
其中提示输入的 (y/n/a/q/l/^E/^Y) 中,分别代表:
| 输入指令 | 说明 | 记忆 |
|---|---|---|
y |
确认替换当前光标所在匹配 | yes [v]确认 |
n |
不替换当前匹配 | no [v]不 |
a |
确认替换所有匹配 | all [v]所有 |
q |
不进行替换并退出 | quit [v]退出 |
l |
替换当前匹配并退出 | instead [adv]顶替 |
(另还有Ctrl+E和Ctrl+Y,是光标移动指令)
除了这些,搜索替换可能还有一些其他的用法尚未提到,另外还有:g这种末行指令。这里就主要记录一下基础的用法啦~

一个文件一个文件地打开编辑老累了,幸运的是,Vim支持多窗口编辑。
打开新窗口
:new [filename]
使用:new指令能在编辑器顶部新建一个横向窗口(原本的文件窗口会下移)
如果有指定文件名,则会在新窗口打开指定文件(文件不存在就是单纯的创建buffer)

:sp[lit] [filename] (也就是:sp和:split都可以)
这个指令行为上和:new挺类似的,都会新建一个横向窗口,使原本的窗口下移。
不同的是,没有指定文件名时会在新窗口打开当前编辑的文件!

👆 split [v]划分,分割
:vs[plit] [filename]
这个指令和:sp[lit]类似,但是:vs[plit]是新建一个纵向窗口,使原本的窗口右移。
和:sp[lit]一样的是,没有指定文件名时会在新窗口打开当前编辑的文件

👆
:vsplit即vertical split [v]垂直分割
在窗口之间切换
开了这么多窗口,得想个法子来回切换哈!
调整窗口尺寸
在调整终端尺寸的时候,Vim里的这些窗口可能并不会如意调整展示尺寸。这里记录一些改变窗口尺寸的指令:
调整窗口高度
:res[ize] [±][height]
:res[ize]指令有两种调整窗口高度的方法。
第一种是设置绝对高度,比如:res 5,将窗口高度设置为5行:

第二种是设置相对高度,比如:res +5,将窗口高度增加5行:

调整窗口宽度
:vert[ical] res[ize] [±][width]
和:res[ize]类似,:vert[ical] res[ize]也是能相对/绝对地调整窗口宽度的:

💡 这个记起来可能有点反直觉,
vertical resize 垂直方向上调整,可以记作此时只有垂直高度固定的窗口,所以调整的是宽度;
而普通的resize则是调整的窗口高度,可以记作此时只有水平宽度是固定的窗口。
退出窗口
同末行模式/命令行模式下的 :q 退出
前面的笔记有几个地方涉及到了可视模式:
可视模式下的操作可谓是简化版的命令模式/正常模式了,显而易见,“简化”的就是“选择文本”的操作了。
在高光选择了文本后可以进行以下操作:
拷剪粘三件套

这里和命令模式/正常模式的不同之处就在于文本已经选择完毕,只需要输入一个指令就可以完成简单的编辑操作。
y 输入一次即可拷贝选择的文本,而Y则是拷贝所有选择文本所在的行 (Yank)
d 输入一次即可剪切选择的文本,D也是会剪切所有选择文本所在的行的。(Delete)
x 和d操作一致 (给文本画叉叉)
c 和d操作一样,不过剪切完之后会进入插入模式
p 在选择的区域中粘贴文本 ( Paste )
替换
r<字符> 将所选文本替换为<字符> ( Replace )

👆
ra将所选块文本全部替换为了a
行连接
这一部分和命令模式/正常模式下的行连接指令几乎是一样了。
J 将所选文本对应的行连接成一行,用空格间隔 ( Join )

gJ 将所选文本对应的行连接成一行,没有间隔

执行末行模式/命令行模式指令
在可视模式下选择文本后输入 : 进入末行模式,会自动填充成 :'<,'>,这代表在所选文字中进行末行语句操作。
之前的搜索替换中就有提到过:基本搜索替换->作用范围
代码缩进
在命令模式/正常模式下要对一行代码进行缩进操作,往往需要输入两次>或<
而在可视模式下只需要输入一次:

👆
>将所选文本增加一次缩进

👆
<将所选文本减少一次缩进(因为选择不完全,只减少了一部分)

👆
>将所选文本增加2次缩进
大小写转换
这一部分和命令模式/正常模式下的大小写转换指令是很类似的:
u 将所选文本转换为小写
U 将所选文本转换为大写 ( UPPERCASE )
~ 将所选文本中的大写和小写分别转换为小写和大写

👆 演示了一下使用
~进行大小写转换
可视模式还有很多用法,这里就不再写更多了(再写更多就成语法手册了喂!)
回过头来...我又写了这么长一篇笔记...稍后我会完善一下目录功能以增加阅读体验。
这篇笔记虽然写得多,但是涉及的内容实际上还是比较浅层的,毕竟这篇笔记主要目的是为了记忆常用方法。也就是说,Vi/Vim的使用技巧远不止这么一点,更多高级的操作还需日常多加使用才能更加熟练!
因个人能力极其有限,可能文章中会出现部分错误。如果有写错的地方,也请大家多指教!
最后...感谢你读到这里,再会~

评论已关闭。