Ted's Blog

Happy coding

extern "C"

时常在cpp的代码之中看到这样的代码:

以下是引用片段:
#ifdef __cplusplus
extern "C" {
#endif
//一段代码
#ifdef __cplusplus
}
#endif

  这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。

  要明白为何使用extern "C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返 回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.

  比如下面的一段简单的函数,我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化:

以下是引用片段:
int f(void)
{
return 1;
}

  在加入extern "C"的时候产生的汇编代码是:

以下是引用片段:
.file "test.cxx"
.text
.align 2
.globl _f
.def _f; .scl 2; .type 32; .endef
_f:
pushl %ebp
movl %esp, %ebp
movl $1, %eax
popl %ebp
ret

  但是不加入了extern "C"之后

以下是引用片段:
.file "test.cxx"
.text
.align 2
.globl __Z1fv
.def __Z1fv; .scl 2; .type 32; .endef
__Z1fv:
pushl %ebp
movl %esp, %ebp
movl $1, %eax
popl %ebp
ret

  两段汇编代码同样都是使用gcc -S命令产生的,所有的地方都是一样的,唯独是产生的函数名,一个是_f,一个是__Z1fv。

  明白了加入与不加入extern "C"之后对函数名称产生的影响,我们继续我们的讨论:为什么需要使用extern "C"呢?C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而 extern "C"就是其中的一个策略。

  试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果 这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数,也就是说假设这 个函数当时是用C写成的:

以下是引用片段:
//f1.c
extern "C"
{
void f1()
{
return;
}
}

  编译命令是:gcc -c f1.c -o f1.o 产生了一个叫f1.o的库文件。再写一段代码调用这个f1函数:

以下是引用片段:
// test.cxx
//这个extern表示f1函数在别的地方定义,这样可以通过
//编译,但是链接的时候还是需要
//链接上原来的库文件.
extern void f1();
int main()
{
f1();
return 0;
}

  通过gcc -c test.cxx -o test.o 产生一个叫test.o的文件。然后,我们使用gcc test.o f1.o来链接两个文件,可是出错了,错误的提示是:

以下是引用片段:
test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()'

  也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链接器找不到函数。

  因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。

  比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写:

以下是引用片段:
extern "C"
{
#include "f.h"
}

  回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:

以下是引用片段:
extern "C"
{
extern void f1();
}
int main()
{
f1();
return 0;
}

  重新编译并且链接就可以过去了.

  总结

  C和C++对函数的处理方式是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。

extern "C" 的用意

前些天,编程序是用到了很久以前写的C程序,想把里面的函数利用起来,连接发现出现了找不到具体函数的错误:

以下是假设旧的C程序库

C的头文件

/*-----------c.h--------------*/
#ifndef _C_H_
#define _C_H_
extern int add(int x, int y);
#endif

C的源文件

/*-----------c.c--------------*/
int
add(int x, int y){
return
x+y;
}

C++的调用

/*-----------cpp.cpp--------------*/
#include "c.h"
void main()
{
add(1, 0);
}

这样编译会产生错误cpp.obj : error LNK2001: unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z),原因是找不到add的目标模块

这才令我想起C++重载的函数命名方式和C函数的命名方式,让我们回顾一下:C中函数编译后命名会在函数名前加以"_",比如add函数编译成obj文件时的实际命名为_add,而c++命名则不同,为了实现函数重载同样的函数名add因参数的不同会被编译成不同的名字

例如

int add(int , int)==>add@@YAHHH@Z,

float add(float , float )==>add@@YAMMM@Z,

以上是VC6的命名方式,不同的编译器会不同,总之不同的参数同样的函数名将编译成不同目标名,以便于函数重载是调用具体的函数。

编译cpp.cpp中编译器在cpp文件中发现add(1, 0);的调用而函数声明为extern int add(int x, int y);编译器就决定去找add@@YAHHH@Z,可惜他找不到,因为C的源文件把extern int add(int x, int y);编译成_add了;

为了解决这个问题C++采用了extern "C",这就是我们的主题,想要利用以前的C程序库,那么你就要学会它,我们可以看以下标准头文件你会发现,很多头文件都有以下的结构

#ifndef __H
#define __H
#ifdef __cplusplus
extern "C" {
#endif

extern
int f1(int, int);
extern
int f2(int, int);
extern
int f3(int, int);


#ifdef __cplusplus
}
#endif

#endif /*__H*/

如果我们仿制该头文件可以得到

#ifndef _C_H_
#define _C_H_
#ifdef __cplusplus
extern "C" {
#endif

extern
int add(int, int);

#ifdef __cplusplus
}
#endif

#endif /* _C_H_ */

这样编译

/*-----------c.c--------------*/
int
add(int x, int y){
return
x+y;
}

这时源文件为*.c,__cplusplus没有被定义,extern "C" {}这时没有生效对于C他看到只是extern int add(int, int);
add函数编译成_add(int, int);

而编译c++源文件

/*-----------cpp.cpp--------------*/
#include "c.h"
void main()
{

add(1, 0);
}

这时源文件为*.cpp,__cplusplus被定义,对于C++他看到的是extern "C" {extern int add(int, int);}编译器就会知道 add(1, 0);调用的C风格的函数,就会知道去c.obj中找_add(int, int)而不是add@@YAHHH@Z

这也就为什么DLL中常看见extern "C" {},windows是采用C语言编制他首先要考虑到C可以正确调用这些DLL,而用户可能会使用C++而extern "C" {}就会发生作用

#ifndef、#def、#endif等宏的含义

 
 
 
一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。
条件编译命令最常见的形式为:
   #ifdef 标识符
    程序段1
    #else
    程序段2
    #endif
    
    它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。
    其中#else部分也可以没有,即:
    #ifdef
    程序段1
    #endif
    
    这里的“程序段”可以是语句组,也可以是命令行。这种条件编译可以提高C源程序的通用性。如果一个C源程序在不同计算机系统上系统上运行,而不同的计算机 又有一定的差异。例如,我们有一个数据类型,在Windows平台中,应该使用long类型表示,而在其他平台应该使用float表示,这样往往需要对源 程序作必要的修改,这就降低了程序的通用性。可以用以下的条件编译:
    #ifdef WINDOWS
    #define MYTYPE long
    #else
    #define MYTYPE float
    #endif
    
    如果在Windows上编译程序,则可以在程序的开始加上
    #define WINDOWS
    
    这样则编译下面的命令行:
    #define MYTYPE long
    
    如果在这组条件编译命令之前曾出现以下命令行:
    #define WINDOWS 0
    
    则预编译后程序中的MYTYPE都用float代替。这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。当然以上介绍的只是一种简单的情况,可以根据此思路设计出其它的条件编译。
    例如,在调试程序时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。可以在源程序中插入以下的条件编译段:
    #ifdef DEBUG
    print ("device_open(%p)\n", file);
    #endif
    
    如果在它的前面有以下命令行:
    #define DEBUG
    
    则在程序运行时输出file指针的值,以便调试分析。调试完成后只需将这个define命令行删除即可。有人可能觉得不用条件编译也可达此目的,即在调试 时加一批printf语句,调试后一一将printf语句删除去。的确,这是可以的。但是,当调试时加的printf语句比较多时,修改的工作量是很大 的。用条件编译,则不必一一删改printf语句,只需删除前面的一条“#define DEBUG”命令即可,这时所有的用DEBUG作标识符的条件编译段都使其中的printf语句不起作用,即起统一控制的作用,如同一个“开关”一样。
    有时也采用下面的形式:
    #ifndef 标识符
    程序段1
    #else
    程序段2
    #endif
    
    只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1,否则编译程序段2。这种形式与第一种形式的作用相反。
    以上两种形式用法差不多,根据需要任选一种,视方便而定。
    还有一种形式,就是#if后面的是一个表达式,而不是一个简单的标识符:
    #if 表达式
    程序段1
    #else
    程序段2
    #endif
    
    它的作用是:当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。
    例如:输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写字母输出。
    #define LETTER 1
    main()
    {
    char str[20]="C Language",c;
    int i=0;
    while((c=str[i])!='\0'){
    i++;
    #if LETTER
    if(c>='a'&&c<='z') c=c-32;
    #else
    if(c>='A'&&c<='Z') c=c+32;
    #endif
    printf("%c",c);
    }
    }
    
    运行结果为:C LANGUAGE
    现在先定义LETTER为1,这样在预处理条件编译命令时,由于LETTER为真(非零),则对第一个if语句进行编译,运行时使小写字母变大写。如果将程序第一行改为:
    #define LETTER 0
    
    则在预处理时,对第二个if语句进行编译处理,使大写字母变成小写字母(大写字母与相应的小写字母的ASCII代码差32)。此时运行情况为:
    c language
    有人会问:不用条件编译命令而直接用if语句也能达到要求,用条件编译命令有什么好处呢?的确,此问题完全可以不用条件编译处理,但那样做目标程序长(因 为所有语句都编译),而采用条件编译,可以减少被编译的语句,从而减少目标的长度。当条件编译段比较多时,目标程序长度可以大大减少。

vim gvim技巧大全(9)

2 用命令}移动到这个段落的底部,标记为b
3 输入命令:'a,'b move来移动文本.
老版本的Vi编辑器不能很好的来处理多文
件.但是Vim在处理多文档上却显得优秀得多.我们有多种不同的方法在不同的文档之间进行文档拷贝.我们能够使用传统的Vi风格的命令,也能够使用Vim
可视化模式.我们还能够利用系统的剪切板来进行不同文档间的文本拷贝,任何的这些方法都能够很好的来工作,采用哪一种方法这就要看我们个人的喜好了.
使用传统的Vi风格命令来在不同的窗口之间进行文本的拷贝能够按照如下的方法来做:
1 编辑第一文档
2 执行命令:split second_file打开另一个窗口并开始编辑第二个文档
3 使用命令CTRL-W p回到含有原始文档的前一个窗口
4 将光标移动到要拷贝文本的第一行
5 用命令ma标记这一行
6 移动到要拷贝文本的最后一行
7 执行命令y'a来复制当前光标位置到所做标记之间的文本.
8 使用命令CTRL-W p 回到将要放置文本的这个文档.
9 将光标移到将要插入文本的地方,复制的文本将会放到这一行的前面.
10 使用命令P命令将复制的文本粘贴到文档中.
(注:p命令则是将文本放在光标所在行的后面)
用可视化模式在两个窗口中进行文本的拷贝能够按照如下的方法:
1 编辑第一个文档.
2 执行命令:split开始编辑第二个文档.
3 使用命令CTRL-W p 回到前一个包含有原始文档的窗口.
4 移动到将要复制文本的第一行.
5 执行命令V进入可视化模式.
6 移动到将要复制文本的最后一行,被选中的文本将会被高亮显示.
7 执行命令y复制选中的文本
8 使用命令CTRL-W p 回到将要放置文本的文档中.
9 移动到将要插入文本的地方,所复制的文本将会被放置在光标所在行的前面.
10 使用命令P来放置所复制的文本.
在不同的vim程式间实现在文本的拷贝能够照如下的方法:
1 编辑第一个文档.
2 启动Vim程式并编辑第二个文档
3 回到含有第一个文档的窗口.
4 移到要复制文本的第一行.
5 执行V命令进入可视化模式.
6 移到要复制文本的最后一行,选中的文本将会被高这显示.
7 使用命令"*y命令将文本复制到系统剪切板.
8 回到另一个窗口.
9 移动到将要放置复制文本的地方,复制的文本将会被放到当前光标的前面.
10 执行命令"*P将复制的文本放在这个文档中.
(注:这个方法似乎只在Gvim中有效)
也许我们经常会编辑一些文档,其中含有一个名字的列表,这时我们希望做到的是将这个名字列表按照一定的顺序进行排列.例如我们能够按照字母的顺序进行排列,可是按照ASCII的顺序进行排列.我们能够按照下面的方法进行:
1 将光标移到要排列的内容的第一行.
2 使用命令ma进行标记.
3 将光标移到要排序的内容的底部.
4
执行命令!'asort进行排序.!命令告诉Vim通过UNIX命令来执行.'a则是告诉Vim这个命令作用的范围.
我们还能够按照下面的方法进行排序:
1 将光标移到要排序内容的第一行.
2 执行命令V进入可视化模式.
3 移到光标到要排序内容的底部,这时选择的文本将会被高亮显示.
4 执行命令!sort进行排序.
Vim
编辑器是个程式员研发给程式员的编辑器.我们能够用这个编辑器在C或是C++程式文档中进行函数的定位.我们要想使用这个功能,我们首先要生成一个名为
tags的文档,在这个文档中含C或是C++程式文档中任何函数的信息.我们能够使用下面的命令来当前我们工作的目录下的任何C程式生成一个tags文
件:
$ ctags *.c
假如是对于C++文档,我们能够使用下面的命令来生成:
$ ctags *.cpp
假如我们是使用其他的扩展名,我们能够使用相应的扩展名,而不一定非要使用C或是C++的扩展名.
在我们生成这个文档以后,假如我们要利用这个文档来编辑我们的程式文档,这样Vim就会查找相应的文档并会在函数中进行定位,例如我们要编辑的文档为write_file,我们能够使用下面的命令来开始我们的编辑工作:
$ gvim -t write_file

如我们正在看一个名为write_file的函数,而在这个函数中调用了函数setup_data,而我们又想要知道这个函数的周详内容,这时我们能够将
光标位在这个函数的开头部分,然后按下CTRL-],这样Vim就会跳到这个函数定义的地方.哪怕是我们要查找的函数在其他的文档中,Vim也能够为我们
进行精确的定位.
假如我们编辑了当前文档在没有保存的情况下使用了这个命令,那么Vim会给出警告信息,并且会忽略这个命令.
有许多和标记函数相关的命令能够使得我们在所标记的函数中进行向前或是向后的跳转和搜索,还能够打开一个新窗口并将被调用的函数在新窗口中打开.
我们在编写程式的时候常常会在程式的开头部分写上一个注释的边框,在其中写一些表明程式用途等等的信息.在Vim我们能够利用在~/.vimrc这个初始化文会写上一些命令来快速的完成这样的工作.例如我们能够其中加入下面的内容:
:ab #b /***************************
:ab #e **************************/
这样就在Vim当中定义的一个简写的标记,在我们要写注释时,只要输入#b就能够了.
而我们要输出下面的注释只要输入#e就能够了.这样的命令对于那些每天要有大量程式写的朋友们来说是不是个巨大的帮助呢!:)

们还能够利用vim来读man帮助手册页,但是用这样的方法并不是太好,因为man显示的结果中一些下划线在Vim中显示会有一些困难.为了去除这样的字
符,我们能够使用标准的UNIX命令ul -i.这样就会去除那些很难阅读的控制字符.例如我们能够用下面的命令来读date的man手册页:
$ man date | ul -i | vi -
我们还要用这样的技巧来使用Vim:
:%s/.\b//g
这个命令是告诉Vim移除那些退格符(\b),从而使用文档更易读.
位于文档行后面的空格符或是制表符有时是没有用,而他们的存在也使得文档看起为不是太舒服,去除这些多余的符号我们能够使用下面命令:
:%s/\s*$//
冒号是表明进入命令模式,任何的命令模式都要指明命令作用的范围,在这里我们是指整个文档(%),这个命令是使得Vim将文档行末的空白符(\s)重复0次或是多数(*).

如我们正在编辑一个文档,而且在这个文档中我们做了许多的改变.这是个很重要的文档,我们不希望因为偶然的改变而造成损失,这时我们能够将这个文档进行
写保护.当我们用Vim来编辑一个有写保护的文档时我们并不会得到警告或是只有很少的警告,但是当我们想保存退出出Vim会给出错误信息,指出这是个写
保护的文档,并且Vim并不会退出.这时我们要怎么办呢?我们能够使用命令:w!强行保存或是使用:w
otherfilename来以另一个文档名进行保存.
在UNIX或是Linux系统中我们能够使用Vim和grep命令组合来编辑一些含有同一
个给定单词的文档.这对我们编写程式的朋友们来说是有着极大的帮助,因为有时我们也许能够希望查看或是编辑含有同一个变量的文档.例如我们任何含有
frame_counter的C程式文档,这时我们就能够使用下面的命令:
$ vim `grep -l 'frame_counter' *.c`
grep命令是在任何的文档中查找含有指定单词的文档,因为我们指定了-l选项,所以这个命令只是会列出含这个单词的文档而不会打印出这一行的信息.这样Vim就会打开grep命令列出的文档进行编辑,而我们也就能够使用:n或是:rewind命令在这些文档中进行跳转.
我们还能够在Vim内部使用:grep命令来查找我们想要的字符串,例如我们要在任何的C程式文档中查找含有error_string的字符串,我们能够使用下面的命令:
:grep error_string *.c
但是这个命令是使用外部的UNIX或是Linux命令,而且Vim会打开第一个匹配的文档,并将光标置于第一个查找到的字符串处.
vi编辑器的学习使用(十七)
在Vim编辑器有着相当丰富的命令和配置.有许多的命令配置能够说我们是根本就不会用到的.下面的只是简要的介绍一些这样的命令和配置的方法.
命令cscope能够检查C或是C++程式文档并产生一个含有程式中函数和变量信息的数据库.我们能够使用Cscope程式来查看这个数据库从而能够得函数定义和使用的一些信息.Cscope能够从下处得到:
http://cscope.soureforge.net
我们能够使用下面的命令来得一些周详的帮助信息:
:help cscope
Cscope一些相关的命令如下:
:cs arguments
:cscope argument
处理一些和Cscope程式相关联的活动
:cstag procedure
定位到Cscope数据库中名为procedure的函数标记处
:set csprg=program
:set cscopeprg=program
定义CScope程式名(默认为Cscope)
:set cst
:set cscopetag
:set nocst
:set nocscopetag
假如配置了cscopetag选项就能够在使用Cscope数据中使用命令(:tags,CTRL-])来浏览标记
:set csto=flag
:set cscopetagorder=flag
这个选项配置了CScope标记查询命令的查询顺序.假如是默认的0,那么会先查Cscope数据库,然后是标记;假如是1,则首先查在标记中查找.
:set csverb
:set cscopeverbose
:set nocsverb
:set nocopeverbose
假如配置了cscopeverbose选项,那么在Vim查找Cscope数据库并且查找失败时给出错误信息,而Vim默认的配置为nocscopeverbose
OLE系统是运行在Windows下面的程式彼此之间进行通信的方法.而Vim编辑器能够来扮演一个OLE服务器的角色.这就意味着我们能够来编写Window程式并和Vim通信.我们能够用下面的命令来得到更周详的帮助信息:
:help ole-interface
和Perl的接口能够使得我们在Vim中执行perl命令,同时能够提供给Perl程式一个接口,使得他能够访问Vim的功能.我们能够使用下面的命令来得一些更周详的帮助信息:
:help perl
Perl的一些接口命令如下:
:pe command
:perl command    执行单一的perl命令
:range perld command
:range perldo command    在几行上执行perl命令
和Perl相类似是Python.我们能够用命令:help python得到更周详的帮助信息.
Python的一些接口命令如下:
:range py statement
:range python statement    执行单一的Python命令描述
:range pyf file
:range pyfile file    执行文档中的Python程式
Sniff+的一些接口命令如下:
:sni command
:sniff command    通过和Sniff+的接口来执行命令.假如没有提供命令,则会显示出当前连接信息.
同样我们能够通过命令:help sniff来得到Vim提供的帮助信息.
Tcl的一些接口命令:
:tc command
:tcl command    执行单一的Tcl命令
:range tcld command
:range tcldo command
在所列出的行中每行执行一次Tcl命令
:tclf file
:tclfile file    在给定的文档中执行Tcl脚本
Vim编辑器能够处理各种不同的语言.在这里列出我们一些常用字的用其他语言来编辑文档的命令.假如我们要想得到一些更周详的说明,则要查阅Vim文档或是我们的系统文档了.
下面的是个固定的常用的命令:
    在从左到右和从右到左两种模式间转换
:set rl
:set rightleft
:set norl
:set norightleft
通过这些选项的配置我们能够选择是从左到右的模式还是从右到左的模式
:set ari
:set allowrevins
:set noari
:set noallowrevins
通过配置这些选项我们能够通过CTRL-_来配置revins选项.这个能够使得我们选择语言的输入的是从左到右还由右至左.
:set ri
:set revins
:set nori
:set norevins
通过这些选项配置,我们能够选择在插入模式下是由左至右还是由右至左.假如配置了allowrevins选项我们可通过CTRL-_来在这几个选项间进行转换.
:set gfs=f1,f2
:set guifontset=f1,f2
定义英语使用f1字体,而另一种语言使用f2字体
这个选项只有在我们编译Vim编辑器时允许进行字体配置并且是只在UNIX系统才能够正常的工作.
:set lmap=ch1ch2,ch1ch2
:set langmap=ch1ch2,ch1ch2
为外文本配置键盘映射
Vim编辑器对汉字的输入支持由左到右,由右到左几种模式.他还支持传统的中文和简体中文.和中文相关的命令如下:
:set fe=encodning
:set fileencoding=encoding
配置文档的编码.对于中文这个选项能够是对于传统中文的taiwan或是对于简体中文的pre.
假如我们要编辑Vim编辑器时打开了Farsi的支持,我们就能够在用这种语言来编辑文档了.能够用-F选项在启动Vim时进入Farsi模式:
$ vim -F file.txt
我们能够得到更周详的信息:
:help farsi
和Farsi相关的命令如下:
:set fk
:set fkmap
:set nofk
:set nofkmap
通过这些选项的配置我们能够告诉Vim我们正在使用Farsi键盘
:set akm
:set altkeymap
:set noakm
:set noaltkeymap
通过配置这些选项我们能够告诉vim编辑器键盘映射是Farsi还是Hebrew
CTRL-_    在Farsi和正常模式下进行转换
    在标准的ISIP-3342编码和扩展的ISIR-3342编码之间进行转换
Hebrew是由右到左的另一种语言.采用Hebrew模式进行编辑能够使用下面的命令:
$ vim -H file.txt
:help hebrew能够使得我们得到更多的帮助信息.
和Hebrew相关的一些命令:
:set hk
:set hkmap
:set nohk
:set nohkmap
通过这些选项我们能够打开或是关闭Hebrew键盘映射
:set hkp
:set hkmappp
:set nohkp
:set nohkmapp
通过这些选项我们能够告诉Vim编辑器我们正在使用Hebrew键盘还是标准的英语键盘(默认为nohkmapp,即标准的英语键盘)
CTRL-_    这个命令能够使得我们在Hebrew或是正常插入状态下进行转换
:set akm
:set altkeymap
:set noakm
:set noaltkeymap
假如配置了altkeymap选项,那么和其交换的键盘映射为Farsi.假如配置了noaltkeymap选项,那么则是Hebrew键盘映射.(默认为noaltkeymap)
Vim编辑器还能够支持日文,和日文相关的一些命令如下:
:set fe=japan
:set fileencoding-japan
告诉Vim编辑器当前文档采用日文编码.
我们能够通过命令:help hangul得到更多的韩文帮助信息.和韩文相关的命令如下:
:set fe=korea
:set fileencoding=korea
告诉Vim编辑器当前的文本采用韩文的编码.
我们还能够使用Vim编辑器来编辑二进制文档,相关的命令如下:
:set bin
:set binary
:set nobin
:set nobianary
假如我们配置了insertmode选项,那么Vim默认的便为插入模式.我们能够通过命令CTRL-O转换到正常模式.相关的命令如下:
:set im
:set insertmode
:set noim
:set noinsertmode
CTRL-L    假如配置了insertmode选项,则保留这种配置
vi编辑器的学习使用(十八)
我们在Vim编辑器的学习使用(二)曾学过一些基本的编辑命令,有了这样的一些基本的编辑命令,我们能够完成绝大多数的编辑任务.在这一次的学习中我们将会接触到更多的更深一些的东西.
Vim编辑器有着各种不同的命令能够使得我们任意的移到一个单词的开头或是结尾.但是有我们却能够通过Vim的一些选项的内容来自定义一个单词的定义.w命令能够使得光标向前移到一个单词.而命令e也是向前移到一个单词,但是这个命令是将光标定位在一个单词的结尾处.
而命令ge则是向后移到一个单词到达前一个单词的结尾处.

么怎么样来定义一个单词呢?我们也许都知道单词只是一系列的字母的组合.然而在C程式中size56却会被认为是个单词,因为在C程式中我们是通过字
母,数字和下划线来定义一个单词的.但是在LISP程式中我们能够在变量名中使用-,这时他们会认为total-size是个单词,而在C程式中这却会
被认为是两个单词.我们如何来解决这样的冲突呢?Vim的解决办法是产生一个选项来定义哪些是个单词中的,而哪些不是.例如下面的命令定义了属于一个单
词中的字母:
:set iskeyword=specification
查看当前选项的值我们能够使用下面的命令:
:set iskeyword?
下面的是一般的值:
[email=iskeyword=@,48-57,_,192_255]iskeyword=@,48-57,_,192_255[/email]
在这些值中间中用逗号来分隔的.
假如我们想要单词中的字母是专一的元音,我们能够使用下面的命令:
:set iskeyword=a,e,i,o,u
我们还能够使用横线来指定字母的范围.假如要指定任何的小写字母,我们能够用下面的命令:
:set iskeyword=a-z
对于那些不能直接指定的字符我们能够使用十进制的数字来表示.假如我们要指定小写字母和下划线为一个单词,我们能够使用下面的命令:
:set iskeyword=a-z,45
(短横线的十进制数字表示为45)
字母@指代C函数isalpha()返回值为真是的任何的字符.这个要取决于我们所使用的C编译器和我们编译Vim所使用的操作系统)
排除某一个字符我们能够在这个字符前加上一个前缀^.例如我们能够使用下面的命令来定义一个单词,这个单词能够由除了q以外的小写字符组成,也就是说在由空格分格的字符串组成,q为一个单词,而其他的字符串的组合为一个单词:
:set
[email=iskeyword=@,%5Eq]iskeyword=@,^q[/email]
而字符@则是由@-@所指代.
iskeyword选项一些特别字符如下:
a    字符a
a-z    任何由a到z的字符
45    十进制数字45(短横线-)
@    由函数isalpha()所定义的任何字符
@-@    字符@
^x    除了x以外的字符
^a-c    除了a以处的到c的字符,即b和c
命令iskeyword能够简记为isk
iskeyword选项能够控制哪些字符能够是个单词而哪些不是.下列的一些相似的命令能够控制其他类型的字符:
isfname        文档名
isident        定义
isprint        打印字符
选项isfname在使用命令gf时会用到,这个命令将会编辑以光标下的单词为文档名的文档.
选项isident在使用命令[d时会用到,这个命令将会查找以光标下的单词为名称的宏定义.
选项isprint定义了哪些字符能够显示在屏幕上.但是对这个我们要小心,假如我们弄错了,屏幕就会被弄得一团糟.这个选项还能够用特定的查找命令\p,这代表可打印的字符.

许到了现在我们已明白了什么是单词(words).而Vim编辑更有一些命令影响到WORDS.虽然这只是大小的不同,但是他们却代到了两种不同的事
物.word是指由iskeyword选项定义的字符串,而WORD则是指没有空白符的字符.在这样的观点上,that-all是两个单词(word),
但是却是个WORD.
W命令是向前移动WORDS,而B命令是向后移动WORDS.
和WORD相关的一些命令如下:
[count]B    向后移动count个WORDS
[count]E    向前移动count个WORDS,并且将光标置于WORD的末尾.
[count]gE    向后移动count个WORDS,并且将光标置于WORD的末尾.
[count]W    向前移动count个OWRDS.
我们能够使用命令将光标移动到一个句子中第一个没有空白符的字符处.假如我们要到达一行的开始处,我们能够命令用0命令.
我们能够使用命令fx在当前行光标的后面查找字符x.假如我们要重复这个查找操作能够使用命令;.就像大多数的Vim命令相同这个命令能够用数字来指明查找的次数.;命令是按照前一次f或是F的查找方向继续查找.假如我们要向相反的方向查找我们能够使用命令,.
命令-能够向上移动到第一个没有空白符的前一行处.我们能够指定参数来移到几行.
命令+能够向下移到到第一个没有空白符的后一行处,我们能够指定参数来移到几行.
命令_能够移动到当前行第一个没有空白符的字符处.
更有一些命令能够使得我们在屏幕的不同地方进行移动.H命令能够将光标移到屏幕的顶端.假如指定的数字参数则能够移动到从屏幕顶端算起的由数字所指定的行处.和H命令相类似是L命令,所不同的只是这个命令移动到屏幕的底端.M命令能够将光标移到屏幕的中间位置.
Vim编辑器还能够记录您曾到过的地方,并且能够使得我们回到前一次到过的地方.例如我们在编辑一个文档的时候执行了下面的命令从而到过不同的行处:
1G    到第一行
10G    到第十行
20G    第第二十行
现在我们执行下面的命令:
:jumps

样我们就会看到一个我们曾到过的行数的列表.在这个列表中>指的是这个列表中的当前项.这样在我们处在命令模式下我们就能够使用命令CTRL-O跳
回一行.这样>所指的就会上移一项.而命令CTRL-I或是能够使得我们在这个列表中向下跳转.应用这样的命令我们就能够实
现在文档中进行快速的浏览和跳转.
在通常的情况下,当光标位于一行的开头是结尾的时候Vim是不能够移动光标的.但是我们能够通过配置whichwrap选项来控制光标是否能够越过一行的结尾并且Vim处于哪种模式.whichwrap选项一些可能的值如下表所示:
Character    Command        Mode(s)
b                正常模式或是可视模式
s                正常模式或是可视模式
h        h        正常模式或是可视模式
l        l        正常模式或是可视模式
        右移        正常模式或是可视模式
~        ~        正常模式

们能够通过命令CTRL-G来使用Vim在屏幕的下端显示出我们所在的位置的一些信息.然而只要我们来发问我们就能够得到一些更周详的信息.为了得到更详
细的信息,我们能够在CTRL-G命令加上一个数字参数.这个数字越大我们得到的信息就越周详.例如命令1CTRL-G会告诉我们文档的全路径.而命令
2CTRL-G会同时显示缓冲区的数字标号.而命令gCTRL-G则能够显示当前光标所在的行号,列号连同文章的字符数等一些周详的信息.
我们在前面曾讨论过,CTRL-U命令能够使得vim编辑器向上翻滚半屏,但是我们可通过配置scroll选项来控制这个命令翻滚的行数.例如下面的命令能够使得Vim一次翻滚10行:
:set scroll=10
我们也能够通过防变CTRL-U命令的参数来改变翻滚的行数.例如命令2CTRL-U能够使得Vim编辑器一次向上翻滚两行,直到有命令来翻滚的大小为止.
假如我们要一次翻滚一行我们能够使用CTRL-Y命令.当然这个命令也能够在前面配置参数来控制翻滚的行数.而CTRL-B命令则是一次翻滚一屏.
和向上翻滚的命令相对是向下翻滚的命令,这样的命令有如下的一些:
CTRL-D    向下翻滚.这个数值我们能够通过scroll的值来控制.
CTRL-E    一次向下翻滚一行.
CTRL-F    一次向下翻滚一屏.
当光标到达窗口的上端或是下端的时候窗口要发生滚动.我们能够通过配置scrolljump选项来控制这个翻滚数值的大小.默认情况下为1,当然了我们也能够将其改我们希望的样子.如下面的命令将翻滚量设为5:
:set scrolljump=5
和其相类似的就是sidescroll选项,所不同是后者来控制水平的翻滚.
通常情况下,窗口的翻滚是当光标到达窗口的顶部或是底部时才发生的,我们也能够通过scrolloff选项来控制光标和顶部或是底部有多少距离时发生:
:set scrolloff=3
这个命令将其为3,当光标和顶部距离为三行时发生翻滚,且翻滚后光标和底部相距三行.

许我们在编辑文档的过程中有时希望将指定的一行放在屏幕顶端.当然了这样的问题我们能够使用CTRL-E或是CTRL-Y命令来一行一行的移动直支满期足
需要为止.我们还能够将光标放在指定的行上,然后输入z.我们这一行就会出现在屏幕的顶端了.
这个命令在没有任何参数
的情况是将当前行置于屏幕的顶端,我们还能够指定参数,这样就能够将指定的行置于屏幕顶端了.例如命令8z就是将第八行置于屏
幕顶端.这个命令不但能够将指定的行置于顶端,还能够将光标移动到本行第一个没有空白符(non-blank)的字符处.假如我们要将光标保持在一行的某
一位置不变我们能够使用命令zt,这样在这一行的位置发生变化,光标的位置也会保持不变.
假如我们要将指定的一行放在屏幕的底部,我们能够使用命令z-或是zb.所不同的只是前者是将光标放在这一行中第一个没有空白符的字符,而后者是保持光标的位置不变.
命令zz或是z.能够将指定的行放在屏幕的中部.这两个命令的不同就是前者保持光标的位置不变,而后者是将光标置于第一个没有空白符的字符处.
D命令能够将光标所在处到这一行的结尾的文字全部删掉.我们也能够在这个命令前面加上数字做为前缀,这样在执行这个命令以后,Vim不但会将光标到光标所在行结尾处的字符全部删除,而且会再删除这一行以下的count(数字前缀)-1行文本.
和D命令相类似的是C命令.所不同的仅是C命令在删除文本以后会进入插入模式使得我们能够接着进行文本的编辑.
s命令能够删除单个的字符并进入插入状态.假如我们在前面加上数字做为前缀,那么Vim就会删除数字所指定的那么多的字符,然后进入插入状态.
而S命令是删除当前行然后进入插入状态.我们能够指定数字count做为前缀,这样Vim就会删除count个行,然后进入插入状态.这个命令和C命令的不同之处只是S命令作用整个行,而C命令仅是光标所处的位置到行末.
x命令是删除当前光标下的字符,假如指定count作为参数,则是向右查找count个字符并删除,而X命令是删除当前光标前的一个字行,假如指定count作为参数,则是向左查找count个字符并删除.

进入插入模式时我们能够使用i或是I命令.i是在当前光标处开始插入字符,而I则是一行的开头部开始插入字符.(所谓的开头是指第一个没有空白符的字符
处).假如我们要在一行中的第一个字符处开始插入我们能够使用gI命令(不论有没有空白符).a命令是在当前光标的后开始插入,而A命令和a命令相类似,
只是他是在一行的后面开始插入状态.
Vim还能够对于文本进行简单的算术运算.命令CTRL-A能够将当前光标下的数字加1,我们还能够在前面指
定参数,这样就能够指定的数字加在光标下的数字上了.假如这个数字是以0开头,那么Vim就会认为这是个八进制的数.例如0177在我们执行这个命令后
就会变为0200.假如一个是以0x或是0X开头的Vim就会这是个十六进制的数,例如0x1E在我们执行这个命令后就会成为0x1F.和CTRL-A
命令相类似的是CTRL-X命令.所不同的只是这个命令会使得当前光标下面的数字减1.Vim是个精巧的编辑器,他能够很好的来处理十进制,八进制,十
六进制的数字计算问题.
在默认的情况下,Vim能够识别出八进制和十六进制的数字.我们能够通过nrformats来控制Vim所识别的数字形式.假如要使得Vim识别出十进制和八进制的数字,我们能够使用下面的命令:
:set nrformats=""
假如我们要使Vim只识别出八进制数,我们可使用下面的命令:
:set nrformats=octal
默认的情况下我们Vim能够识别出十进制,八进制,十六进制数字:
:set nrformats=octal,hex
我们能够使用J命令将当前行和下一行合并为一行.在合并后Vim会加入一空格来分隔这两行.假如我们不希望用空格来分隔,我们能够使用gJ命令.这个命令和J命令类似,只是他不会加入空格来分隔这两行.
R
命令能够使得Vim进入替换模式.在这种模式下,我们输入的每一个字符都会替换光标下面的字符,直到我们按退出为止.我们还能够指
定数字作为参数来指明这条命令所要执行的次数.(注:这里我做的结果是r命令能够指定参数来指明执行次数,而R命令则不成)
当我们在替换的文本中
有键时,替换命令就会出现问题.因为他也会将替换为相应的字符,这样就影响了我们文本的缩进.在这样的情况
下我们能够使用gr命令来进行替换.假如光标下的字符是Tab的一部分,那么就会跳过而替换别的字符,这样就不会影响我们文本的缩进了.我们还能够使用
gR命令进入虚替换模式(virtual replace mode),这时我们输入的字符就会替换屏幕空白处的一个字符.
我们能够通过执行命令CTRL-K character1 character2来插入一个特别字符.我们也能够用下面的命令为定义们的自己的特别字符:
:digraphs character1 character2 number
这就是告诉Vim编辑器当我们输入CTRL-K character1 character2命令以后要插入数字表示为number的字符.
假如我们的工作要我们插入很多的特别字符,我们能够用下面的命令打开digraph选项:
:set digraph
关闭这个选项的命令为:
:set nodigraph
(注:这部分内容部是看不明白,也做不出结果)
命令~能够改变字母的大小写.~命令的执行结果是由选项tildeop的值来控制的.假如我们没有配置这个选项,那个这个命令就像正常相同的动作:
:set notildeop
但是假如我们配置了tildeop选项,那么这个命令的语法就变成了~motion的形式:
:set tildeop
例如下面的句子:
this is a test
假如我们没配置这个选项,那么在我们执行命令时就会实单个字符的大小转换.但是在我们配置了这个选项以后,我们将光标放在第一t上并执行~ft的结果则为
THIS IS A Test
这个命令会使得当前光标以后第一t和光标间的字符全部转换为大写.假如在这个句子中更有小写的字符,那么这个命令还能够执行第二次.直到句子中的字符全部为大写为止.
这时的命令还能够指定字符转换的个数和方向.例如命令3~l是从当前字符开始向右3个字符进行大小写的转换.和这个命令方式相类似的是g~motion格式的命令,所不同的仅是后者不依赖于tildeop选项的配置.命令g~g~或是g~~是进行整个一行的转换.
其他的一些大小写转换的命令更有:
gUmotion
命令是使得当前光标处到motino所指处的字符全部变为大写.而gUU或是gUgU则是作用在整个行上.假如指定了参数count则所指定的行都会发生
变化.而gumotion,guu,gugu则是和上面所说的命令相类似,所不同的只是他们是将字符变为小写.
我们在用Vim进行编辑的时候能够进行多次的撤销命令,这个次数是由选项undolevels来指定的.假如我们要将这个值设为5000,那么我们能够用下面命令来做到:
:set undolevels=5000
命令ZQ是命令:q!的另一种说法.这个会放弃我们所做更改然后退出.
命令:write则是保存文档.命令:quit是退出Vim.我们能够使用一个缩写来达到保存退出的目的::wq
这个命令能够用参数来指定保存退出时使用的文档名,如:
:wq filename
假如这个文档存在且是个只读文档时我们会得到一些错误信息,当然了我们能够强制执行这个命令:
:wq! filename
我们还能够将指定的行进行保存:
:1,10 wq filename
和命令:wq相类似的命令是:xit
vi编辑器的学习使用(十九)
Vim编辑器强大的搜索引擎能够使得我们快速的执行各种类型的查找,从而大的方便了我们的编辑工作,使得我们的编辑工作更加快速和高效.
我们在进行查找的过程中能够打开高亮显示选项,这样在我们找到我们想要的字符后,Vim就会将字符串进行高亮显示,我们也能够很方便的知道我们要找的字符串在哪里.我们能够使用下面的命令来打开高亮显示选项:
:set hlsearch
关闭这个选项的命令为:
:set nohlsearch
在默认的情况下Vim编辑器是很敏感的,也就是Vim编辑器能够很好的来区分一个单词的大小写,从而能够准确的查找得到我们想要的字符串.例如假如一个文档中有这样的几个字符串:
include,INCLUDE,Include.当我们使用命令/include来查找字符串时只有include字符会被高亮显示.
但是假如我们打开ignorecase选项后再执行这个命令结果就不一了,这时任何的类似的字符都会被高亮显示.打开这个选项的命令为:
:set ignorecase
但是这样的查找结果并不是我们想要的,也不是我们所希望发生的,因而我们常用下面的命令选项配置:
:set noignorecase
这样Vim就能够正确的查找我们想要的字符串.
假如我们配置了ignorecase选项后,我们想要查找字符串word,而匹配的则可能是word,Word,WORD.假如我们要查找字符串WORD,匹配的结里也是相同的.但是假如我们配置了以下的两项后的执行结果就会变得不相同了:
;set ignorecase
:set smartcase
经过这样的配置以后,假如我们输入的是小写字符,那么Vim就会匹配各种可能的组合,这时和配置了ignorecase的情况相同,但是假如我们在输入中有一个大写字符,那么这时的查找就变成了精确查找了,和配置了noignorecase的情况相同.
在默认的情下,我们输入要查找的字符串,vim是从当前光标处向前查找,直到文档的结尾,假如没有打到,那么就会从文档的开头开始查找,直到光标所处的位置.我们能够通过配置选项nowrapscan来禁止这种循环查找的方式,这个命令如下:
:set nowrapscan
这样以后假如已查找到文档的底部时就会在Vim底部显示出一条错误信息.假如我们想要回到正常的状态,我们能够使用下面的命令;
:set wrapscan
假如我们正处在一个很长的查找过短中而我们想要停止这一查找开始我们新的工作,这时我们能够使用CTRL-C命令,假如是在Windows系统上则要使用CTRL-BREAK命令.

们在编辑文档的过程中还能够使用立时查找的命令.假如我们想快速查找当前光标下的字符串,我们能够使用命令*,这个命令能够向前查找和当前光标下的单词精
确匹配的字符串.而命令#则向前查找和当前光标下的字符精确匹配的字符串.换句话说假如当前光标下的字符串为word,在执行*命令查找时并不会和
Word相匹配.和这个立时查找命令相类似的就是g*命令.这也是个立时查找的命令,只但是他不会进行严格的整字匹配,假如用这个命令来查找word那
么就有可能和Word相匹配.而g#命令和其相同,只但是他是向相反的方向进行查找匹配.
在默认的情况下,在查找时Vim会将光标放在第一个匹配的结果的开始处.我们也能够指定查找结束后光标所处的位置.对于向前查找的命令我们能够斜线后用数字来指明光标所处的位置,如下面的命令:
/set/2
这个命令会在查找结束后将光标入在第一个set字符串后第二行的开始处.

这个命令中这个数字能够是正数也能够是负数.假如仅是个简单的数字,光标会被放在第一个匹配字符串处后或是前的数字所指定的行的开始处.正是向后,负数
是向前.假如斜线后是b和数字的,那么在查找结束后,光标将会被置于第一个匹配字符串的开始处,然后向左或是右移动n个字符,这里的n即为数字所指定的
数.假如为正数则是向右移动,假如是负数,则是向左移动.如下面的命令:
/set/b2
这个命令会使用Vim在查找结束后将光标放在第一个匹配字符的开始处,然后向右移动两个字符,也就是说最后光标会位于第一个匹配字符串中的t的位置.将b改为s也是相同的效果.
和参数b或是s相类似是e参数,这个参数会使得光标放在第一个匹配字符串的结尾处.同样我们也能够指定数来指是向右还是向左移动光标连同移动的字符数.如下面的命令:
/set/e
这个命令会使光标放在第一个匹配字符处的结尾处,在没有指定数字时是这个样子的.而下面命令:
/set/e2
这个命令是会将光标放在第一个匹配字符串的结尾处,然后向右移动2个字符.这里的数字假如是正数则向右移,假如为负数,则向左移.
例如下面的命令:
/set/e+2
这个命令是告诉Vim在查找set字符串结束后将光标放在第一个匹配字符串的结尾处,然后向右移动两个字符.在这里我们将这个数字称为偏移量.假如我们要重复上一次的查找但是需要不同的偏移量我们能够用下面的命令:
//5
不使用偏移量时我们能够指明一个空的偏移量,如:
//
例如下面的一些例子:
/set/e+2
向前搜索字符串2,并将光标放在第一个匹配字符串的结尾处,然后向右移动2个字符
/    重复前一次的查找,使用相同的偏移量.
//    重复前一次的查找,但是不使用偏移量.
我们还能够使用查找命令?来进行类似的查找,例如:
?set?b5
这个命令是告诉Vimu将光标放在最后一个匹配字符串的开头部分,然后向右移动5个字符
??-2命令则继续前一次的查找命令,但是使用新的偏移量.
??命令是继续前一次的查找命令,但是不使用偏移量.

是有一件事我们要清楚的就是我们在用偏移量进行光标定位时的查找是从当前光标所在处开始的,这会给我们带来一些麻烦.例如我们在执行命令/set/-2时
Vim会将光标放在第一匹配的字符串处,然后上移两行.当我们用n命令来重复上一次的查找时,我们会找到我们刚才找到过的那个字符串,然后再向上偏移两
行.这个结果就是不论我们输入了多少的次n命令,我们仍是在原地踏步的,哪里也去不了.
Vim使用通用的表达式(regular
expressions)来进行逻辑查找.我们在以前讨论过用简单的字符串进行查找,但是这里我们将要看到的通用字符串查找要简单字符查找的功能强大得
多.通过在我们的命令中使用通用表达式,我们能够查找任何一种字符类型,例如我们能够查找以t开头而以ing结尾的字尾串(通用表达式为\).然而这种强大的功能也是要付出一定的代价的.通用表达式是神秘的和简洁的.也许我们要花上很上的一段时间才会习惯这种查找方
式,然后才能掌控这个强大的查找工具.
在学习这些通用表达式的查找的过程中我们最好将高亮显示这个选项打开,这样就能够使Vim高亮显示最后一次查找的匹配结果.打开高亮显示的命令为:
:set hlsearch
一个通用表达式是由一些元素组成的.这些元素是通用表达式中最小的匹配单位.一个元素能够是个字符,例如a,和字符a相匹配,或是个特别字符,例如$,匹 配一行的结束.还能够是其他的字符,例如\来匹配一个单词的结束.也就是说要将我们想要查找的字符串放在这两个中间.这样我们就
能够精确的来查找我们想要查找的字符串,而不会有其他的一些匹配情况.而假如我们用简单字符串形式来查找,我们就会得到许多的匹配情况,甚至在一个单词中
的组成部分也能够成为匹配情况.例如在文档中有Californian,Unfortunately.假如用命令/for来查找,那么就会找到这两个单
词.而假如我们用通用表达式\来进行查找,则只会精确的查找到for,而不会用其他的匹配情况.这时的命令形式如下:
/\

们在进行查找的时候还能够使用一些修饰符来进行表达式的组合.例如修饰符*就能够表示一个字符能够匹配0次或是多次.换句话说,Vim编辑器会进行尽可能
多的匹配.所以通用表达式te*能够匹配te,tee,teee等等.基于还能够匹配t,为什么呢?因为在这里e能够匹配0次,所以就能够匹配t.而修饰
符\+则表明这个字符能够匹配一次或是多次.所以表达式te\+能够匹配te,tee,teee等等.但是这一次这个表达式不能够再匹配t了,因为这里e
最少要匹配一次.
最后一个修饰符\=表示一个字符匹配0次或是一次.这就是说表达te\=能够匹配t,te,但是不能够是tee,虽然这个命令会匹配tee的前两个字符.
更有一些特别的字符能够来匹配一定范围的字符.如\a匹配一个字符,而\d匹配任何数字.所以表达式\a\a\a能够匹配任意三个字符.例如下面的命令能够查找任意四个数字:
/\d\d\d\d
我们还能够用下面的命令来查找任意后带一个下划线的三个字符:
/\a\a\a_
\a
能够匹配任何的字符(小写的或是大写的).但是假如我们现在只要匹配元音字符又该如何来做呢?这时我们能够使用范围作用符[].范围作用符能够匹配一系字
符中的一个.例如[aeiou]只匹配一个小写元音字符.所以表达式t[aeiou]n能够匹配tan,ten,tin,ton,tun.
我们还能够通过短横线来在括号内指明字符的范围.例如[0-9]能够匹配0到9中的任一字符.
我们还能够组合其他的字符.例如[0-9aeiou]能够匹配任意一个数字或是小写的元音字符.
而修饰符^能够指代除本身以外的任何字符.
下面列出一些匹配的情况:
表达式        匹配结果
one[\-]way    one-way
2\^4        2^4
2[\^*]4        2^4,2*4

果我们要指找任何的大写字符又应如何来做呢?一个办法就是使用表达式[A-Z].更有一个办法就是我们能够使用预先定义的字符类.[:upper:]能够
匹配大写字符.所以我们要指找大写字符也能够这样的来写:[[:upper:]].我们能够使用字符类来写出任何的字符:
[[:upper:][:lower:]].
在Vim中更有许多不同的字符类定义
我们还能够通过表达来指出一个字符重复的次数.这个的一般格式如下:
\{minimum,maximum}
例如表达式a\{3,5}能够匹配3到5个a.在默认的情况下Vim将会尽可能多的进行匹配.所以表达a\{3,5}最多能够匹配到5个a.
在这个命令格式中最小次数能够省略,Vim默认的情况下最小次数为0.所以表达式a\{,5}能够匹配0到5个a.最大次数也能够省略,在这种情况下Vim默认匹配无穷大.所以表达式a\{3,}最少能够匹配3个a,最多是尽可能的多.
假如我们只指定一个数字,那么Vim就会精确的匹配相应的次数.例如a\{5}只会精确的匹配5次.
假如我们在数字前加了一个负号(-),那么Vim在查找时就会尽可能少的进行匹配.
例如a\{-3,5}匹配3到5个a,但是会尽可能少的进行匹配.事实上这个表达式仅会匹配3个a,哪怕是我们的文档中用aaaaa,Vim也只会尽可能少的进行匹配.

达式a\{-3,}匹配三个或是更多个a,并且尽能够少地的进行匹配.而表达式a\{-,5}能够匹配0到五个字符.表达式a\{-}能够匹配0到无穷大
个字符.在通常情况下这个表达式匹配0个符,除非在他的后面更有字符或是表达式.例如[a-z]\{-}x将会匹配.cxcx中的cx.而表达式[a-
z]*x将会匹配整个cxcx.最后表达式a\{-5}将会精确的匹配5个字符.
我们还能够使用运算符\(和\)定义一个组.例如表达式a*b能够匹配b,ab,aab,aaab等等.而表达式a\(XY\)*b能够匹配ab,aXYb,aXYXYb,aXYXYXYb等等.
我们还能够用或运算运符\|来查找两个或是多个可能的匹配.例如通用表达式foo\|bar能够查找foo或是bar.
现在我们知道了这样多的表达式的表示方法,那么我们如何来应用他们呢?例如我们现在要查的内容为1MGU103.这个字符串是由1个数字,3个大写字符,3个数字组成的.能够有几种方法来表示:

是先表示前面的一个数字:[0-9],然后加入大写字符后就成为了:[0-9][A-Z].因为有三个大写字符我们能够加入精确匹配的数字:[0-9]
[A-Z]\{3}.最后我们再加入最后面的三个数字.所以最后的结果就成了:[0-9][A-Z]\{3}[0-9]\{3}
另一种利用\d指任何的数字,而\u指任何的大写字符.所以用这样的方法写成的表达示就为:
\d\u\{3}\d{3}.
从这里我们能够看到用这样的方式来查找要比第一种方法快得多.假如我们编辑的文档在采用这两种方法进行查找时会看出差别,那么我们的文档也许就是太大了.
我们还能够用这样的表达示:\d\u\u\u\d\d\d,这个也能够找到我们想要的内容.
最后我们还能够用字符类来写出我们的表达式:
[[:digit:]][[:upper:]]\{3}[[:digit:]]\{3}
这四种方法都能够很好的来完成我们的工作,我们只要记住一种我们容易记住的就能够了.毕竟我们能够记住的简单的方法要比我们不能记住的精妙的方法快得多啊.
到现在我们任何的讨论和方法都是在认为我们已打开magic选项的基础上来做的.假如这个选项被关闭了,那么我们在通用表达式中的许多的特别字符就失去了他们神奇的魔力,我们只有通过字符转义才能够正常的来使用.
关闭magic的选项命令为:
:set nomagic
这时*,.,[,]就都被认为只是平常的字符了.假如我们还想用*来指0次或是更多次就要用到转义:\*.
所以我们应确保magic选项是打开的,这样我们也才能够使用Vim强大的搜索能力,而这也正是Vim默认情况下所做的.
一些常用的偏移(Offset)定义:
+[num]        光标置于第一个匹配字符下第num行的开始处.
-[num]        光标置于第一个匹配字符上第num行的开始处.
e        匹配字符串的结尾处.
e[num]
光标置于第一个匹配字符串的结尾处,然后移动num个字符,假如为正,向右移,为负,向左移.
b s        第一个匹配字符串的开始处.
b[num]
s[num]
光标置于第一个匹配字符串的开始处,然后移动num个字符,假如为正,向右移,为负,向左移.
常用的通用表达式如下:(认为magic选项打开)
简单的元素:
x    字符x   
^    一行的开始处
$    一行的结尾处.
.    单一的字符
\    查找字符串的结束标记.
范围运算符:
[abc]        匹配a,b或是c
[^abc]        匹配除abc以处的字符
[a-z]        匹配从a到z的任何小写字符
[a-zA-Z]    匹配任何字符,包括大小写.
字符类:
[:alnum:]    匹配任何的字符和数字
[:alpha:]    匹配任何的字符
[:ascii:]    匹配任何的ASCII字符
[:backspace:]    匹配退格符
[:blank:]    匹配空格和Tab
[:cntrl:]    匹配任何的控制字符
[:digit:]    匹配任何的数字
[:escape:]    匹配Esc
[:graph:]    匹配所打印的字符,不包括空格
[:lower:]    匹配任何的小写字符
[:print:]    匹配任何的要打印字符,包括空格
[:return:]    匹配任何的行末符号(包括,,).
[:punct:]    匹配任何的功能符号
[:space:]    匹配任何的空白符
[:tab:]        匹配Tab
[:upper:]    匹配任何的大写字符
[:xdigit:]    匹配十六进制数字.
类型:
\(pattern\)    标记一个类型以后使用
\1        和第一个在\(\)中的子表达式匹配的字符串匹配相同的字符串
例如表达式\([a-z]\)\1能够匹配aa,bb或是类似的.
\2        和\1相类似,但是是使用第二个子表达式
\9        和\1相类似,但是是使用第九个子表达式
特别字符:
\a    大小写字母字符
\A    除了a-zA-Z以外的字母字符
\b   
\d    数字字符
\D    非数字字符
\e   
\f    由isfname选项定义的文档名字符
\F    文档名字符,但是不包含数字
\h    单词的头字符(A-Za-z)
\H    不是单词的头字符(A-Za-z)
\i    由isdent选项定义的字符
\I    定义的字符,但是不包括数字
\k    由iskeyword选项定义的关键字字符
\K    关键字字符,但是不包括数字
\l    小字字符(a-z)
\L    非小写字符(除了a-z以外的字符)
\o    八进制数字
\O    非八进制数字
\p    由isprint选项定义的可打印字符
\P    可打印字符,但是不包括数字
\r   
\s    空白符和
\S    非空白符
\t   
\u    大写字母字符(A-Z)
\U    非大写字母字符
\w    单词字符(0-9A-Za-z)
\W    非单词字符
\x    十六进制数字
\X    非十六进制数字
\~    匹配最后指定的字符串
修饰符:
*    匹配0次或是多次,尽可能多的匹配
\+    匹配1次或是多次,尽可能多的匹配
\=    匹配0次或是1次
\{}    匹配0次或是多次
\{n}
\{-n}    匹配n次
\{n,m}    匹配n次到m次
\{n,}    匹配n次到多次
\{,m}    匹配0次到m次
\{-n,m}    匹配n次到m次,尽可能少的进行匹配
\{-n,}    至少匹配n次,尽可能少的进行匹配
\{-,m}    匹配到m次,尽可能少的进行匹配
\{-}    匹配0次到多次,尽可能少的进行匹配
str1\|str2    匹配str1或是str2
vi编辑器的学习使用(二十)
Vim编辑器有不同的方法来处理各类事物.我们在Vim编辑器的学习使用(四)已讨论过文本块和多文档的处理方法.有了这些命令,我们就能够很好的来完成我们的工作.在这一次的学习中我们会讨论一些更多的内容.从而使得我们的Vim编辑工作来得更完美一些.

我们插入文本行的时候能够使用p命令或是P命令.所不同的是p命令是在当前行的下一行进行插入,插入后光标移动到新行的开头处,而P命令是在当前的上一行
进行插入,插入后光标移到新行的下一行的开头处.而我们还能够使用gp或是gP命令.不同的是gp命令是将光标移动到新行的结尾处,也就是新行的下一行的
开头处.gP命令和此相类似,是在当前的上一行进行插入,插入后,光标移动新行的结尾处,也就是下一行的开头处.
在Vim编辑器中更有一些特别的标记符,如单引号'是指光标上一次的位置,但是这个位置不包括由方向键移动的位置.其他的一些特别的标记包括:
]    上一次插入的文本的开头
[    上一次插入的文本的结尾
"    当我们离开文档时光标所处的位置
(注:这个地方看书是这样写的,但是自己做时却只能够是',[,],而且是要按两次键才行,不解:().

现在为止我们所做的任何的复制和删除文本时我们并没有指明我们要使用哪一个寄存器.假如我们没有指明要用哪一个寄存器,Vim就会使用默认的没有命名的寄
存器.用来指明这个寄存器的标记符是两个双引号("").在这里前一个双引号用来指示一个寄存器,而后一个双引号则是这个寄存器的名字.这样"a就是指我
们使用a寄存器.
我们能够在我们的复制文本或是删除文本这前来指明我们的所复制或是删除的文本要放在哪一个寄存器中,指明寄存器的命令格式如下:
"register
在这里register是个小写字母,是我们所指定的寄存器的名字,这样我们就能够有26个寄存器能够使用.
在通常的情况下我们使用yy所复制的文本是被放在没有命名的那个寄存器中,我们能够使用命令"ayy将我们所复制的文本放在指定的寄存器中.当然假如我们使用这样的命令,我们所复制的文本也会被放入未命名的寄存中的.
假如我们想知道寄存中都包含有哪些内容,我们能够使用下面的命令:
:registers
一般我们复制或是粘贴的文本会被入以字母命名的寄存器中,当然我们也能够使用一些特别的寄存器.
我们能够通过给命令:registers一个参数,我们能够来查看特定寄存器中的内容,例如我们能够用下面的命令来查看寄存器a和x中的内容:
:registers ax
当我们使用命令"ayy我们是将当前文本行的内容放入寄存器中,而当我们再使用相应的大写字母来指定寄存器时,如"Ayy我们是将当前行内容追加到寄存器"a中,这时候在这个寄存器中就存两行文本,在寄存器中^J是指一行的结束.
在Vim中更有一些特别的寄存器,第一个就是我们已知道的未命名寄存器,他的名字是个双引号".其他的更有1到9寄存器,寄存器1中含有我们上一次删除的文本,依次类推.

古老时代的Vi中,Vi只能够撤销三次.假如我们将dd命令执行了三次,也许我们就没有太多的好运来使用u命令将我们删掉的文本再恢复过来.但是幸运的是
这三个文本被分别存放在寄存器1,2,3中.我们能够通过命令"1P,"2P,"3P将这些文本再粘贴回来,或是我们能够使用下面的命令来达到同样的结
果:
""P..
其他的一些特别的寄存器:
寄存器        描述            可复写
0        上一次复制的文本    是   
-        上一次删除的文本    否
.        上一次插入的文本    否
%        当前文档的名字        否
#        交替文档的名字        否
/        上一次查找的字符串    否
:        上一次":"命令        否
_        黑洞(black hole)    是
=        表达式            否
*        由鼠标选中的文本    是
黑洞寄存器(_)(The Black Hole Register)

洞寄存器是个特别的寄存器,我们放入其中的任何文本都不复存在.当然我们也能够使用p命令来粘贴这个寄存器中的文本,但是这样做是没有意义的,因为在这
个寄存器中根本就不存在任何内容.这样的寄存器也有着相当重要的作用.假如我们想永久删除某一文本而不是将他放入1-9中某个寄存中,我们能够使用这个寄
存器.例如命令dd删除一行文本并将这一行文本存在寄存器1中,而我们用命令"_dd则是将这行文本放入黑洞寄存器中,这些文本也就会永久地消失了,而寄
存器1中的文本会保持不变.
表达式寄存器(=)(The Expression Register)
我们能够使用表达式寄存器来在文本中输入表达式.当我们输入表达式寄存器开始的命令时,就会在Vim的下部显示一个提示行,这就是给我们一个机会,我们就能够在这里来输入我们的表达式了.然后我们能够使用命令p将表达式的结果粘贴到文本.
例如我们要在文本中插入38*56的值,我们能够这样来做:
进入命令模式,输入表达式寄存器开始的命令"=,这时就会在Vim的下端显示出=来等待我们的输入,我们能够输入38*56,回车,然后输入命令p,这样就能够将我们的计算结果插入文本中了.

表达式寄存器中我们不但能够使用通常的算术运算符,还能够使用Vim特定的函数和运算符.假如我们不但仅是用常用的算术运算符来完成我们的工作,也许我们
就需要来查阅表达式文档了.例如我们能够通过表达式寄存器来得到环境变量的值.假如我们输入"=$HOME我们就能够得到HOME变量的值了.
剪切板寄存器(*)(The Clipboard Register)
剪切板寄存器能够使得我们通过系统的剪切板来读写数据.假如是在UNIX系统上,这个要在图像界面下使用.这样我们就能够通过剪切板寄存器来在不同的Vim编辑器或是其他的程式包之间进行文本的剪切和复制.

们在UNIX或是Linux系统中能够使用Vim编辑器和grep命令处理包含某一个指定词的任何文档.这对于我们有着极大有用处,因为我们能够用这个组
合来查看或是处理包含某一个变量的程式文档.例如现在我们要编辑包含有变量frame_counter的C程式文档.这时我们就能够使用下面的命令:
$ vim `grep -l 'frame_counter' *.c`
(注:在这里最外面的是反外号,即数字键1旁边的,而里面的是单引号)

这个命令中,grep命令在一系列文档查找包含有指定单词的文档.在这里我们指定-l选项,这样这个命令就会列出包含有这个单词的文档,而不会打印找到的
字符串.在这里我们要查找的字符串frame_counter,我们能够使用这个命令来查找其他的字符串或是表达式.而整个的命令由反引号包括起来.这就
能够告诉UNIX
Shell来运行这个命令,并假定认为这个命令的执行已由命令打印输出.所以这个命令的执行结果就是运行grep命令并产生一个文档的列表,这些文档名放
在Vim的命令行中,这样Vim就能够来编辑这些文档了.
也许有的人会说为什么要在这里展现这个命令呢?这是UNIX Shell的特征而不是Vim作品的一部分.而这正是使得Vim更加完美.
我们能够用下面的命令来配置参数列表:
:arg `grep -l 'frame_counter' *.c`
在这个参数列表中我们能够用命令来指定我们想要编辑的文档,例如假如我们要编辑第三个文档我们能够用下面的命令:
:argument 3
这个命令能够使得我们用文档在参数列表中的位置来编辑这个文档.假如我们是用下面的命令来启动Vim的:
$ gvim one.c two.c three.c four.c five.c
这时假如我们要编辑第四个文档我们能够用下面的命令:
:argument 4
当我们在命令行中用命令来指定一系列文档时,我们完成文档列表的初始化工作.但是我们能够用命令:args来指定新的文档列表.例如下面的命令:
:args alpha.c beta.c gamma.c
执行完毕这个命令以后我们开始编辑文档alpha.c,然后是文档beta.c.

时我们在编辑文档时希望文档在打开后光标能定位我们希望在的行,这我们该如何来做到呢?例如我们要第12行开始编辑文档,我们能够用命令打开这个文档,然
后执行命令12G来使得光标到指定的行.我们也能够在vim启动时在命令后面加上行号来指明光标要到的地方.如下面的命令:
$ gvim +12 file.c
我们还能够用这样的命令形式+/string来使得文档在装入以后光标放在指定的字符串.例如我们希望文档在打开后光标放在第一个包含字符#include处,我们能够用下面这样的命令:
$ gvim +/#include file.c
当然我们能够在+后面放上任何命令模式的命令.
我们能够用多种多样的命令来指明+cmd的参数.通常:vi命令格式如下:
:vi [+cmd] {file}
下面的这些命令也能够带上+cmd的形式:
:next [+cmd]
:wnext [+cmd]
:previous[+cmd]
:wprevious [+cmd]
:Next[+cmd]
:wNext[+cmd]
:rewind[+cmd]
:last[+cmd]

记符a-z只是当前文档的标记.换句话说,我们能够在一个文档标记a,也能够在另外一个文档中标记a.假如我们执行命令'a我们就能够跳转到当前文档中的
a标记处.而大写字母(A-Z)的标记符则就不相同了.他不但标记了当前文档中的某一行而且也标记当前文档.例如我们正在编辑文档one.c而且在其中用
A做了一处标记.然后当我们在编辑文档two.c时我们就能够执行命令'A,这样就能够跳转到one.c文档中的标记处
当我们用插入模式进入文本
时我们也能够执行种不同的命令.例如能够清除光标前面的字符,而CTRL-U会清除整个一行或
至少是您刚输入的内容,CTRL-W会清除光标前面的字符.当我们在插入模式时我们也能够来移动光标,这时我们不能够再用传统的h,j,k,l,但是这时
我们能够使用小方向键来移动光标.能够移动到一行的开头,能够移动到一行结尾.能够向上翻屏,能够向下翻屏.
在插入模式下假如我们输入CTRL-A,Vim编辑器就会插
入我们上一次在Vim插入状态时所输入的文本.例如假如我们在文档中一行里输入#include,然后我们用命令j向下移动一行并插入新的一行,这时假如
我们想输入#include,能够用命令CTRL-A来完成.CTRL-@命令和其相类似,所不同的只是在执行完这个命令后会退出插入模式.我们还能够用
命令CTRL-V来引用下一个字符,换句话说任何有着特别意义的字符他都将被忽略掉.例如假如我们输入CTRL-V会在文本中插入
空白符.我们也能够用命令CTRL-Vdigits来插入这个数字所指的字符,例如假如我们输入CTRL-V64就会在文本中插入@.在默认的情况下
CTRL-V是使用十进制的数字,但是我们也能够插入十六进制的数字.
CTRL-Y命令能够输入光标上面的字会,当我们想要重复上一行时这个命令就会显得尤为有用.
和CTRL-Y命令相似的是CTRL-E命令,不同的是这个命令可能重复光标下面的字符.

令CTRL-Rregister能够使得我们寄存器中的内容插入文本中.假如寄存器的内容含有类似于这样的特别字符时,这些特别会被
重样解释,就像我们从键盘中输入这些字符相同.假如我们想这样,只是想着将这些字符作为普通字符业对待,我们能够用这样的命令:CTRL-R
CTRL-R register
例如我们输入下面的文本:
This is a test.
然后我们用命令"ayy将这些文本复制到寄存器a中,完成以后我们进入插入模式,输入命令CTRL-Ra,我们就会刚才我们复制的内容就会出现在文本行了.
假如我们不希望特别字符被重新解释我们能够用命令CTRL-R CTRL-R register.
命令CTRL-\CTRL-N能够离开插入模式而回到正常模式下.换句话这个命令和相类似,但这个命令能够在任何模式下使用,就这一点而似乎是要比命令强大一些啊.
最后要介绍的就是CTRL-O命令,这个命令能够回到命令模式执行Vim命令在执行完以后又回到了插入模式.例如我们执行命令CTRL-Odw,Vim就会回到命令模式执行命令dw,在执行完毕后又回到了插入模式.
我们在使用全局标记时碰到的一个问题就是当我们退出Vim以后我们所设定的那些全局变量就不在存在.而我们希望能将这些保存下来.文档viminfo就是设计成为一个用保存标记信息连同下面的一些信息的文档:
命令行历史
查找历史
输入历史
寄存器
标记
缓冲区列表
全局变量
保存某一项的办法是我们要打开这个保存的选项,我们能够下面的命令来做到:
:set viminfo=string
这里的string表明了我们要保存的内容.
这里的string的语法是个选项字符跟上一个参数.中间用逗号来分隔.首先'option用来表明我们要为少文档保存局部标记,我们能够选一个合适的数字,例如我们要选1000,那么这时的命令形式如下:
:set viminfo-'1000
而f选项则用来控制是否要保存全局标记.假如这个选项的值为0,则不保存,假如值为1或是我们没有指明f选项,那么vim都会保存这些全局标记.假如我们要用这一特征,我们能够用下面的命令:
:set viminfo='1000,f1

项r会告诉Vim一些关于可移动介质的情况.在Vim中为可移动介质上的文档所做标记是不会被保存的,原因就是我们在Vim中要跳转到软盘文档的标记处是
一个很难的操作.我们多次来表明r选项.假如我们在Windows系统上就能够用下面的命令来告诉Vim磁盘A和B是可移动介质:
:set viminfo='1000,f1,rA:,rB:
在UNIX系统并没有标准的软盘名称.在一般的情况下我们总是将软盘挂载在/mnt/floppy文档下,所以我们能够用下面的命令:
:set viminfo='1000,f1,r/mnt/floppy
\"选项用来控制每一个寄存器能够保存多少行.在默认的情况下,任何的行都会被保存.假如是0就不会保存.一般情况默认的配置已能够很好的来满足我们的需要了,所以我们能够不必加入我们自己的配置.
:选项能够用来控制:历史行的记录.我想100应是能够满足我们的需要了吧:-):
:set viminfo='1000,f1,r/mnt/floppy,:100
/选项能够用来定义查找历史记录的大小,100也已是很充足的了:
:set viminfo='1000,f1,r/mnt/floppy,:100,/100
(注:Vim不会保存那些超过他的记录能力的内容,这是在选项history中配置的)
通常,当我们启动Vim以后假如我们已配置了hlsearch选项,编辑器就会高亮显示上一次查找的内容.要关闭这个选项,我们能够viminfo的选项列表中加入h标记,或是我们在Vim启动后执行命令:nohlsearch来关闭这个选项.
@选项用来控制输入历史记录的行数.输入历史会记住我们输入的任何内容.
假如我们配置了%选项,我们就能够保存缓冲区列表记录.假如我们没有在命令行指定一个文档来编辑,缓冲区列表就会被重新保存:
:set viminfo='1000,f1,r/mnt/floppy,:100,/100,%
!选项用保存全局变量,这些全局是名字全部为大写字母的变量:
:set viminfo='1000,f1,r/mnt/floppy,:100,/100,%,!
最后n选项用来指明viminfo的文档名.在UNIX系统中默认的为$HOME/.viminfo
我们能够将这些命令连同一些其他的初始化命令放在初始文档vimrc中.这样viminfo文档就会在Vim退出时自动保存,启动时来读取初始化.
假如我们要更清楚一些的来保存和读取这个文档,我们能够用下面的命令来以另外的文档名保存:
:wviminfo[file]
这样我们的这一些配置信息就会被写入这个文档中了.
我们能够用下面的命令来读取这个文档:
:rviminfo [file]
这样就会从这个文档中读入配置信息.假如这些信息和当前的配置有冲突,那么我们的这些配置信息就不再起作用了.我们能够用下面的命令来强制这些配置信息起作用:
:rviminfo![file]
有时我们会编辑一些文档中一行的宽度要超过屏幕的宽度.当行宽超过屏幕的宽度会发生什么事情呢?这时vim会将这一行进行回折以适应屏幕的宽度.假如我们配置了nowrap选项,则Vim会用单行来处理文档的第一行文本.这时超出屏幕的部分就会不再出现,从而从屏幕消失.
在默认的情况下,Vim并不会显示水平滚动条,我们能够用下面的命令来使得Gvim显示小平滚动条:
:set guioptions+=b
这样Gvim就能够水平滚动了.
当我们配置了nowrap选项后,命令^将光标移动当前行的第一个非空字符处.g^命令能够将光标移动到当前屏幕的第一个非空字符处.在执行这们执行这样的命令时假如在窗口的其他部分有文本,那么这一部分的文本将会被忽略.类似的一些命令如下:
命令        命令        含义
^        g^        向左移动当前屏幕的第一个非空字符处
        g
0        g0        向左移动当前屏幕的第一个字符处
        g
$        g$        向右移动当前屏幕的结尾处
        gm        移动到屏幕的中间
命令count|可将光标移动屏幕中指定的列.
命令countzh能够向左移动屏幕,移动的量度为count个字符.而命令countzl和其相类似,只是这个命令是向右移动屏幕.
命令zH能够向左移动个屏幕,而命令zL能够向右移到半个屏幕.命令j或是能够下移一行.在这里我们要知道的就是假如我们配置了wrap选项,那么下移一行在屏幕上显示也许就会是几行,这时我们要清楚,此时的几行正是配置了nowrap时的一行.
当我们配置了wrap选项,一个很长的一行Vim就会折成几行显示在屏幕上,这时我们能够用命令gj或是g来下移屏幕屏幕中显示的一行,这时也许我们移动的并非是真正的一行.而命令gk或是g命令和其相类似.
在默认的情况下,Vim编辑时会折回很长的一行,这时他首先是尽可能多的在屏幕的第一行放置文本,假如这一行的文本超出了屏幕的范围,Vim就会将其打断,然后在屏幕中的下一行显示其余的部分.我们也能够通过下面的命令来并闭这个选项:
:set nowrap
这时一个很长的句子超出屏幕的部分就在从屏幕上消失.我们能够通过沿着这个句子来移动光标,这样屏幕就会进行水平滚动,我们就能够看到这一行的其余部分了.
当然了我们也能够来自定义我们自己的句子回折形式:
首先我们能够告诉Vim在合适的地方来打断一个句子.我们能够用下面的命令来实现:
:set linebreak
那么又如何来定义一个合适的地方呢?这个是由breakat选项中的字符来确定的.在默认的情况下这些默认的字符是
[email=%5EI%21@*-+_;:,./]^I!@*-+_;:,./[/email]
?假如我们不希望在下划线_处打断句子,我们能够用下面的命令来将_从这个列表移除就能够了:
:set breakat-=_
在通常的情况下,假如一个句子被打断Vim是不会在句子的连接的地方显示任何内容的.我们能够通过配置showbreak选项来显示我们所希望显示的内容信息:
:set showbreak="->"
我们最后要讨论的一个问题就当我们要在屏幕的结尾处打断一个句子我们应如何来做呢?这时我们能够用两个选择:一是我们能够不显示半行.这时Vim编辑器会在屏幕的底部来显示@以表时这是个长句子,我们不能把他全部放在屏幕内.二是我们能够显示半行.
Vim默认的是采用第一种方法.假如我们要采用第二种方法我们能够用下面的命令来实现:
:set display=lastline
               
               

RPM简明中文手册


RPM是RedhatPackageManager,即redhat包管理器,rpm是一个功能强大的包管理器,可以创建,安装,查询,更改,升级和卸载软件包,一个包包含了文件的架构,包信息,名字,版本号和包的描述


更详细的信息,以自己系统上的rpm的manual为准 一、安装




命令格式:



rpm -i ( or --install) options file1.rpm ... fileN.rpm



参数:



file1.rpm ... fileN.rpm 将要安装的RPM包的文件名



详细选项:



-h (or --hash) 安装时输出hash记号 (``#'')


--test 只对安装进行测试,并不实际安装。


--percent 以百分比的形式输出安装的进度。


--excludedocs 不安装软件包中的文档文件


--includedocs 安装文档


--replacepkgs 强制重新安装已经安装的软件包


--replacefiles 替换属于其它软件包的文件


--force 忽略软件包及文件的冲突


--noscripts 不运行预安装和后安装脚本


--prefix 将软件包安装到由 指定的路径下


--ignorearch 不校验软件包的结构


--ignoreos 不检查软件包运行的操作系统


--nodeps 不检查依赖性关系


--ftpproxy 用 作为 FTP代理


--ftpport 指定FTP的端口号为



通用选项



-v 显示附加信息


-vv 显示调试信息


--root 让RPM将指定的路径做为"根目录",这样预安装程序和后安


装程序都会安装到这个目录下


--rcfile 设置rpmrc文件为


--dbpath 设置RPM 资料库存所在的路径为



二、删除



命令格式:



rpm -e ( or --erase) options pkg1 ... pkgN



参数



pkg1 ... pkgN :要删除的软件包



详细选项



--test 只执行删除的测试


--noscripts 不运行预安装和后安装脚本程序


--nodeps 不检查依赖性



通用选项



-vv 显示调试信息


--root 让RPM将指定的路径做为"根目录",这样预安装程序和后安装


程序都会安装到这个目录下


--rcfile 设置rpmrc文件为


--dbpath 设置RPM 资料库存所在的路径为



三、升级



命令格式



rpm -U ( or --upgrade) options file1.rpm ... fileN.rpm



参数



file1.rpm ... fileN.rpm 软件包的名字



详细选项



-h (or --hash) 安装时输出hash记号 (``#'')


--oldpackage 允许"升级"到一个老版本


--test 只进行升级测试


--excludedocs 不安装软件包中的文档文件


--includedocs 安装文档


--replacepkgs 强制重新安装已经安装的软件包


--replacefiles 替换属于其它软件包的文件


--force 忽略软件包及文件的冲突


--percent 以百分比的形式输出安装的进度。


--noscripts 不运行预安装和后安装脚本


--prefix 将软件包安装到由 指定的路径下


--ignorearch 不校验软件包的结构


--ignoreos 不检查软件包运行的操作系统


--nodeps 不检查依赖性关系


--ftpproxy 用 作为 FTP代理


--ftpport 指定FTP的端口号为



通用选项



-v 显示附加信息


-vv 显示调试信息


--root 让RPM将指定的路径做为"根目录",这样预安装程序和后安装程序都会安装到这个目录下


--rcfile 设置rpmrc文件为


--dbpath 设置RPM 资料库存所在的路径为



四、查询



命令格式:



rpm -q ( or --query) options



参数:



pkg1 ... pkgN :查询已安装的软件包



详细选项



-p (or ``-'') 查询软件包的文件


-f 查询属于哪个软件包


-a 查询所有安装的软件包


--whatprovides 查询提供了 功能的软件包


-g 查询属于 组的软件包


--whatrequires 查询所有需要 功能的软件包



信息选项



显示软件包的全部标识


-i 显示软件包的概要信息


-l 显示软件包中的文件列表


-c 显示配置文件列表


-d 显示文档文件列表


-s 显示软件包中文件列表并显示每个文件的状态


--scripts 显示安装、卸载、校验脚本


--queryformat (or --qf) 以用户指定的方式显示查询信息


--dump 显示每个文件的所有已校验信息


--provides 显示软件包提供的功能


--requires (or -R) 显示软件包所需的功能



通用选项



-v 显示附加信息


-vv 显示调试信息


--root 让RPM将指定的路径做为"根目录",这样预安装程序和后安装程序都会安装到这个目录下


--rcfile 设置rpmrc文件为


--dbpath 设置RPM 资料库存所在的路径为



五、校验已安装的软件包



命令格式:



rpm -V ( or --verify, or -y) options



参数



pkg1 ... pkgN 将要校验的软件包名



软件包选项



-p Verify against package file


-f 校验所属的软件包


-a Verify 校验所有的软件包


-g 校验所有属于组 的软件包



详细选项



--noscripts 不运行校验脚本


--nodeps 不校验依赖性


--nofiles 不校验文件属性



通用选项



-v 显示附加信息


-vv 显示调试信息


--root 让RPM将指定的路径做为"根目录",这样预安装程序和后安装程序都会安装到这个目录下


--rcfile 设置rpmrc文件为


--dbpath 设置RPM 资料库存所在的路径为



六、校验软件包中的文件



语法:



rpm -K ( or --checksig) options file1.rpm ... fileN.rpm



参数:



file1.rpm ... fileN.rpm 软件包的文件名



Checksig--详细选项



--nopgp 不校验PGP签名



通用选项



-v 显示附加信息


-vv 显示调试信息


--rcfile 设置rpmrc文件为




七、其它RPM选项



--rebuilddb 重建RPM资料库


--initdb 创建一个新的RPM资料库


--quiet 尽可能的减少输出


--help 显示帮助文件


--version 显示RPM的当前版本

typedef的四个用途和两个陷阱

用途一:
定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:
char* pa, pb;  // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针, 
// 和一个字符变量;
以下则可行:
typedef char* PCHAR;  // 一般用大写
PCHAR pa, pb;        // 可行,同时声明了两个指向字符变量的指针
虽然:
char *pa, *pb;
也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。

用途二:
用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名 对象名,如:
struct tagPOINT1
{
    int x;
    int y;
};
struct tagPOINT1 p1; 

而在C++中,则可以直接写:结构名 对象名,即:
tagPOINT1 p1;

估计某人觉得经常多写一个struct太麻烦了,于是就发明了:
typedef struct tagPOINT
{
    int x;
    int y;
}POINT;

POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候

或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。

用途三:
用typedef来定义与平台无关的类型。
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double REAL; 
在不支持 long double 的平台二上,改为:
typedef double REAL; 
在连 double 都不支持的平台三上,改为:
typedef float REAL; 
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。

用途四:
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:

1. 原声明:int *(*a[5])(int, char*);
变量名为a,直接用一个新别名pFun替换a就可以了:
typedef int *(*pFun)(int, char*); 
原声明的最简化版:
pFun a[5]; 

2. 原声明:void (*b[10]) (void (*)());
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];

3. 原声明:doube(*)() (*e)[9]; 
变量名为e,先替换左边部分,pFuny为别名一:
typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef pFuny (*pFunParamy)[9];
原声明的最简化版:
pFunParamy e; 

理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
int (*func)(int *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明 (*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
int (*func[5])(int *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰 func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的 元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。

也可以记住2个模式:
type (*)(....)函数指针 
type (*)[]数组指针 
---------------------------------

陷阱一:
记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:
先定义:
typedef char* PSTR;
然后:
int mystrcmp(const PSTR, const PSTR);

const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

陷阱二:
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef static int INT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。

 

使用示例:

 

1.比较一:

 

#include <iostream>

using namespace std;

 

typedef int (*A) (char, char);

 

int ss(char a, char b)

{

    cout<<"功能1"<<endl;

    cout<<a<<endl;

    cout<<b<<endl;

    return 0;

}

 

int bb(char a, char b)

{

    cout<<"功能2"<<endl;

    cout<<b<<endl;

    cout<<a<<endl;

    return 0;

}

 

void main()

{

    A a;

    a = ss;

    a('a','b');

    a = bb;

    a('a', 'b');

}

 

2.比较二:

 

typedef int (A) (char, char);

 

void main()

{

    A *a;

    a = ss;

    a('a','b');

    a = bb;

    a('a','b');

}

 

两个程序的结果都一样:

功能1

a

b

功能2

b

a

 

 

*****以下是参考部分*****

 

参考自:http://blog.hc360.com/portal/personShowArticle.do?articleId=57527

 

typedef #define的区别:

 

案例一:

 

通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:

typedef char *pStr1;

#define pStr2 char *;

pStr1 s1, s2;

pStr2 s3, s4;

 

在上述的变量定义中,s1s2s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。

 

 

案例二:

 

下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?

typedef char * pStr;

char string[4] = "abc";

const char *p1 = string;

const pStr p2 = string;

p1++;

p2++;

 

  是p2++出错了。这个问题再一次提醒我们:typedef#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2const pStr p2const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。

製作patch, 使用patch的方法


製作patch
diff -Naur olddir newdir > new-patch



製作patch要注意的事項
記得要讓前置的目錄階層數有相同的深度,例如:

--- old/modules/pcitable Mon Sep 27 11:03:56 1999
+++ new/modules/pcitable Tue Dec 19 20:05:41 2000

上面用diff產生的結果,可以給patch使用

--- old/try1/other/modules/pcitable Mon Sep 27 11:03:56 1999
+++ new/modules/pcitable Tue Dec 19 20:05:41 2000

但是上面產生的結果在使用上可能會發生問題,第一行目錄的深度有四層,而第二行只有兩層. 這裡建議直接修改 ---和+++後面的內容讓目錄結構有相同的深度.

使用Patch命令
patch -p0 < new-patch
patch -p1 < new-patch

這兩個命令的差異在於目前工作目錄的不同

另外一種用法是讓patch 從標準輸入讀取資料:

cat new-patch | patch -p0

還有一種用法是讓patch 讀取 "here document"的方式

( 省略此種方法之舉例 )

Patch指令的階層 ( -p0 和 -p1 的差別 )

假設patch的表頭如下

--- old/modules/pcitable Mon Sep 27 11:03:56 1999
+++ new/modules/pcitable Tue Dec 19 20:05:41 2000

使用 -p0 , patch會期待在你目前的工作目錄下有一個名為new的目錄, 接著會有modules目錄, 然後是pictable目錄

使用 -p1 , 路徑的第一層會被拿掉, 也就是patch期待工作目錄下有 modules目錄, 然後是pictable目錄

-p2, -p3, .... 以此類推


Patch使用方法

Patch表頭
開頭是 ---, +++, 並且接著被比較的檔案

同個patch檔包含多個patch

瀏覽patch檔的方法

less /tmp/file-patch
/---
n
n
n

( /--- 會搜尋---,而n會搜尋下個出現的--- )

Hunk
每個patch的段落稱作 Hunk,每個段落皆以兩個老鼠記號 ( @@ ) 開始, 直至下個段落開始,或者下個patch header為止

vim


[Undo]
u

[redo]
ctrl + r

[移动到指定字符]
"fx"在当前行上查找下一个字符x
助记:
一个命令的大写字母形式会做同样的事情,但是方向相反
f   find
F   反方向f
t   to
T   反方向

[以匹配一个括号为目的的移动]
% 适用于[]{}()


[自动存盘]
:set autowrite
不自动存盘
:set noautowrite

[只读查看一个文件]
vim -r file
view file

[save as...]
:saveas new_file_name

[关闭窗口]
:close
比quit和ZZ的好处是不会关闭最后一个窗口

[关闭除当前窗口外的其它所有窗口]
:only

[切换窗口]
CTRL-W h 到左边的窗口
CTRL-W j 到下面的窗口
CTRL-W k 到上面的窗口
CTRL-W l 到右边的窗口
CTRL-W t 到顶部窗口
CTRL-W b 到底部窗口


[替换]
在一行内替换头一个字符串 old 为新的字符串 new,请输入 :s/old/new
在一行内替换所有的字符串 old 为新的字符串 new,请输入 :s/old/new/g
在两行内替换所有的字符串 old 为新的字符串 new,请输入 :#,#s/old/new/g
在文件内替换所有的字符串 old 为新的字符串 new,请输入 :%s/old/new/g
进行全文替换时询问用户确认每个替换需添加 c 选项,请输入 :%s/old/new/gc

删除空行

 

grep -v ^$ oldfile > newfile

 

vim的命令为:%s/^\n//g

如果有多个连续的空行,想保留一个
vim的命令为:%s/^\n$//g

删除多余的空格
行末:$     
行首:^
空格:\s
行末空格:\s\+$
行首空格:^\+\s
有些人认为行末的空格是无用,浪费而难看的。要删除这些每行后面多余的空格,可以
执行如下命令:
        :%s/\s\+$//

 

刪除沒有內容的空行
g/^$/d

刪除包含有空格組成的空行
g/^\s*$/d

除以空格或tab開頭到結尾的空行
g/^[ |\t]*$/d

 

$@与$*的区别

$cat test.sh
  for   i   in   "$@";   do   echo   $i;done 
  for   i   in   "$*";   do   echo   $i;done  

$./test.sh   “abc   efg”   hij 

输出:

abc efg
hij
abc efg hij
 

可看出$*是对整个参数表的引用