The missing semester of your CS education--编辑器Vim

本文介绍了Vim编辑器的基础操作,包括不同模式的切换,文本编辑,命令行模式的使用,以及如何通过Vimrc进行个性化设置。还探讨了Vim的高级特性,如宏的录制和使用,以及搜索和替换功能。强调了通过持续实践和自定义,提高Vim使用效率的重要性。
摘要由CSDN通过智能技术生成

课程结构

01.课程概览与 shell
02.Shell 工具和脚本
03.编辑器 (Vim)
04.数据整理
05.命令行环境
06.版本控制(Git)
07.调试及性能分析
08.元编程
09.安全和密码学
10.大杂烩
11.提问&回答

本文档修改自这里,补充了一些视频中展示但配套文档中未提供的代码,以及一些注释,另外,本节中涉及的相关文件可在百度云链接中获取。

强烈建议:本节内容是对编辑器Vim的一个简略入门介绍,更加先直接完成本节的第一道习题,即Vim自带的vimtutor教程

~$ vimtutor
# 终端的“终端(T)”选项,可以设置窗口为80x24,vimtutor在该窗口下效果最好

代码编辑器


写作和写代码其实是两项非常不同的活动。当我们编程的时候,会经常在文件间进行切换、阅读、浏览和修改代码,而不是连续编写一大段的文字。因此代码编辑器和文本编辑器是很不同的两种工具(例如微软的 Word 与 Visual Studio Code)。

通常学习使用一个新的编辑器包含以下步骤:

  • 阅读教程(比如这节课以及我们为您提供的资源)
  • 坚持使用它来完成你所有的编辑工作(即使一开始这会让你的工作效率降低)
  • 随时查阅:如果某个操作看起来像是有更方便的实现方法,一般情况下真的会有

学习一个复杂的代码编辑器的过程一般是这样的:

  • 头两个小时,您会学习到编辑器的基本操作,例如打开和编辑文件、保存与退出、浏览缓冲区。
  • 当学习时间累计达到20个小时之后,您使用新编辑器的效率应该已经和使用老编辑器一样快。
  • 在此之后,其益处开始显现:有了足够的知识和肌肉记忆后,使用新编辑器将大大节省你的时间。而现代文本编辑器都是些复杂且强大的工具,永远有新东西可学:学的越多,效率越高。

编辑器选择


程序员们对自己正在使用的文本编辑器通常有着 非常强的执念

现在最流行的编辑器是什么?Stack Overflow 的调查(这个调查可能并不如我们想象的那样客观,因为 Stack Overflow 的用户并不能代表所有程序员)显示,Visual Studio Code 是目前最流行的代码编辑器。而 Vim 则是最流行的基于命令行的编辑器。

Vim 有很多聪明的设计思想,所以很多其他工具也支持 Vim 模式(比如,140 万人安装了 Vim emulation for VS code)。

编辑模式


Vim 的设计以大多数时间都花在阅读、浏览和进行少量编辑改动为基础,因此它具有多种操作模式:

  • 正常模式按下<ESC>键):在文件中四处移动光标进行修改
  • 插入模式正常模式下按下i):插入文本
  • 替换模式正常模式下按下R):替换文本
  • 可视化模式正常模式下输入v、V、ctrl+v):分别表示可选中字符、行、文本块
  • 命令模式正常模式下输入:):用于执行命令,若输入的是:!command,还可以执行外部命令

在不同的操作模式下,键盘敲击的含义也不同。比如,x 在插入模式会插入字母 x,但是在正常模式 会删除当前光标所在的字母,在可视模式下则会删除选中文本块。

在默认设置下,Vim 会在左下角显示当前的模式。Vim 启动时的默认模式是正常模式。你可以按下 <ESC>(退出键)从任何其他模式返回正常模式。

因为你会在使用 Vim 时大量使用 <ESC> 键,所以可以考虑把大小写锁定键重定义成 <ESC> 键(MacOS 教程)。

Ubuntu系统可以参考这里,使用cap2esc将Cap_Lock键映射为Esc和Ctrl键

基本操作

插入文本


在正常模式,键入

  • i: 在光标所在位置之前,进入插入模式
  • 小写的o: 可以在光标下方打开新的一行并进入插入模式
  • 大写的O: 可以在光标上方打开新的一行
  • 小写的a: 可以在光标所在位置之后插入文本
  • 大写的A: 可以在光标所在行的行末之后插入文本
  • 大写的R: 可以在光标所在位置开始,进入替换模式,逐一将光标后的原内容替换

键入Esc键后,可重新回到正常模式

缓存, 标签页, 窗口


当你在终端执行vim filename命令时,会默认打开一个标签页,这个标签页包含一个窗口。每个窗口显示一个缓存,即你将要编辑的文件内容。
缓存和窗口不是一一对应的关系。

  • 一个缓存可以在多个窗口打开
    • 例如,你可以打开多个终端窗口,都执行vim filename,打开同一个文件。
  • 也可以在同一个标签页内的多个窗口打开,即多窗口模式
    • 正常模式下,输入:sp:vsp,会在当前标签页切分为上下两个窗口

多窗口模式的一种实用情形——多窗口间跳转

  • 输入:sp打开多个窗口后,输入两次Ctrl+w,可以将光标从一个窗口跳转到另一个窗口(光标所在窗口是你当前能进行编辑的)
  • 或者,输入Ctrl+w后,再按下jk,分别能跳转到下一个或上一个窗口

多窗口模式下,输入:e {{path/to/file}},可以在该窗口打开另一个文件,在该文件选中内容,复制之后,利用窗口跳转到原文档所在窗口,进行粘贴。这样,就实现了跨文件的复制粘贴

命令行模式


在正常模式下键入 : 进入命令行模式。 在键入 : 后,你的光标会立即跳到屏幕下方的命令行。 这个模式有很多功能,包括打开,保存,关闭文件,以及 退出 Vim

  • :q 退出(关闭窗口)
  • :q! 强制退出(关闭窗口,且不保存已编辑的内容)
  • :w 保存(写)
  • :wq 保存然后退出
  • :exit 退出
  • :e {{path/to/file}} 打开要编辑的文件
  • :ls 显示打开的缓存
  • :help {标题} 打开帮助文档
    • :help :w 打开 :w 命令的帮助文档
    • :help w 打开 w 移动的帮助文档
  • :set xxx 可以设置 xxx 选项。一些有用的选项如下:
    • ‘ic’或’ignorecase’——查找时忽略字母大小写
    • ‘is’或’incsearch’——查找短语时显示部分匹配
    • ‘hls’或’hlsearch’——高亮显示所有的匹配短语

Vim 的接口其实是一种编程语言


Vim 最重要的设计思想是 Vim 的界面本身是一个程序语言。键入操作(以及他们的助记名) 本身是命令,这些命令可以组合使用。这使得移动和编辑更加高效,特别是一旦形成肌肉记忆。

移动


多数时候你会在正常模式下,使用移动命令在缓存中导航。在 Vim 里面移动也被称为 “名词”, 因为它们指向文字块。

  • 基本移动: hjkl (左, 下, 上, 右)
  • 词: w (下一个词word), b (词初beginning), e (词尾ending)
  • 行: 0 (数字0,行初), ^ (第一个非空格字符), $ (行尾)
  • 屏幕: H (屏幕首行High), M (屏幕中间Middle), L (屏幕底部Low)
  • 翻页: Ctrl-u (上翻up), Ctrl-d (下翻down)
  • 文件: gg (文件头), G (文件尾)
  • 行数: :{行数}<Enter> 或者 {行数}G ({行数}为行数)
  • 杂项: % (找到配对,比如括号或者 /* */ 之类的注释对)
    • 如果光标当前位置是符号(){}[],按下%会将光标移动到配对的符号上
    • 例如,光标置于(,按下%,光标会跳转到配对的)
  • 查找(单个字符): f{字符}t{字符}F{字符}T{字符}
    • 查找/到 向前/向后 在本行的{字符}
    • ,; 用于导航匹配
  • 搜索: /{正则表达式}(按Enter键跳转到匹配结果,使用n / N 可在匹配结果间导航)

编辑


所有你需要用鼠标做的事, 你现在都可以用键盘:采用编辑命令+移动命令的组合来完成。 这就是 Vim 的界面开始看起来像一个程序语言的时候。Vim 的编辑命令也被称为 “动词”, 因为动词可以施动于名词

  • d{移动命令} 删除 {移动命令}
    • 例如,dw 删除词, d$ 删除到行尾, d0 删除到行头。
  • c{移动命令} 改变 {移动命令}
    • 例如,cw 改变词
    • 等价于: d{移动命令}i
  • x 删除字符(等同于 dl
  • s 替换字符(等同于 xi
  • 可视化模式(v/V/ctrl+v) + 选中文字 + 操作(d/c/x/s)
    • 选中文字, 再键入上面的编辑操作,可删除或替换选中的文字
  • u 撤销, <Ctrl> + r 重做
  • y 复制 / “yank” (其他一些命令比如 d 也会复制,d的功能类似于“剪切”)
    • 使用可视化模式选中文本后,再使用y进行复制,
    • 或者直接键入y,再键入<Enter>,会复制光标所在的
  • p 粘贴y复制或者d删除的内容
  • ~ 改变字符的大小写
    • 使用可视化模式,选中内容后,按下~,可改变选中的内容的大小写

计数


你可以用一个计数来结合“名词”和“动词”,这会执行指定操作若干次。

  • 3w 向前移动三个词
  • 5j 向下移动5行
  • 7dw 删除7个词

修饰语


你可以用修饰语改变“名词”的意义。修饰语有 i,表示“内部”或者“在内“,和 a, 表示”周围“。

  • ci(
    • 在光标位置往前查找左括号(,同时往后查找与之配对的右括号)
    • 然后将这对括号()之间的内容删除,并在左括号与右括号之间进入插入模式
    • (可以替换为成对的其他[ { " '
  • da'
    • 删除一个单引号字符串, 包括周围的单引号,具体作用方式同上面的ci(
    • '同样可以替换为成对的其他[ { " (

例子演示


这里是一个有问题的 fizz buzz 实现:

# codes of fizzbuzz.py
def fizz_buzz(limit):
    for i in range(limit):
        if i % 3 == 0:
            print('fizz')
        if i % 5 == 0:
            print('fizz')
        if i % 3 and i % 5:
            print(i)

def main():
    fizz_buzz(10)

我们会修复以下问题:

  • 主函数没有被调用
  • for循环从 0 而不是 1 开始
  • 在 15 的整数倍的时候在不用行打印 “fizz” 和 “buzz”
  • 在 5 的整数倍的时候打印 “buzz”
  • 采用硬编码的参数 10 而不是从命令控制行读取参数

修复步骤:

  • 主函数没有被调用
    • G 文件尾
    • o 向下插入一个新行
    • 输入
      if __name__=='__main__':
              main()
      
  • for循环从 0 而不是 1 开始
    • 搜索 /range
    • ww 向前移动两个词
    • i 插入文字: 1,
    • ea 在 limit 后插入: +1
  • 在 15 的整数倍的时候在不用行打印 “fizz” 和 “buzz”
    • jj$i 插入文字到行尾
    • 加入 ,end=''
    • jj. : 可以直接重复输入,end=''
  • 在 5 的整数倍的时候打印 “buzz”
    • ci' 输入 buzz
  • 从命令控制行读取参数
    • ggO 向上打开
    • “import sys”
    • /10
    • ci( ,然后输入int(sys.argv[1])

修改后的版本:

import sys
def fizz_buzz(limit):
    for i in range(1,limit+1):
        if i % 3 == 0:
            print('fizz',end='')
        if i % 5 == 0:
            print('buzz',end='')
        if i % 3 and i % 5:
            print(i)

def main():
    fizz_buzz(int(sys.argv[1]))

if __name__=='__main__':
        main()

Vim高级操作

自定义 Vim


Vim 由一个位于 ~/.vimrc 的文本配置文件(包含 Vim 脚本命令)。

我们提供一个文档详细的基本设置,你可以用它当作你的初始设置。我们推荐使用这个设置因为 它修复了一些 Vim 默认设置奇怪行为。 这儿下载我们的设置,然后将它保存成 ~/.vimrc.

Vim 能够被重度自定义,花时间探索自定义选项是值得的。你可以参考其他人的在 GitHub 上共享的设置文件,比如,你的授课人的 Vim 设置 (Anish, Jon (uses neovim), Jose)。 有很多好的博客文章也聊到了这个话题。尽量不要复制粘贴别人的整个设置文件, 而是阅读和理解它,然后采用对你有用的部分。

扩展 Vim


Vim 有很多扩展插件。跟很多互联网上已经过时的建议相反,你 不需要在 Vim 使用一个插件 管理器(从 Vim 8.0 开始)。你可以使用内置的插件管理系统。只需要创建一个 ~/.vim/pack/vendor/start/ 的文件夹,然后把插件放到这里(比如通过 git clone)。具体配置方法可以参见本讲的习题部分

以下是一些我们最爱的插件:

我们尽量避免在这里提供一份冗长的插件列表。你可以查看讲师们的开源的配置文件 (Anish, Jon, Jose) 来看看我们使用的其他插件。 浏览 Vim Awesome 来了解一些很棒的插件。 这个话题也有很多博客文章:搜索 “best Vim plugins”。

Shell

如果你是一个 Bash 用户,用 set -o vi。如果你用 Zsh:bindkey -v。Fish 用 fish_vi_key_bindings。另外,不管利用什么 shell,你可以 export EDITOR=vim。 这是一个用来决定当一个程序需要启动编辑时启动哪个的环境变量。 例如,git 会使用这个编辑器来编辑 commit 信息。

Readline

很多程序使用 GNU Readline 库来作为 它们的命令控制行界面。Readline 也支持基本的 Vim 模式, 可以通过在 ~/.inputrc 添加如下行开启:

set editing-mode vi

比如,在这个设置下,Python REPL 会支持 Vim 快捷键。

其他

甚至有 Vim 的网页浏览快捷键 browsers, 受欢迎的有 用于 Google Chrome 的 Vimium 和用于 Firefox 的 Tridactyl。 你甚至可以在 Jupyter notebooks 中用 Vim 快捷键。 这个列表 中列举了支持类 vim 键位绑定的软件。

Vim 进阶

一个好的学习策略是: 当你在使用你的编辑器的时候感觉 “一定有更好的方法来做这个”, 那么很可能真的有:上网搜寻一下!!

搜索和替换


:s (替换)命令(文档)。

  • :%s/\[.*\](\(.*\))/\1/g
    • 将有命名的 Markdown 链接替换成简单 URLs
命令格式含义
:s/foo/bar/gChange each ‘foo’ to ‘bar’ in the current line.
:%s/foo/bar/gChange each ‘foo’ to ‘bar’ in all the lines.
:5,12s/foo/bar/gChange each ‘foo’ to ‘bar’ for all lines from line 5 to line 12 (inclusive).
:'a,'bs/foo/bar/gChange each ‘foo’ to ‘bar’ for all lines from mark a to mark b inclusive (see Note below).
:'<,'>s/foo/bar/gWhen compiled with +visual, change each ‘foo’ to ‘bar’ for all lines within a visual selection. Vim automatically appends the visual selection range (‘<,’>) for any ex command when you select an area and enter :. Also, see Note below.
:.,$s/foo/bar/gChange each ‘foo’ to ‘bar’ for all lines from the current line (.) to the last line ($) inclusive.
:.,+2s/foo/bar/gChange each ‘foo’ to ‘bar’ for the current line (.) and the two next lines (+2).
:g/^baz/s/foo/bar/gChange each ‘foo’ to ‘bar’ in each line starting with ‘baz’.


  • q{字符} 来开始在寄存器{字符}中录制宏
  • q停止录制
  • @{字符} 重放宏
  • 宏的执行遇错误会停止
  • {计数}@{字符}执行一个宏{计数}
  • 宏可以递归
    • 首先用q{字符}q清除宏
    • 录制该宏,用 @{字符} 来递归调用该宏 (在录制完成之前不会有任何操作)

例子:将 xml 转成 json (example-data.xml),详细步骤及解释参照下面的习题解答

  • Gdd, ggdd 删除第一行和最后一行
  • 格式化最后一个元素的宏 (寄存器 e
    • 跳转到有 <name> 的行
    • qe^r"f>s": "<ESC>f<C"<ESC>q
  • 格式化一个的宏
    • 跳转到有 <person> 的行
    • qpS{<ESC>j@eA,<ESC>j@ejS},<ESC>q
  • 格式化一个标签然后转到另外一个的宏
    • 跳转到有 <person> 的行
    • qq@pjq
  • 执行宏到文件尾
    • 999@q
  • 手动移除最后的 , 然后加上 [] 分隔符

扩展资料


课后练习


(提示:本讲课后练习出现的相关文档,均已在百度云盘链接中)

  1. 完成 vimtutor。备注:它在一个 80x24(80 列,24 行) 终端窗口看起来效果最好。
  2. 下载我们提供的 vimrc,然后把它保存到 ~/.vimrc。 通读这个注释详细的文件 (用 Vim!), 然后观察 Vim 在这个新的设置下看起来和使用起来有哪些细微的区别。
  3. 安装和配置一个插件: ctrlp.vim.(提示:这个插件协助vim编辑器快速查找文件、同时打开多个文件、创建新文件和目录,同时支持正则表达式进行文件搜索
    1. mkdir -p ~/.vim/pack/vendor/start 创建插件文件夹
    2. 下载这个插件: cd ~/.vim/pack/vendor/start; git clone https://github.com/ctrlpvim/ctrlp.vim
    3. 阅读这个插件的 文档(提示:阅读readme.md文件)。 尝试用 CtrlP 来在一个工程文件夹里定位一个文件,打开 Vim, 然后用 Vim 命令控制行开始 :CtrlP.
    4. 自定义 CtrlP:添加 configuration (提示:阅读readme.md文件)到你的 ~/.vimrc 来用按 Ctrl-P 打开 CtrlP
  4. 练习使用 Vim, 在你自己的机器上重做 演示
  5. 下个月用 Vim 完成 所有的 文件编辑。每当不够高效的时候,或者你感觉 “一定有一个更好的方式”时, 尝试求助搜索引擎,很有可能有一个更好的方式。如果你遇到难题,可以来我们的答疑时间或者给我们发邮件。
  6. 在其他工具中设置 Vim 快捷键 (见上面的操作指南)。
  7. 进一步自定义你的 ~/.vimrc 和安装更多插件。
  8. (高阶)用 Vim 宏将 XML 转换到 JSON (example-data.xml)。 尝试着先完全自己做,但是在你卡住的时候可以查看上面 章节。

习题解答


  1. 完成 vimtutor。 备注: 它在一个 80x24(80 列,24 行) 终端窗口看起来最好。

    ~$ vimtutor
    # 提示:命令行界面的“终端(T)”选项,可以直接设置窗口为80x24,vimtutor在该窗口下效果最好
    
  2. 下载我们的vimrc,然后把它保存到 ~/.vimrc。 通读这个注释详细的文件 (用 Vim!), 然后观察 Vim 在这个新的设置下看起来和使用起来有哪些细微的区别。

  3. 安装和配置一个插件: ctrlp.vim.

    1). 用 mkdir -p ~/.vim/pack/vendor/start 创建插件文件夹

    2). 下载这个插件: cd ~/.vim/pack/vendor/start; git clone https://github.com/ctrlpvim/ctrlp.vim
    下载后需要在~/.vimrc 中添加如下设置,参考这里

    3). 请阅读这个插件的文档。 尝试用 CtrlP 来在一个工程文件夹里定位一个文件, 打开 Vim, 然后用 Vim 命令控制行开始 :CtrlP.
    4). 自定义 CtrlP: 添加 configuration 到你的 ~/.vimrc 来用按 Ctrl-P 打开 CtrlP

    ~$ vim .vimrc
    # 进入.vimrc文件后,按住Ctrl+g键,跳转到文件末尾,按o键,在末尾添加如下内容即可:
    
    " 设置插件ctrlp
    set runtimepath^=~/.vim/pack/vendor/start/ctrlp.vim
    
    let g:ctrlp_map = '<c-p>'
    let g:ctrlp_cmd = 'CtrlP'
    let g:ctrlp_working_path_mode = 'ra' " 设置默认路径为当前目录
    ~$ vim 
    
    • 进入vim编辑页面后,在正常模式下,按下Ctrl+p,可以打开插件
    • 输入一串字符后,会在电脑查询匹配字符串的文件
  4. 练习使用 Vim, 在你自己的机器上重做演示。

  5. 下个月用 Vim 完成 所有 的文件编辑。每当不够高效的时候,或者你感觉 “一定有一个更好的方式”, 尝试求助搜索引擎,很有可能有一个更好的方式。如果你遇到难题,来我们的答疑时间或者给我们发邮件。

  6. 在你的其他工具中设置 Vim 快捷键 (见上面的操作指南)。

  7. 进一步自定义你的 ~/.vimrc 和安装更多插件。 安装插件最简单的方法是使用 Vim 的包管理器,即使用 vim-plug 安装插件:

    1). 安装 vim-plug

    ~$ curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
    

    2). 修改 ~/.vimrc

    ~$ vim .vimrc
    # 进入.vimrc文件后,键入Ctrl+g,跳转到文件末尾,再键入o,在新的一行里输入以下内容,保存后退出,即可:
    
    call plug#begin()
    Plug 'preservim/nerdtree'   " 需要安装的插件 nerdtree
    Plug 'wikitopian/hardmode'  " 安装 hardmode
    call plug#end()
    

    3). 在 vim 命令行中执行 :PlugInstall

    ~$ vim
    
    • 进入一个空白的编辑页面后,键入 “:PlugInstall” ,即可
  8. (高阶)用 Vim 宏将 XML 转换到 JSON (example-data.xml)。 尝试着先完全自己做,但是在你卡住的时候可以查看上面 章节。

    提示
    a. 先对example-data.xml建立副本,在副本上操作;
    b. 对下面的步骤中不清楚的命令,可以在vim编辑页面中,键入:help Command进行查询;
    c. 最终修改完成的json格式文件可参考example-data.json

    vim example-data.xml
    

    在vim编辑页面依序执行以下操作:

  • 1). 删除首尾两行
    • Gdd:跳转到最后一行,并删除该行
    • ggdd: 跳转到第一行,并删除该行
  • 2). 录制寄存器e,实现对<name>标签的处理
    • /<name>,再键入Enter,然后键入N:在文档的开头查找<name>并跳转到文件的最后一个<name>
    • 接下来,录制宏(即寄存器e):
      • qe:即将录制名为e的宏
      • ^r"^跳转到当前行的首个非空字符,即<name><r"<替换为"
      • f>s": "f>查找>,此处即匹配刚才的<name>>s">替换为": "
      • <Esc>:回到正常模式
      • f<C":查找下一个<,由于xml文件的特征,此时匹配到的是刚才修改的<name>对应的</name><;然后,将当前位置到本行末尾的内容删除,同时插入"
      • <Esc>:回到正常模式
      • q:停止录制,寄存器e完成
    • 寄存器e的效果示例:将<name>Mike</name>修改为"name": "Mike"
  • 3). 录制寄存器p,实现对<person>标签的处理
    • gg:跳转到文件开头(本例中,正好处于一个<person>标签)
    • 接下来,录制宏(寄存器p
      • qp:即将录制宏p
      • S{:将当前行删除,并替换为{
      • <Esc>:回到正常模式
      • j@e:移到下一行,并执行宏e(因为本例的xml文件中,<person>的下一行为<name>
      • A,:在本行的末尾添加上,
      • <Esc>
      • j@e:移到下一行,并执行宏e(本例中,完成对<email>标签行的处理,其处理与<name>标签行的一致)
      • jS},:跳转到下一行,并删除整行,同时添加上},(本例中该行内容为:</person>
      • <Esc>q:回到正常模式,并停止录制,完成宏p
  • 4). 录制寄存器q,实现从一个<person>标签到另一个<person>标签的跳转
    • /<person>:从第三步的位置开始查找下一个<person>标签
    • qq:开始录制宏q
    • @p:执行宏p,可以将这一组<person>标签包含的内容修改为json格式
    • jq:移到下一行,并停止录制,完成宏q(此时,刚好处于下一个<person>标签行)
  • 5). 执行宏q到文件尾
    • 999@q:执行999次宏q(本文档总行数为1002行,执行999次能确保到达文件末尾)
  • 6). 手动修改文件内容
    • gg
    • O:(大写的o)在开头插入如下内容
      {
          "people": {
              "person": [
      
    • <Esc>
    • /<email>:跳转到文件末尾唯一未处理的<email>标签行
    • @e:对该标签行进行处理
    • jS}:移到下一行,删除</person>标签
    • <Esc>
    • o:在文件末尾插入以下内容
              ]
          }
      }
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值