Jun
06
 

  施聪
高级程序员、网络设计师
2005 年 4 月

   BerkeleyDB是历史悠久的嵌入式数据库系统,主要应用在UNIX/LINUX操作系统上,其设计思想是简单、小巧、可靠、高性能。本文是对DB开 发的一个入门级指南,重点讨论了DB的核心数据结构和数据访问算法,并通过实际的代码演示如何使用DB。最后有一个对DB的简单总结,并提出作者对工具选 择的一些感想。
前言
UNIX/LINUX平台下的数据库种类非常多,参考资料1中 列举了其中的大部分。通常,我们在设计UNIX/LINUX平台下的应用软件时,如果数据种类繁多,数据与数据之间关系比较复杂,就会选用一些大型的企业 级数据库系统,如DB2,ORACLE、SYBASE等,如果软件规模不大,就倾向选用如MYSQL、POSTGRESQL等中小型数据库。例如使用 PHP/PERL +MYSQL/POSTGRESQL设计网站基本上是一个很常规的做法。但是,当应用软件管理的数据类型较少(特别注意:这并不是说需要管理的数据量 小),数据管理本身不复杂,且对数据操作要求高效率,则由大名鼎鼎的Berkeley(美国加州大学伯克利分校)开发的 BerkeleyDB可能是一个很明智的选择。

DB综述
DB最初开发的目的是以新的HASH访问算法来代替旧的hsearch函数和大量的dbm实现(如AT&T的dbm,Berkeley的 ndbm,GNU项目的gdbm),DB的第一个发行版在1991年出现,当时还包含了B+树数据访问算法。在1992年,BSDUNIX第4.4发行版 中包含了DB1.85版。基本上认为这是DB的第一个正式版。在1996年中期,Sleepycat软件公司成立,提供对DB的商业支持。在这以后,DB得到了广泛的应用,当前最新版本是4.3.27。

DB支持几乎所有的现代操作系统,如LINUX、UNIX、WINDOWS等,也提供了丰富的应用程序接口,支持C、C++、JAVA、PERL、TCL、PYTHON、PHP等。DB的应用十分广泛,在很多知名的软件中都能看到其身影。例如参考资料2中作者谈到利用DB在LINUX下实现内核级文件系统;参考资料3中通过实际测试数据说明DB提高了OPENLDAP的效率。LINUX下的软件包管理器RPM也使用DB管理软件包相关数据,可以使用命令file查看RPM数据目录/var/lib/rpm下的文件,则有形式如下的输出:

Dirnames: Berkeley DB (Btree, version 9, native byte-order)
Filemd5s: Berkeley DB (Hash, version 8, native byte-order)

值得注意的是DB是嵌入式数据库系统,而不是常见的关系/对象型数据库,对SQL语言不支持,也不提供数据库常见的高级功能,如存储过程,触发器等。
DB的设计思想
DB 的设计思想是简单、小巧、可靠、高性能。如果说一些主流数据库系统是大而全的话,那么DB就可称为小而精。DB提供了一系列应用程序接口(API),调用 本身很简单,应用程序和DB所提供的库在一起编译成为可执行程序。这种方式从两方面极大提高了DB的效率。第一:DB库和应用程序运行在同一个地址空间, 没有客户端程序和数据库服务器之间昂贵的网络通讯开销,也没有本地主机进程之间的通讯;第二:不需要对SQL代码解码,对数据的访问直截了当。

DB对需要管理的数据看法很简单,DB数据库包含若干条记录,每一个记录由关键字和数据(KEY/VALUE)构成。数据可以是简单的数据类型,也可以是 复杂的数据类型,例如C语言中结构。DB对数据类型不做任何解释,完全由程序员自行处理,典型的C语言指针的"自由"风格。如果把记录看成一个有n个字段 的表,那么第1个字段为表的主键,第2--n个字段对应了其它数据。DB应用程序通常使用多个DB数据库,从某种意义上看,也就是关系数据库中的多个表。 DB库非常紧凑,不超过500K,但可以管理大至256T的数据量。

DB的设计充分体现了UNIX的基于工具的哲学,即若干简单工具的组合可以实现强大的功能。DB的每一个基础功能模块都被设计为独立的,也即意味着其使用 领域并不局限于DB本身。例如加锁子系统可以用于非DB应用程序的通用操作,内存共享缓冲池子系统可以用于在内存中基于页面的文件缓冲。

DB核心数据结构
数据库句柄结构DB:包含了若干描述数据库属性的参数,如数据库访问方法类型、逻辑页面大小、数据库名称等;同时,DB结构中包含了大量的数据库处理函数指针,大多数形式为 (*dosomething)(DB *, arg1, arg2,…)。其中最重要的有open,close,put,get等函数。

数据库记录结构DBT:DB中的记录由关键字和数据构成,关键字和数据都用结构DBT表示。实际上完全可以把关键字看成特殊的数据。结构中最重要的两个字段是 void * data和u_int32_t size,分别对应数据本身和数据的长度。

数据库游标结构DBC:游标(cursor)是数据库应用中常见概念,其本质上就是一个关于特定记录的遍历器。注意到DB支持多重记录(duplicate records),即多条记录有相同关键字,在对多重记录的处理中,使用游标是最容易的方式。

数据库环境句柄结构DB_ENV:环境在DB中属于高级特性,本质上看,环境是多个数据库的包装器。当一个或多个数据库在环境中打开后,环境可以为这些数据库提供多种子系统服务,例如多线/进程处理支持、事务处理支持、高性能支持、日志恢复支持等。

DB中核心数据结构在使用前都要初始化,随后可以调用结构中的函数(指针)完成各种操作,最后必须关闭数据结构。从设计思想的层面上看,这种设计方法是利用面向过程语言实现面对对象编程的一个典范。


DB数据访问算法
在数据库领域中,数据访问算法对应了数据在硬盘上的存储格式和操作方法。在编写应用程序时,选择合适的算法可能会在运算速度上提高1个甚至多个数量级。大 多数数据库都选用B+树算法,DB也不例外,同时还支持HASH算法、Recno算法和Queue算法。接下来,我们将讨论这些算法的特点以及如何根据需 要存储数据的特点进行选择。

B+树算法:B+树是一个平衡树,关键字有序存储,并且其结构能随数据的插入和删除进行动态调整。为了代码的简单,DB没有实现对关键字的前缀码压缩。B+树支持对数据查询、插入、删除的常数级速度。关键字可以为任意的数据结构。

HASH算法:DB中实际使用的是扩展线性HASH算法(extended linear hashing),可以根据HASH表的增长进行适当的调整。关键字可以为任意的数据结构。

Recno算法: 要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。实际上,这和关系型数据库中逻辑主键通常定义为int AUTO型是同一个概念。Recho建立在B+树算法之上,提供了一个存储有序数据的接口。记录的长度可以为定长或不定长。

Queue算法:和Recno方式接近, 只不过记录的长度为定长。数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。

对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择B+树或HASH算法,如果关键字为逻辑记录号,则应该选择Recno或Queue算法。 当工作集关键字有序时,B+树算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。Queue算法只能存储定长的记录,在高的 并发处理情况下,Queue算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把数据存储为平面文件格式。


DB常用函数使用范例



#include <db.h>

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

/* DB的函数执行完成后,返回0代表成功,否则失败 */

void print_error(int ret)

{

 if(ret != 0)

                printf("ERROR: %s\n",db_strerror(ret));

}

/* 数据结构DBT在使用前,应首先初始化,否则编译可通过但运行时报参数错误  */

void init_DBT(DBT * key, DBT * data)

{

 memset(key, 0, sizeof(DBT));

        memset(data, 0, sizeof(DBT));

}

void main(void)

{

      DB *dbp;

 DBT key, data;

      u_int32_t flags;

  int ret;

        char *fruit = "apple";

      int number = 15;

    typedef struct customer

     {

           int  c_id;

          char name[10];

              char address[20];

           int  age;

   } CUSTOMER;

 CUSTOMER cust;

     int key_cust_c_id = 1;

  cust.c_id = 1;

      strncpy(cust.name, "javer", 9);

     strncpy(cust.address, "chengdu", 19);

       cust.age = 32;

  /* 首先创建数据库句柄 */

    ret = db_create(&dbp, NULL, 0);

 print_error(ret);

    /* 创建数据库标志 */

        flags = DB_CREATE;

  /* 创建一个名为single.db的数据库,使用B+树访问算法,本段代码演示对简单数据类型的处理 */

     ret = dbp->open(dbp, NULL, "single.db", NULL, DB_BTREE, flags, 0);

      print_error(ret);

       init_DBT(&key, &data);

    /* 分别对关键字和数据赋值和规定长度 */

      key.data = fruit;

   key.size = strlen(fruit) + 1;

       data.data = &number;

    data.size = sizeof(int);

        /* 把记录写入数据库中,不允许覆盖关键字相同的记录 */

        ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);

  print_error(ret);

/* 手动把缓存中的数据刷新到硬盘文件中,实际上在关闭数据库时,数据会被自动刷新 */

dbp->sync();

init_DBT(&key, &data);

      key.data = fruit;

   key.size = strlen(fruit) + 1;

    /* 从数据库中查询关键字为apple的记录 */

     ret = dbp->get(dbp, NULL, &key, &data, 0);

       print_error(ret);

       /* 特别要注意数据结构DBT的字段data为void *型,所以在对data赋值和取值时,要做必要的类型转换。 */

     printf("The number = %d\n", *(int*)(data.data));

    if(dbp != NULL)

             dbp->close(dbp, 0);

 ret = db_create(&dbp, NULL, 0);

 print_error(ret);

       flags = DB_CREATE;

  /* 创建一个名为complex.db的数据库,使用HASH访问算法,本段代码演示对复杂数据结构的处理 */

    ret = dbp->open(dbp, NULL, "complex.db", NULL, DB_HASH, flags, 0);

      print_error(ret);

       init_DBT(&key, &data);

  key.size = sizeof(int);

     key.data = &(cust.c_id);

    data.size = sizeof(CUSTOMER);

       data.data = &cust;

  ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);

   print_error(ret);

    memset(&cust, 0, sizeof(CUSTOMER));

    key.size = sizeof(int);

     key.data = &key_cust_c_id;

  data.data = &cust;

      data.ulen = sizeof(CUSTOMER);

      data.flags = DB_DBT_USERMEM;

    dbp->get(dbp, NULL, &key, &data, 0);

     print_error(ret);

    printf("c_id = %d name = %s address = %s age = %d\n",

              cust.c_id, cust.name, cust.address, cust.age);

    if(dbp != NULL)

             dbp->close(dbp, 0);

}

DB游标使用范例
游标是依赖于数据库句柄的,应用程序代码框架如下:



     /* 定义一个游标变量 */

      DBC * cur;

  /* 首先打开数据库,再打开游标 */

    dbp->open(dbp, ……);

    dbp->cursor(dbp, NULL, &cur, 0);

    /* do something with cursor */

  /* 首先关闭,在关闭数据库 */

        cur->c_close(cur);

       dbp->close(dbp, 0);

      

在游标打开后,可以以多种方式遍历特定记录。



             Memset(&key, 0, sizeof(DBT));

   Memset(&data, 0, sizeof(DBT));

      /* 因为KEY和DATA为空,则游标遍历整个数据库记录 */

   While((ret = cur->c_get(cur, &key, &data, DB_NEXT)) == 0)

        {

           /* do something with key and data */

        }

   

当想查询特定关键字对应的记录,则应对关键字赋值,并把cur->c_get()函数中标志位设置为DB_SET。例如:



             key.data = "xxxxx";

 key.size =  XXX;

    While((ret = cur->c_get(cur, &key, &data, DB_SET)) == 0)

 {

           /* do something with key and data */

        }

   

游标的作用还有很多,如查询多重记录,插入/修改/删除记录等。

DB环境使用范例
本文前面已说明环境是DB数据库的包装器,提供多种高级功能。应用程序代码框架如下:



             /* 定义一个环境变量,并创建 */

      DB_ENV *dbenv;

      db_env_create(&dbenv, 0);

/* 在环境打开之前,可调用形式为dbenv->set_XXX()的若干函数设置环境 */

 /* 通知DB使用Rijndael加密算法(参考资料4)对数据进行处理 */

dbenv->set_encrypt(dbenv, "encrypt_string", DB_ENCRYPT_AES);

       /* 设置DB的缓存为5M */

dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0);

/* 设置DB查找数据库文件的目录 */

        dbenv->set_data_dir(dbenv, "/usr/javer/work_db");

    /* 打开数据库环境,注意后四个标志分别指示DB启动日志、加锁、缓存、事务处理子系统 */

  dbenv->open(dbenv,home,DB_CREATE|DB_INIT_LOG|DB_INIT_LOCK| DB_INIT_MPOOL|DB_INIT_TXN, 0);

    /* 在环境打开后,则可以打开若干个数据库,所有数据库的处理都在环境的控制和保护中。注意db_create函数的第二个参数是环境变量 */

db_create(&dbp1, dbenv, 0);

dbp1->open(dbp1, ……);

db_create(&dbp2, dbenv, 0);

dbp1->open(dbp2, ……);

 /* do something with the database */

    /* 最后首先关闭打开的数据库,再关闭环境 */

  dbp2->close(dbp2, 0);

    dbp1->close(dbp1, 0);

    dbenv->close(dbenv, 0);

  

DB软件的安装和编译
从DB的官方站点http://www.sleepycat.com/下载最新的软件包db-4.3.27.tar.gz,解压到工作目录,进入该目录,依次执行下列三条命令即可。



../dist/configure

make

make install

执行make uninstall,则可卸载已安装的DB软件。

DB缺省把库和头文件安装在目录/usr/local/BerkeleyDB.4.3/下,使用gcc test.c -ggdb-I/usr/local/BerkeleyDB.4.3/include/ -L/usr/local/BerkeleyDB.4.3/lib/-ldb -lpthread就可正确编译程序。如果读者的测试主机操作系统为REDHAT9,则安装的DB版本可能是4.0。特别要注意到这两个版本的库是不兼容 的。例如打开数据库函数DB->open(),在4.0版本中入参为6个,而在4.3版中则为7个(可自行比较两个库的头文件db.h中 DB->open函数的定义)。因为在DB相关的应用程序中,open函数基本上都是要执行的,所以如果函数和版本不匹配,编译肯定会出错。当然, 编译完成后,可以使用命令ldd查看库的依赖关系。

总结
DB是一个具有工业强度的嵌入式数据库系统,数据处理的效率很高。DB功能的稳定性历经时间的考验,在大量应用程序中使用便是明证。可以想见,在同等代码 质量的条件下,软件的BUG数和代码的长度是成正比的,相对几十兆、几百兆大型数据库软件,DB的只有不到500K的大小!

从实现功能上看,DB是轻量级数据库系统,或可称为"极"轻量级数据库系统。但是,我认为不能因此而心存轻视之意,所谓"尺有所短,寸有所长",以绝对角 度比较工具之间的好坏是没有什么意义的,关键在于对工具的选择和运用(似乎可以参考一下极限编程的思想)。也许,正确的"表达范式"应该是:在当前应用背 景下,选择这种工具是最合适的。

来源:http://www-128.ibm.com/developerworks/cn/linux/l-embdb/ 

推荐(0)
收藏
Jun
06

sqlite 入门初步
2008-06-06 8:29 pm 作者:tangfl.openid.35.com

译者: zhuyubing@gmail.com

这里告诉你怎么开始实验 SQLite ,这里没有长长的说明和配置。

下载代码
http://www.sqlite.org/download.html
目前的最新版本: http://www.sqlite.org/sqlite-3.4.0.tar.gz

取得一份二进制拷贝, 或者是源代码并自己编译它. 关于下载页(download) 的更多信息.

    ./configure      ;#  Run the configure script
make                     ;#  Run the makefile.
make install

创建一个新数据库

在命令行下, 输入: "sqlite3 test.db". 将创建一个新的数据库文件名叫"test.db". (你可以使用不同的名字)

输入 SQL 命令在提示符下创建和写入新的数据.

$ sqlite3 ex1
SQLite version 3.3.10
Enter ".help" for instructions
sqlite> create table tbl1(one varchar(10), two smallint);
sqlite> insert into tbl1 values('hello!',10);
sqlite> insert into tbl1 values('goodbye', 20);
sqlite> select * from tbl1;
hello!|10
goodbye|20
sqlite>.q

下面是一个C程序的例子,显示怎么使用 sqlite 的 C/C++ 接口. 数据库的名字由第一个参数取得且第二个参数或更多的参数是 SQL 执行语句. 这个函数调用sqlite3_open() 在 22 行打开数据库, sqlite3_exec() 在 27 行执行 SQL 命令, 并且sqlite3_close() 在 31 行关闭数据库连接.

代码:

#include <stdio.h>
#include <sqlite3.h>

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}

int main(int argc, char **argv){
sqlite3 *db;
char *zErrMsg = 0;
int rc;

if( argc!=3 ){
fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
exit(1);
}
rc = sqlite3_open(argv[1], &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
}
sqlite3_close(db);
return 0;
}

将上面的文件保存为test.c
编译:

# gcc -o test test.c -lsqlite3

执行:

#./test ex1 "select * from tbl1"

 不使用回调查询数据库

上面介绍的 sqlite3_exec 是使用回调来执行 select 操作。还有一个方法可以直接查询而不需要回调。但是,我个人感觉还是回调好,因为代码可以更加整齐,只不过用回调很麻烦,你得声明一个函数,如果这个函数是类成员函数,你还不得不把它声明成 static 的(要问为什么?这又是C++基础了。C++成员函数实际上隐藏了一个参数:this,C++调用类的成员函数的时候,隐含把类指针当成函数的第一个参数传递进去。结果,这造成跟前面说的 sqlite 回调函数的参数不相符。只有当把成员函数声明成 static 时,它才没有多余的隐含的this参数)。

虽然回调显得代码整齐,但有时候你还是想要非回调的 select 查询。这可以通过 sqlite3_get_table 函数做到。

int sqlite3_get_table(sqlite3*, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg );

第1个参数不再多说,看前面的例子。

第2个参数是 sql 语句,跟 sqlite3_exec 里的 sql 是一样的。是一个很普通的以结尾的char *字符串。

第3个参数是查询结果,它依然一维数组(不要以为是二维数组,更不要以为是三维数组)。它内存布局是:第一行是字段名称,后面是紧接着是每个字段的值。下面用例子来说事。

第4个参数是查询出多少条记录(即查出多少行)。

第5个参数是多少个字段(多少列)。

第6个参数是错误信息,跟前面一样,这里不多说了。

下面给个简单例子:

int main( int , char ** )
{
sqlite3 * db;
int result;
char * errmsg = NULL;
char **dbResult; //是 char ** 类型,两个*号
int nRow, nColumn;
int i , j;
int index;

result = sqlite3_open( “c:\\Dcg_database.db”, &db );
if( result != SQLITE_OK )
{
//数据库打开失败
return -1;
}

//数据库操作代码
//假设前面已经创建了 MyTable_1 表
//开始查询,传入的 dbResult 已经是 char **,这里又加了一个 & 取地址符,传递进去的就成了 char ***
result = sqlite3_get_table( db, “select * from MyTable_1”, &dbResult, &nRow, &nColumn, &errmsg );

if( SQLITE_OK == result )
{
//查询成功
index = nColumn; //前面说过 dbResult 前面第一行数据是字段名称,从 nColumn 索引开始才是真正的数据
printf( “查到%d条记录\n”, nRow );
for(  i = 0; i < nRow ; i++ )
{
printf( “第 %d 条记录\n”, i+1 );
for( j = 0 ; j < nColumn; j++ )
{
printf( “字段名:%s  ?> 字段值:%s\n”,  dbResult[j], dbResult [index] );
++index; // dbResult 的字段值是连续的,从第0索引到第 nColumn - 1索引都是字段名称,从第 nColumn 索引开始,后面都是字段值,它把一个二维的表(传统的行列表示法)用一个扁平的形式来表示
}
printf( “-------\n” );
}
}

//到这里,不论数据库查询是否成功,都释放 char** 查询结果,使用 sqlite 提供的功能来释放
sqlite3_free_table( dbResult );
//关闭数据库
sqlite3_close( db );
return 0;
}

例子二:

#include <stdio.h>
#include <sqlite3.h>
int main(int argc, char **argv){
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char ** azResult;
int nrow,ncolumn;
int k;
if( argc!=3 ){
fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
exit(1);
}
rc = sqlite3_open(argv[1], &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
//rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
rc = sqlite3_get_table(db, argv[2], &azResult, &nrow, &ncolumn, &zErrMsg);

if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
} else {
for ( k = 1; k <= nrow; k++ )
{
printf(azResult[k*ncolumn]);
printf(azResult[k*ncolumn+1]);
}
}
sqlite3_close(db);
return 0;
}

推荐(0)
收藏
Jun
06

The MySQL C API
2008-06-06 8:19 pm 作者:tangfl.openid.35.com

简介

C APIs包含在mysqlclient库文件当中与MySQL的源代码一块发行,用于连接到数据库和执行数据库查询。有一些例子在MySQL原代码的clients目录里。

MySQL C 变量类型

以下变量类型在MySQL的库当中定义。我们需要这些变量是为了使用MySQL的函数。这些变量有详细的解释,但是这些解释对于写代码来说并不重要。

MYSQL

要连接MYSQL,必须建立MYSQL实例,通过mysql_init初始化方能开始进行连接。

typedef struct st_mysql {
NET           net;            /* Communication parameters */
gptr          connector_fd;   /* ConnectorFd for SSL */
char          *host,*user,*passwd,*unix_socket,
*server_version,*host_info,*info,*db;
unsigned int  port,client_flag,server_capabilities;
unsigned int  protocol_version;
unsigned int  field_count;
unsigned int  server_status;
unsigned long thread_id;      /* Id for connection in server */
my_ulonglong affected_rows;
my_ulonglong insert_id;       /* id if insert on table with NEXTNR */
my_ulonglong extra_info;              /* Used by mysqlshow */
unsigned long packet_length;
enum mysql_status status;
MYSQL_FIELD   *fields;
MEM_ROOT      field_alloc;
my_bool       free_me;        /* If free in mysql_close */
my_bool       reconnect;      /* set to 1 if automatic reconnect */
struct st_mysql_options options;
char          scramble_buff[9];
struct charset_info_st *charset;
unsigned int  server_language;
} MYSQL;

MYSQL_RES

这个结构代表返回行的一个查询的(SELECT, SHOW, DESCRIBE, EXPLAIN)的结果。返回的数据称为“数据集”,在C的API里对应的就是MYSQL_RES,从数据库读取数据,最后就是从MYSQL_RES中读取数据。

typedef struct st_mysql_res {
my_ulonglong row_count;
unsigned int  field_count, current_field;
MYSQL_FIELD   *fields;
MYSQL_DATA    *data;
MYSQL_ROWS    *data_cursor;
MEM_ROOT      field_alloc;
MYSQL_ROW     row;            /* If unbuffered read */
MYSQL_ROW     current_row;    /* buffer to current row */
unsigned long *lengths;       /* column lengths of current row */
MYSQL         *handle;        /* for unbuffered reads */
my_bool       eof;            /* Used my mysql_fetch_row */
} MYSQL_RES;

MYSQL_ROW

这是一个行数据的类型安全(type-safe)的表示。当前它实现为一个计数字节的字符串数组。(如果字段值可能包含二进制数据,你不能将这些视为空终止串,因为这样的值可以在内部包含空字节) 行通过调用mysql_fetch_row()获得。

typedef char **MYSQL_ROW;

MYSQL_FIELD

这个结构包含字段信息,例如字段名、类型和大小。其成员在下面更详细地描述。你可以通过重复调用mysql_fetch_field()对每一列获得MYSQL_FIELD结构。字段值不是这个结构的部分;它们被包含在一个MYSQL_ROW结构中。

typedef struct st_mysql_field {
char *name;                   /* Name of column */
char *table;                  /* Table of column if column was a field */
char *def;                    /* Default value (set by mysql_list_fields) */
enum enum_field_types type;   /* Type of field. Se mysql_com.h for types */
unsigned int length;          /* Width of column */
unsigned int max_length;      /* Max width of selected set */
unsigned int flags;           /* Div flags */
unsigned int decimals;        /* Number of decimals in field */
} MYSQL_FIELD;

my_ulonglong
typedef unsigned long my_ulonglong;

该类型用于行编号和mysql_affected_rows()、mysql_num_rows()和mysql_insert_id()。这种类型提供 0到1.84e19的一个范围。在一些系统上,试图打印类型my_ulonglong的值将不工作。为了打印出这样的值,将它变换到unsigned long并且使用一个%lu打印格式。例如:

printf ("Number of rows: %lu\n", (unsigned long) mysql_num_rows(result));

连接MySQL,查询数据

MySQL的库文件在mysqlclient,因此在编译MySQL程序的时候有必要加上-lmysqlclient编译选项。
MySQL 的头文件在/usr/include/mysql目录下(根据Linux的发行版本的不同,这个目录也有所不同),因此你的程序头部看起来有点这个样子:

#include <mysql.h>

MySQL的变量类型和函数都包含在这个头文件当中

然后,我们需要创建连接数据库的变量,可以简单地这么做:

MYSQL mysql;

在连接数据库之前,先调用以下函数初始化这个变量:

mysql_init(&mysql);

然后,调用mysql_real_connect函数:

MYSQL *         STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned int clientflag);

该函数被调用连接到数据库。host是MySQL服务器的主机名,user是登录的用户名,passwd是登录密码,db是要连接的数据库,port是 MySQL服务器的TCP/IP端口,unix_socket是连接类型,clientflag是MySQL运行成ODBC数据库的标记。连接寻建立后,这个函数返回0。

现在可以连接数据库,进行查询了:

char *query;

使用这个字符串我们可以创立任何SQL查询语句进行查询。执行这个查询的函数是:

int STDCALL mysql_real_query(MYSQL *mysql, const char *q, unsigned int length);

mysql是我们前面用过的变量,q是SQL查询语句,length是这个查询语句的长度。如果查询成功,函数返回0。

查询之后,我们要到一个MYSQL_RES变量来使用查询的结果。以下这行创立这个变量:

MYSQL_RES *res;

然后

 res = mysql_store_result(&mysql);

对客户端而言,有两种方法处理结果集合。一种方法是通过调用mysql_store_result()立刻检索全部结果。该函数从服务器获得查询返回的所有行,并将他们存储在客户端。第二种方法是对客户通过调用mysql_use_result()初始化一个一行一行地结果集合的检索。该函数初始化检索,但是实际上不从服务器获得任何行。

在两种情况中,你通过mysql_fetch_row()存取行。用mysql_store_result()、mysql_fetch_row()储存取已经从服务器被取出的行。用mysql_use_result()、mysql_fetch_row()实际上从服务器检索行。调用 mysql_fetch_lengths()可获得关于每行中数据值尺寸的信息。

在你用完一个结果集合以后,调用mysql_free_result()释放由它使用的内存。

两种检索机制是互补的。客户程序应该选择最适合他们的要求的途径。在实践中,客户通常更愿意使用mysql_store_result()。

该函数读出查询结果。

尽管可以很容易地查询了,要用这个查询的结果还要用到其它的函数。第一个是:

 MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);

该函数把结果转换成“数组”。你可能注意到了,该函数返回的是MYSQL_ROW变量类型。以下语句创立那样的变量:

 MYSQL_ROW row = mysql_fetch_row(res)

如前所解释的,变量row是一个字符串数组。也就是说,row[0]是数组的第一个值,row[1]是数组的第二个值...当我们用mysql_fetch_row的时候,接着变量row会取得结果的下一组的数据。当到了结果的尾部,该函数返回一负值。

使用数据集结束后,记得释放数据集,否则会发生内存泄漏,释放数据集函数如下:

void mysql_free_result(MYSQL_RES *result)

释放由mysql_store_result()、mysql_use_result()、mysql_list_dbs()等为一个结果集合分配的内存。当你用完了一个结果集合时,你必须调用mysql_free_result()来释放它使用的内存。

最后我们要关闭这个连接:

mysql_close(&mysql);

例子程序

执行一个select操作,从数据库中取数据,并执行一个insert操作,往数据库中插入数据,根据这两个操作你可以自由的扩展为任意数据库操作,
准备条件

1、已经安装mysql,上有数据库test,如果没有执行Create Databse test

建立数据库

2、test数据库上有表t1,如果没有,执行CREATE TABLE `t1` (
`id` int(11) default NULL,
`name` varchar(100) default NULL
)

建立表t1
testsql.c:

  #include <mysql.h>
#include <stdio.h>
int main(){
MYSQL mysql;     // need a instance to init
MYSQL_RES *res;
MYSQL_ROW row;
char *query;
int t,r;
// connect the database
mysql_init(&mysql);
if (!mysql_real_connect(&mysql,"localhost", "mmim", "mmim", "test",0,NULL,0))
{
printf( "Error connecting to database: %s\n",mysql_error(&mysql));
}
else printf("Connected...\n");

// get the result from the executing select query
query = "select * from t1";

t = mysql_real_query(&mysql,query,(unsigned int) strlen(query));
if (t)
{
printf("Error making query: %s\n",
mysql_error(&mysql));
}
else printf("[%s] made...\n", query);
res = mysql_store_result(&mysql);
while(row = mysql_fetch_row(res))
{
for(t=0;t<mysql_num_fields(res);t++)
{
printf("%s ",row[t]);
}
printf("\n");
}

printf("mysql_free_result...\n");
mysql_free_result(res);     //free result after you get the result

sleep(1);

// execute the insert query
query = "insert into t1(id, name) values(3, 'kunp')";
t = mysql_real_query(&mysql,query,(unsigned int) strlen(query));
if (t)
{
printf("Error making query: %s\n",
mysql_error(&mysql));
}
else printf("[%s] made...\n", query);

mysql_close(&mysql);

return 0;
}

编译

假定mysql的头文件在/usr/include/mysql,库文件在/usr/lib/mysql,执行下列命令进行编译:
gcc testsql.c -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient

推荐(0)
收藏
Jun
06

引用:http://www.wikilib.com/wiki?title=DHT%E7%BD%91%E7%BB%9C

分布式散列表(英语:Distributed Hash Table,简称DHT)是分布式计算系统中的一类,用来将一个关键值(key)的集合分散到所有在分布式系统中的节点,并且可以有效地将讯息转送到唯一一个拥有查询者提供的关键值的节点(Peers)。这里的节点类似散列表中的储存位置。分布式散列表通常是为了拥有极大节点数量的系统,而且在系统的节点常常会加入或离开(例如网络断线)而设计的。在一个结构性的延展网络(overlay network)中,参加的节点需要与系统中一小部份的节点沟通,这也需要使用分布式散列表。分布式散列表可以用以建立更复杂的服务,例如分布式档案系统、点对点技术档案分享系统、合作的网页快取、多播、任播(anycast)、网域名称系统以及即时通讯等。
目录

* 1 发展背景
* 2 性质
* 3 结构
o 3.1 关键值空间分割
o 3.2 延展网络
* 4 范例
o 4.1 分布式散列表实作与协定
o 4.2 分布式散列表的应用
* 5 参见
* 6 参考资料
* 7 外部链接

 发展背景

研究分布式散列表的主要动机是为了开发点对点系统,像是Napster、Gnutella及Freenet。这些系统得益于使用分散在因特网上的各项资源以提供实用的应用,特别在带宽及硬盘储存空间上,他们所提供的档案分享功能因此得到最大的好处。

这些系统使用不同的方法来解决如何找到拥有某资料的节点的问题。Napster 使用中央的索引服务器:每个节点加入网络的同时,会将他们所拥有的档案列表传送给服务器,这使得服务器可以进行搜寻并将结果回传给进行查询的节点。但中央索引服务器让整个系统易受攻击,且可能造成法律问题。于是,Gnutella 和相似的网络改用大量查询模式(flooding query model):每次搜寻都会把查询讯息广播给网络上的所有节点。虽然这个方式能够防止单点故障(single point of failure),但比起 Napster 来说却极没效率。

最后,Freenet 使用了完全分布式的系统,但它建置了一套使用经验法则的基于关键值的转送方法(key based routing)。在这个方法中,每个档案与一个关键值相结合,而拥有相似关键值的档案会倾向被相似的节点构成的集合所保管。于是查询讯息就可以根据它所提供的关键值被转送到该集合,而不需要经过所有的节点。然而,Freenet 并不保证存在网络上的资料在查询时一定会被找到。

分布式散列表为了达到 Gnutella 与 Freenet 的分散性(decentralization)以及 Napster 的效率与正确结果,使用了较为结构化的基于关键值的转送方法。不过分布式散列表也有个 Freenet 有的缺点,就是只能作精确搜寻,而不能只提供部份的关键字;但这个功能可以在分布式散列表的上层实做。

最初的四项分布式散列表技术——内容可寻址网络(Content addressable network,CAN)、Chord(Chord project)[1]、Pastry(Pastry (DHT)),以及 Tapestry (DHT)(Tapestry (DHT))皆同时于2001年发表。从那时开始,相关的研究便一直十分活跃。在学术领域以外,分布式散列表技术已经被应用在BitTorrent及CoralCDN(Coral Content Distribution Network)等。

 性质

分布式散列表本质上强调以下特性:

* 分散性:构成系统的节点并没有任何中央式的协调机制。
* 规模性:即使有成千上万个节点,系统仍然应该十分有效率。
* 容错:即使节点不断地加入、离开或是停止工作,系统仍然必须达到一定的可靠度。

要达到以上的目标,有一个关键的技术:任一个节点只需要与系统中的部份节点沟通。一般来说,若系统有n 个节点,那麽只有 Θ(logn) 个节点是必须的(见后述)。因此,当成员改变的时候,只有一部分的工作(例如资料或关键值的传送,散列表的改变等)必须要完成。

有些分布式散列表的设计寻求能对抗网络中恶意的节点的安全性,但仍然保留参加节点的匿名性。在其他的点对点系统(特别是档案分享)中较为少见。参见匿名点对点技术。

最后,分布式散列表必须处理传统分布式系统可能遇到的问题,例如负载平衡、资料完整性,以及效能问题(特别是确认转送讯息、资料储存及读取等动作能快速完成)。

 结构

分布式散列表的结构可以分成几个主要的元件[2][3]。其基础是一个抽象的关键值空间(keyspace),例如说所有160位元长的字符串集合。关键值空间分割(keyspace partitioning)将关键值空间分割成数个,并指定到在此系统的节点中。而延展网络则连接这些节点,并让他们能够借由在关键值空间内的任一值找到拥有该值的节点。

当这些元件都准备好后,一般使用分布式散列表来储存与读取的方式如下所述。假设关键值空间是一个160位元长的字符串集合。为了在分布式散列表中储存一个档案,名称为 filename 且内容为 data,我们计算出 filename 的 SHA1 散列值——一个160位元的关键值 k——并将讯息 put(k,data) 送给分布式散列表中的任意参与节点。此讯息在延展网络中被转送,直到抵达在关键值空间分割中被指定负责储存关键值 k 的节点。而 (k,data) 即储存在该节点。其他的节点只需要重新计算 filename 的散列值 k,然后送出讯息 get(k) 给分布式散列表中的任意参与节点,以此来找与 k 相关的资料。此讯息也会在延展网络中被转送到负责储存 k 的节点。而此节点则会负责传回储存的资料 data。

以下分别描述关键值空间分割及延展网络的基本概念。这些概念在大多数的分布式散列表实作中是相同的,但设计的细节部份则大多不同。

 关键值空间分割

大多数的分布式散列表使用某些稳定散列(consistent hashing)方法来将关键值对应到节点。此方法使用了一个函数 δ(k1,k2) 来定义一个抽象的概念:从关键值k1 到 k2 的距离。每个节点被指定了一个关键值,称为ID。ID 为 i 的节点拥有根据函数 δ 计算,最接近 i 的所有关键值。

例:Chord 分布式散列表实作将关键值视为一个圆上的点,而 δ(k1,k2) 则是沿著圆顺时钟地从 k1 走到 k2 的距离。结果,圆形的关键值空间就被切成连续的圆弧段,而每段的端点都是节点的ID。如果 i1 与 i2 是邻近的 ID,则 ID 为 i2 的节点拥有落在 i1 及 i2 之间的所有关键值。

稳定散列拥有一个基本的性质:增加或移除节点只改变邻近ID的节点所拥有的关键值集合,而其他节点的则不会被改变。对比于传统的散列表,若增加或移除一个位置,则整个关键值空间就必须重新对应。由于拥有资料的改变通常会导致资料从分布式散列表中的一个节点被搬到另一个节点,而这是非常浪费带宽的,因此若要有效率地支援大量密集的节点增加或离开的动作,这种重新配置的行为必须尽量减少。

 延展网络

每个节点保有一些到其他节点(它的邻居)的连结。将这些连结总合起来就形成延展网络。而这些连结是使用一个结构性的方式来挑选的,称为网络拓撲。

所有的分布式散列表实作拓撲有某些基本的性质:对于任一关键值 k,某个节点要不就拥有 k,要不就拥有一个连结能连结到距离较接近 k 的节点。因此使用以下的贪心算法即可容易地将讯息转送到拥有关键值 k 的节点:在每次执行时,将讯息转送到 ID 较接近 k 的邻近节点。若没有这样的节点,那我们一定抵达了最接近 k 的节点,也就是拥有 k 的节点。这样的转送方法有时被称为“基于关键值的转送方法”。

除了基本的转送正确性之外,拓撲中另有两个关键的限制:其一为保证任何的转送路径长度必须尽量短,因而请求能快速地被完成;其二为任一节点的邻近节点数目(又称最大节点度(Degree (graph theory)))必须尽量少,因此维护的花费不会过多。当然,转送长度越短,则最大节点度越大。以下列出常见的最大节点度及转送长度(n 为分布式散列表中的节点数)

* 最大节点度 O(1),转送长度 O(logn)
* 最大节点度 O(logn),转送长度 O(logn / loglogn)
* 最大节点度 O(logn),转送长度 O(logn)
* 最大节点度 O(n1 / 2),转送长度 O(1)

第三个选择最为常见。虽然他在最大节点度与转送长度的取舍中并不是最佳的选择,但这样的拓撲允许较为有弹性地选择邻近节点。许多分布式散列表实作利用这种弹性来选择延迟较低的邻近节点。

最大的转送长度与直径有关:最远的两节点之间的最短距离。无疑地,网络的最大转送长度至少要与它的直径一样长,因而拓撲也被最大节点度与直径的取舍限制住[4],而这在图论中是基本的性质。因为贪婪算法(Greed Method)可能找不到最短路径,因此转送长度可能比直径长[5]。

 范例

分布式散列表实作与协定

* Bamboo[6]
* Bunshin[7]
* 内容可寻址网络 (Content Addressable Network)
* Chord
* DKS系统[8]
* Kademlia
* Leopard
* MACE[9]
* Pastry
* P-Grid
* Tapestry

分布式散列表的应用

* BitTorrent:档案分享应用。BitTorrent 可以选用DHT作为分布式Tracker。
* Warez P2P:档案分享应用。
* The Circle:档案分享应用与聊天。
* CSpace:安全的沟通系统。
* Codeen:网页快取。
* CoralCDN
* Dijjer
* eMule:档案分享应用。
* I2P:匿名网络。
* JXTA:开放原始码的点对点平台。
* NEOnet:档案分享应用。
* Overnet:档案分享应用。

参见

* memcached:一个高效能、分布式的物件快取系统。

参考资料

  1.  (英文) Hari Balakrishnan, M. Frans Kaashoek, David Karger, Robert Morris 与 Ion Stoica, Looking up data in P2P systems. 于 Communications of the ACM, 2003年1月
  2. (英文) Moni Naor 与 Udi Wieder. Novel Architectures for P2P Applications: the Continuous-Discrete Approach. Proc. SPAA, 2003年
  3. (英文) Gurmeet Singh Manku. Dipsea: A Modular Distributed Hash Table. 博士论文 (史丹佛大学),2004年8月
  4. (英文) http://maite71.upc.es/grup_de_grafs/table_g.html
  5. (英文) Gurmeet Singh Manku, Moni Naor, and Udi Wieder. Know thy Neighbor's Neighbor: the Power of Lookahead in Randomized P2P Networks. Proc. STOC, 2004.
  6.  http://www.bamboo-dht.org/
  7.  http://planet.urv.es/bunshin/
  8.  http://dks.sics.se
  9.  http://mace.ucsd.edu

外部链接

推荐(0)
收藏
Jun
04
-------------------------------------------------------------------------
SED单行脚本快速参考(Unix 流编辑器)                       2005年12月29日

英文标题:USEFUL ONE-LINE SCRIPTS FOR SED (Unix stream editor)
原标题:HANDY ONE-LINERS FOR SED (Unix stream editor)

整理:Eric Pement  - 电邮:pemente[at]northpark[dot]edu         版本5.5
译者:Joe Hong     - 电邮:hq00e[at]126[dot]com

在以下地址可找到本文档的最新(英文)版本:
   http://sed.sourceforge.net/sed1line.txt
   http://www.pement.org/sed/sed1line.txt

其他语言版本:
  中文          - http://sed.sourceforge.net/sed1line_zh-CN.html
  捷克语        - http://sed.sourceforge.net/sed1line_cz.html
  荷语          - http://sed.sourceforge.net/sed1line_nl.html
  法语          - http://sed.sourceforge.net/sed1line_fr.html
  德语          - http://sed.sourceforge.net/sed1line_de.html

  葡语          - http://sed.sourceforge.net/sed1line_pt-BR.html


文本间隔:
--------

 # 在每一行后面增加一空行
 sed G

 # 将原来的所有空行删除并在每一行后面增加一空行。
 # 这样在输出的文本中每一行后面将有且只有一空行。
 sed '/^$/d;G'

 # 在每一行后面增加两行空行
 sed 'G;G'

 # 将第一个脚本所产生的所有空行删除(即删除所有偶数行)
 sed 'n;d'

 # 在匹配式样“regex”的行之前插入一空行
 sed '/regex/{x;p;x;}'

 # 在匹配式样“regex”的行之后插入一空行
 sed '/regex/G'

 # 在匹配式样“regex”的行之前和之后各插入一空行
 sed '/regex/{x;p;x;G;}'

编号:
--------

 # 为文件中的每一行进行编号(简单的左对齐方式)。这里使用了“制表符”
 # (tab,见本文末尾关于'\t'的用法的描述)而不是空格来对齐边缘。
 sed = filename | sed 'N;s/\n/\t/'

 # 对文件中的所有行编号(行号在左,文字右端对齐)。
 sed = filename | sed 'N; s/^/     /; s/ *\(.\{6,\}\)\n/\1  /'

 # 对文件中的所有行编号,但只显示非空白行的行号。
 sed '/./=' filename | sed '/./N; s/\n/ /'

 # 计算行数 (模拟 "wc -l")
 sed -n '$='

文本转换和替代:
--------

 # Unix环境:转换DOS的新行符(CR/LF)为Unix格式。
 sed 's/.$//'                     # 假设所有行以CR/LF结束
 sed 's/^M$//'                    # 在bash/tcsh中,将按Ctrl-M改为按Ctrl-V
 sed 's/\x0D$//'                  # ssed、gsed 3.02.80,及更高版本

 # Unix环境:转换Unix的新行符(LF)为DOS格式。
 sed "s/$/`echo -e \\\r`/"        # 在ksh下所使用的命令
 sed 's/$'"/`echo \\\r`/"         # 在bash下所使用的命令
 sed "s/$/`echo \\\r`/"           # 在zsh下所使用的命令
 sed 's/$/\r/'                    # gsed 3.02.80 及更高版本

 # DOS环境:转换Unix新行符(LF)为DOS格式。
 sed "s/$//"                      # 方法 1
 sed -n p                         # 方法 2

 # DOS环境:转换DOS新行符(CR/LF)为Unix格式。
 # 下面的脚本只对UnxUtils sed 4.0.7 及更高版本有效。要识别UnxUtils版本的
 #  sed可以通过其特有的“--text”选项。你可以使用帮助选项(“--help”)看
 # 其中有无一个“--text”项以此来判断所使用的是否是UnxUtils版本。其它DOS
 # 版本的的sed则无法进行这一转换。但可以用“tr”来实现这一转换。
 sed "s/\r//" infile >outfile     # UnxUtils sed v4.0.7 或更高版本
 tr -d \r <infile >outfile        # GNU tr 1.22 或更高版本

 # 将每一行前导的“空白字符”(空格,制表符)删除
 # 使之左对齐
 sed 's/^[ \t]*//'                # 见本文末尾关于'\t'用法的描述

 # 将每一行拖尾的“空白字符”(空格,制表符)删除
 sed 's/[ \t]*$//'                # 见本文末尾关于'\t'用法的描述

 # 将每一行中的前导和拖尾的空白字符删除
 sed 's/^[ \t]*//;s/[ \t]*$//'

 # 在每一行开头处插入5个空格(使全文向右移动5个字符的位置)
 sed 's/^/     /'

 # 以79个字符为宽度,将所有文本右对齐
 sed -e :a -e 's/^.\{1,78\}$/ &/;ta'  # 78个字符外加最后的一个空格

 # 以79个字符为宽度,使所有文本居中。在方法1中,为了让文本居中每一行的前
 # 头和后头都填充了空格。 在方法2中,在居中文本的过程中只在文本的前面填充
 # 空格,并且最终这些空格将有一半会被删除。此外每一行的后头并未填充空格。
 sed  -e :a -e 's/^.\{1,77\}$/ & /;ta'                     # 方法1
 sed  -e :a -e 's/^.\{1,77\}$/ &/;ta' -e 's/\( *\)\1/\1/'  # 方法2

 # 在每一行中查找字串“foo”,并将找到的“foo”替换为“bar”
 sed 's/foo/bar/'                 # 只替换每一行中的第一个“foo”字串
 sed 's/foo/bar/4'                # 只替换每一行中的第四个“foo”字串
 sed 's/foo/bar/g'                # 将每一行中的所有“foo”都换成“bar”
 sed 's/\(.*\)foo\(.*foo\)/\1bar\2/' # 替换倒数第二个“foo”
 sed 's/\(.*\)foo/\1bar/'            # 替换最后一个“foo”

 # 只在行中出现字串“baz”的情况下将“foo”替换成“bar”
 sed '/baz/s/foo/bar/g'

 # 将“foo”替换成“bar”,并且只在行中未出现字串“baz”的情况下替换
 sed '/baz/!s/foo/bar/g'

 # 不管是“scarlet”“ruby”还是“puce”,一律换成“red”
 sed 's/scarlet/red/g;s/ruby/red/g;s/puce/red/g'  #对多数的sed都有效
 gsed 's/scarlet\|ruby\|puce/red/g'               # 只对GNU sed有效

 # 倒置所有行,第一行成为最后一行,依次类推(模拟“tac”)。
 # 由于某些原因,使用下面命令时HHsed v1.5会将文件中的空行删除
 sed '1!G;h;$!d'               # 方法1
 sed -n '1!G;h;$p'             # 方法2

 # 将行中的字符逆序排列,第一个字成为最后一字,……(模拟“rev”)
 sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//'

 # 将每两行连接成一行(类似“paste”)
 sed '$!N;s/\n/ /'

 # 如果当前行以反斜杠“\”结束,则将下一行并到当前行末尾
 # 并去掉原来行尾的反斜杠
 sed -e :a -e '/\\$/N; s/\\\n//; ta'

 # 如果当前行以等号开头,将当前行并到上一行末尾
 # 并以单个空格代替原来行头的“=”
 sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D'

 # 为数字字串增加逗号分隔符号,将“1234567”改为“1,234,567”
 gsed ':a;s/\B[0-9]\{3\}\>/,&/;ta'                     # GNU sed
 sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta'  # 其他sed

 # 为带有小数点和负号的数值增加逗号分隔符(GNU sed)
 gsed -r ':a;s/(^|[^0-9.])([0-9]+)([0-9]{3})/\1\2,\3/g;ta'

 # 在每5行后增加一空白行 (在第5,10,15,20,等行后增加一空白行)
 gsed '0~5G'                      # 只对GNU sed有效
 sed 'n;n;n;n;G;'                 # 其他sed

选择性地显示特定行:
--------

 # 显示文件中的前10行 (模拟“head”的行为)
 sed 10q

 # 显示文件中的第一行 (模拟“head -1”命令)
 sed q

 # 显示文件中的最后10行 (模拟“tail”)
 sed -e :a -e '$q;N;11,$D;ba'

 # 显示文件中的最后2行(模拟“tail -2”命令)
 sed '$!N;$!D'

 # 显示文件中的最后一行(模拟“tail -1”)
 sed '$!d'                        # 方法1
 sed -n '$p'                      # 方法2

 # 显示文件中的倒数第二行
 sed -e '$!{h;d;}' -e x              # 当文件中只有一行时,输入空行
 sed -e '1{$q;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,显示该行
 sed -e '1{$d;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,不输出

 # 只显示匹配正则表达式的行(模拟“grep”)
 sed -n '/regexp/p'               # 方法1
 sed '/regexp/!d'                 # 方法2

 # 只显示“不”匹配正则表达式的行(模拟“grep -v”)
 sed -n '/regexp/!p'              # 方法1,与前面的命令相对应
 sed '/regexp/d'                  # 方法2,类似的语法

 # 查找“regexp”并将匹配行的上一行显示出来,但并不显示匹配行
 sed -n '/regexp/{g;1!p;};h'

 # 查找“regexp”并将匹配行的下一行显示出来,但并不显示匹配行
 sed -n '/regexp/{n;p;}'

 # 显示包含“regexp”的行及其前后行,并在第一行之前加上“regexp”所
 # 在行的行号 (类似“grep -A1 -B1”)
 sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h

 # 显示包含“AAA”、“BBB”或“CCC”的行(任意次序)
 sed '/AAA/!d; /BBB/!d; /CCC/!d'  # 字串的次序不影响结果

 # 显示包含“AAA”、“BBB”和“CCC”的行(固定次序)
 sed '/AAA.*BBB.*CCC/!d'

 # 显示包含“AAA”“BBB”或“CCC”的行 (模拟“egrep”)
 sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d    # 多数sed
 gsed '/AAA\|BBB\|CCC/!d'                        # 对GNU sed有效

 # 显示包含“AAA”的段落 (段落间以空行分隔)
 # HHsed v1.5 必须在“x;”后加入“G;”,接下来的3个脚本都是这样
 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;'

 # 显示包含“AAA”“BBB”和“CCC”三个字串的段落 (任意次序)
 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;/BBB/!d;/CCC/!d'

 # 显示包含“AAA”、“BBB”、“CCC”三者中任一字串的段落 (任意次序)
 sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
 gsed '/./{H;$!d;};x;/AAA\|BBB\|CCC/b;d'         # 只对GNU sed有效

 # 显示包含65个或以上字符的行
 sed -n '/^.\{65\}/p'

 # 显示包含65个以下字符的行
 sed -n '/^.\{65\}/!p'            # 方法1,与上面的脚本相对应
 sed '/^.\{65\}/d'                # 方法2,更简便一点的方法

 # 显示部分文本——从包含正则表达式的行开始到最后一行结束
 sed -n '/regexp/,$p'

 # 显示部分文本——指定行号范围(从第8至第12行,含8和12行)
 sed -n '8,12p'                   # 方法1
 sed '8,12!d'                     # 方法2

 # 显示第52行
 sed -n '52p'                     # 方法1
 sed '52!d'                       # 方法2
 sed '52q;d'                      # 方法3, 处理大文件时更有效率

 # 从第3行开始,每7行显示一次
 gsed -n '3~7p'                   # 只对GNU sed有效
 sed -n '3,${p;n;n;n;n;n;n;}'     # 其他sed

 # 显示两个正则表达式之间的文本(包含)
 sed -n '/Iowa/,/Montana/p'       # 区分大小写方式

选择性地删除特定行:
--------

 # 显示通篇文档,除了两个正则表达式之间的内容
 sed '/Iowa/,/Montana/d'

 # 删除文件中相邻的重复行(模拟“uniq”)
 # 只保留重复行中的第一行,其他行删除
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # 删除文件中的重复行,不管有无相邻。注意hold space所能支持的缓存
 # 大小,或者使用GNU sed。
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

 # 删除除重复行外的所有行(模拟“uniq -d”)
 sed '$!N; s/^\(.*\)\n\1$/\1/; t; D'

 # 删除文件中开头的10行
 sed '1,10d'

 # 删除文件中的最后一行
 sed '$d'

 # 删除文件中的最后两行
 sed 'N;$!P;$!D;$d'

 # 删除文件中的最后10行
 sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # 方法1
 sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # 方法2

 # 删除8的倍数行
 gsed '0~8d'                           # 只对GNU sed有效
 sed 'n;n;n;n;n;n;n;d;'                # 其他sed

 # 删除匹配式样的行
 sed '/pattern/d'                      # 删除含pattern的行。当然pattern
                                       # 可以换成任何有效的正则表达式

 # 删除文件中的所有空行(与“grep '.' ”效果相同)
 sed '/^$/d'                           # 方法1
 sed '/./!d'                           # 方法2

 # 只保留多个相邻空行的第一行。并且删除文件顶部和尾部的空行。
 # (模拟“cat -s”)
 sed '/./,/^$/!d'        #方法1,删除文件顶部的空行,允许尾部保留一空行
 sed '/^$/N;/\n$/D'      #方法2,允许顶部保留一空行,尾部不留空行

 # 只保留多个相邻空行的前两行。
 sed '/^$/N;/\n$/N;//D'

 # 删除文件顶部的所有空行
 sed '/./,$!d'

 # 删除文件尾部的所有空行
 sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'  # 对所有sed有效
 sed -e :a -e '/^\n*$/N;/\n$/ba'        # 同上,但只对 gsed 3.02.*有效

 # 删除每个段落的最后一行
 sed -n '/^$/{p;h;};/./{x;/./p;}'

特殊应用:
--------

 # 移除手册页(man page)中的nroff标记。在Unix System V或bash shell下使
 # 用'echo'命令时可能需要加上 -e 选项。
 sed "s/.`echo \\\b`//g"    # 外层的双括号是必须的(Unix环境)
 sed 's/.^H//g'             # 在bash或tcsh中, 按 Ctrl-V 再按 Ctrl-H
 sed 's/.\x08//g'           # sed 1.5,GNU sed,ssed所使用的十六进制的表示方法

 # 提取新闻组或 e-mail 的邮件头
 sed '/^$/q'                # 删除第一行空行后的所有内容

 # 提取新闻组或 e-mail 的正文部分
 sed '1,/^$/d'              # 删除第一行空行之前的所有内容

 # 从邮件头提取“Subject”(标题栏字段),并移除开头的“Subject:”字样
 sed '/^Subject: */!d; s///;q'

 # 从邮件头获得回复地址
 sed '/^Reply-To:/q; /^From:/h; /./d;g;q'

 # 获取邮件地址。在上一个脚本所产生的那一行邮件头的基础上进一步的将非电邮
 # 地址的部分剃除。(见上一脚本)
 sed 's/ *(.*)//; s/>.*//; s/.*[:<] *//'

 # 在每一行开头加上一个尖括号和空格(引用信息)
 sed 's/^/> /'

 # 将每一行开头处的尖括号和空格删除(解除引用)
 sed 's/^> //'

 # 移除大部分的HTML标签(包括跨行标签)
 sed -e :a -e 's/<[^>]*>//g;/</N;//ba'

 # 将分成多卷的uuencode文件解码。移除文件头信息,只保留uuencode编码部分。
 # 文件必须以特定顺序传给sed。下面第一种版本的脚本可以直接在命令行下输入;
 # 第二种版本则可以放入一个带执行权限的shell脚本中。(由Rahul Dhesi的一
 # 个脚本修改而来。)
 sed '/^end/,/^begin/d' file1 file2 ... fileX | uudecode   # vers. 1
 sed '/^end/,/^begin/d' "$@" | uudecode                    # vers. 2

 # 将文件中的段落以字母顺序排序。段落间以(一行或多行)空行分隔。GNU sed使用
 # 字元“\v”来表示垂直制表符,这里用它来作为换行符的占位符——当然你也可以
 # 用其他未在文件中使用的字符来代替它。
 sed '/./{H;d;};x;s/\n/={NL}=/g' file | sort | sed '1s/={NL}=//;s/={NL}=/\n/g'
 gsed '/./{H;d};x;y/\n/\v/' file | sort | sed '1s/\v//;y/\v/\n/'

 # 分别压缩每个.TXT文件,压缩后删除原来的文件并将压缩后的.ZIP文件
 # 命名为与原来相同的名字(只是扩展名不同)。(DOS环境:“dir /b”
 # 显示不带路径的文件名)。
 echo @echo off >zipup.bat
 dir /b *.txt | sed "s/^\(.*\)\.TXT/pkzip -mo \1 \1.TXT/" >>zipup.bat

使用SED:Sed接受一个或多个编辑命令,并且每读入一行后就依次应用这些命令。
当读入第一行输入后,sed对其应用所有的命令,然后将结果输出。接着再读入第二
行输入,对其应用所有的命令……并重复这个过程。上一个例子中sed由标准输入设
备(即命令解释器,通常是以管道输入的形式)获得输入。在命令行给出一个或多
个文件名作为参数时,这些文件取代标准输入设备成为sed的输入。sed的输出将被
送到标准输出(显示器)。因此:

 cat filename | sed '10q'         # 使用管道输入
 sed '10q' filename               # 同样效果,但不使用管道输入
 sed '10q' filename > newfile     # 将输出转移(重定向)到磁盘上

要了解sed命令的使用说明,包括如何通过脚本文件(而非从命令行)来使用这些命
令,请参阅《sed & awk》第二版,作者Dale Dougherty和Arnold Robbins
(O'Reilly,1997;http://www.ora.com),《UNIX Text Processing》,作者
Dale Dougherty和Tim O'Reilly(Hayden Books,1987)或者是Mike Arst写的教
程——压缩包的名称是“U-SEDIT2.ZIP”(在许多站点上都找得到)。要发掘sed
的潜力,则必须对“正则表达式”有足够的理解。正则表达式的资料可以看
《Mastering Regular Expressions》作者Jeffrey Friedl(O'reilly 1997)。
Unix系统所提供的手册页(“man”)也会有所帮助(试一下这些命令
“man sed”、“man regexp”,或者看“man ed”中关于正则表达式的部分),但
手册提供的信息比较“抽象”——这也是它一直为人所诟病的。不过,它本来就不
是用来教初学者如何使用sed或正则表达式的教材,而只是为那些熟悉这些工具的人
提供的一些文本参考。

括号语法:前面的例子对sed命令基本上都使用单引号('...')而非双引号
("...")这是因为sed通常是在Unix平台上使用。单引号下,Unix的shell(命令
解释器)不会对美元符($)和后引号(`...`)进行解释和执行。而在双引号下
美元符会被展开为变量或参数的值,后引号中的命令被执行并以输出的结果代替
后引号中的内容。而在“csh”及其衍生的shell中使用感叹号(!)时需要在其前
面加上转义用的反斜杠(就像这样:\!)以保证上面所使用的例子能正常运行
(包括使用单引号的情况下)。DOS版本的Sed则一律使用双引号("...")而不是
引号来圈起命令。

'\t'的用法:为了使本文保持行文简洁,我们在脚本中使用'\t'来表示一个制表
符。但是现在大部分版本的sed还不能识别'\t'的简写方式,因此当在命令行中为
脚本输入制表符时,你应该直接按TAB键来输入制表符而不是输入'\t'。下列的工
具软件都支持'\t'做为一个正则表达式的字元来表示制表符:awk、perl、HHsed、
sedmod以及GNU sed v3.02.80。

不同版本的SED:不同的版本间的sed会有些不同之处,可以想象它们之间在语法上
会有差异。具体而言,它们中大部分不支持在编辑命令中间使用标签(:name)或分
支命令(b,t),除非是放在那些的末尾。这篇文档中我们尽量选用了可移植性较高
的语法,以使大多数版本的sed的用户都能使用这些脚本。不过GNU版本的sed允许使
用更简洁的语法。想像一下当读者看到一个很长的命令时的心情:

   sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d

好消息是GNU sed能让命令更紧凑:

   sed '/AAA/b;/BBB/b;/CCC/b;d'      # 甚至可以写成
   sed '/AAA\|BBB\|CCC/b;d'

此外,请注意虽然许多版本的sed接受象“/one/ s/RE1/RE2/”这种在's'前带有空
格的命令,但这些版本中有些却不接受这样的命令:“/one/! s/RE1/RE2/”。这时
只需要把中间的空格去掉就行了。

速度优化:当由于某种原因(比如输入文件较大、处理器或硬盘较慢等)需要提高
命令执行速度时,可以考虑在替换命令(“s/.../.../”)前面加上地址表达式来
提高速度。举例来说:

   sed 's/foo/bar/g' filename         # 标准替换命令
   sed '/foo/ s/foo/bar/g' filename   # 速度更快
   sed '/foo/ s//bar/g' filename      # 简写形式

当只需要显示文件的前面的部分或需要删除后面的内容时,可以在脚本中使用“q”
命令(退出命令)。在处理大的文件时,这会节省大量时间。因此:

   sed -n '45,50p' filename           # 显示第45到50行
   sed -n '51q;45,50p' filename       # 一样,但快得多

如果你有其他的单行脚本想与大家分享或者你发现了本文档中错误的地方,请发电
子邮件给本文档的作者(Eric Pement)。邮件中请记得提供你所使用的sed版本、
该sed所运行的操作系统及对问题的适当描述。本文所指的单行脚本指命令行的长
度在65个字符或65个以下的sed脚本〔译注1〕。本文档的各种脚本是由以下所列作
者所写或提供:

 Al Aab                               # 建立了“seders”邮件列表
 Edgar Allen                          # 许多方面
 Yiorgos Adamopoulos                  # 许多方面
 Dale Dougherty                       # 《sed & awk》作者
 Carlos Duarte                        # 《do it with sed》作者
 Eric Pement                          # 本文档的作者
 Ken Pizzini                          # GNU sed v3.02 的作者
 S.G. Ravenhall                       # 去html标签脚本
 Greg Ubben                           # 有诸多贡献并提供了许多帮助
-------------------------------------------------------------------------

译注1:大部分情况下,sed脚本无论多长都能写成单行的形式(通过`-e'选项和`;'
号)——只要命令解释器支持,所以这里说的单行脚本除了能写成一行还对长度有
所限制。因为这些单行脚本的意义不在于它们是以单行的形式出现。而是让用户能
方便地在命令行中使用这些紧凑的脚本才是其意义所在。
推荐(0)
收藏
Jun
02

vimrc
2008-06-02 11:27 am 作者:tangfl.openid.35.com

Special Thanks to  Wu Yongwei

" vim:expandtab shiftwidth=2 tabstop=8 textwidth=72
" Tangfulin's _vimrc for Vim 7

" Last Change: 2008-06-01 00:01:44

if v:version < 700

  echoerr 'This _vimrc requires Vim 7 or later.'

  quit

endif

if has('autocmd')

  " Remove ALL autocommands for the current group

  au!

endif

if has('gui_running')

  " Always show file types in menu

  let do_syntax_sel_menu=1

endif

if has('multi_byte')

  " Legacy encoding is the system default encoding

  let legacy_encoding=&encoding

endif

if has('gui_running') && has('multi_byte')

  " Set encoding (and possibly fileencodings)

  if $LANG !~ '\.' || $LANG =~? '\.UTF-8$'

    set encoding=utf-8

  else

    let &encoding=matchstr($LANG, '\.\zs.*')

    let &fileencodings='ucs-bom,utf-8,' . &encoding

    let legacy_encoding=&encoding

  endif

endif

set nocompatible

source $VIMRUNTIME/vimrc_example.vim

if has('gui_running')

  source $VIMRUNTIME/mswin.vim

  unmap  <C-Y>|             " <C-Y> for Redo is kept in insert mode

  iunmap <C-A>|             " <C-A> for Select-All is kept in normal mode

  " Key mapping to switch windows quickly (<C-Tab> is already mapped)

  nnoremap <C-S-Tab> <C-W>W

  inoremap <C-S-Tab> <C-O><C-W>W

endif

set autoindent

set nobackup

set formatoptions+=mM

set fileencodings=ucs-bom,utf-8,default,latin1          " default value

set grepprg=grep\ -nH

set statusline=%<%f\ %h%m%r%=%k[%{(&fenc==\"\")?&enc:&fenc}%{(&bomb?\",BOM\":\"\")}]\ %-14.(%l,%c%V%)\ %P

set dictionary+=C:\Program\\\ Files\Vim\vimfiles\words

set tags+=C:\Program\\\ Files\Vim\vimfiles\systags      " help ft-c-omni

set directory=~\Locals~1\Temp

set path=.,

        \,

" Personal setting for working with Windows NT/2000/XP (requires tee in path)

if &shell =~? 'cmd'

  "set shellxquote=\"

  set shellpipe=2>&1\|\ tee

endif

" Quote shell if it contains space and is not quoted

if &shell =~? '^[^"].* .*[^"]'

  let &shell='"' . &shell . '"'

endif

" Set British spelling convention for International English

if has('syntax')

  set spelllang=en_gb

endif

if has('eval')

  " Function to find the absolute path of a runtime file

  function! FindRuntimeFile(filename, ...)

    if a:0 != 0 && a:1 =~ 'w'

      let require_writable=1

    else

      let require_writable=0

    endif

    let runtimepaths=&runtimepath . ','

    while strlen(runtimepaths) != 0

      let filepath=substitute(runtimepaths, ',.*', '', '') . '/' . a:filename

      if filereadable(filepath)

        if !require_writable || filewritable(filepath)

          return filepath

        endif

      endif

      let runtimepaths=substitute(runtimepaths, '[^,]*,', '', '')

    endwhile

    return ''

  endfunction

  " Function to display the current character code in its 'file encoding'

  function! EchoCharCode()

    let char_enc=matchstr(getline('.'), '.', col('.') - 1)

    let char_fenc=iconv(char_enc, &encoding, &fileencoding)

    let i=0

    let len=len(char_fenc)

    let hex_code=''

    while i < len

      let hex_code.=printf('%.2x',char2nr(char_fenc[i]))

      let i+=1

    endwhile

    echo '<' . char_enc . '> Hex ' . hex_code . ' (' .

          \(&fileencoding != '' ? &fileencoding : &encoding) . ')'

  endfunction

  " Key mapping to display the current character in its 'file encoding'

  nnoremap <silent> gn :call EchoCharCode()<CR>

  " Function to switch the cursor position between the first column and the

  " first non-blank column

  function! GoToFirstNonBlankOrFirstColumn()

    let cur_col=col('.')

    normal! ^

    if cur_col != 1 && cur_col == col('.')

      normal! 0

    endif

  endfunction

  " Key mappings to make Home go to first non-blank column or first column

  nnoremap <silent> <Home>      :call GoToFirstNonBlankOrFirstColumn()<CR>

  inoremap <silent> <Home> <C-O>:call GoToFirstNonBlankOrFirstColumn()<CR>

  " Function to insert the current date

  function! InsertCurrentDate()

    let curr_date=strftime('%Y-%m-%d', localtime())

    silent! exec 'normal! gi' .  curr_date . "\<ESC>l"

  endfunction

  " Key mapping to insert the current date

  inoremap <silent> <C-\><C-D> <C-O>:call InsertCurrentDate()<CR>

endif

" Key mappings to ease browsing long lines

noremap  <C-J>         gj

noremap  <C-K>         gk

inoremap <M-Home> <C-O>g0

inoremap <M-End>  <C-O>g$

" Key mappings for quick arithmetic inside Vim (requires a calcu in path)

nnoremap <silent> <Leader>ma yypV:!calcu <C-R>"<CR>k$

vnoremap <silent> <Leader>ma yo<ESC>pV:!calcu <C-R>"<CR>k$

nnoremap <silent> <Leader>mr yyV:!calcu <C-R>"<CR>$

vnoremap <silent> <Leader>mr ygvmaomb:r !calcu <C-R>"<CR>"ay$dd`bv`a"ap

" Key mapping for confirmed exiting

nnoremap ZX :confirm qa<CR>

" Key mapping for opening the clipboard (Vim script #1014) to avoid

" conflict with the NERD Commenter (Vim script #1218)

nmap <unique> <silent> <Leader>co <Plug>ClipBrdOpen

" Key mapping to stop the search highlight

nmap <silent> <F2>      :nohlsearch<CR>

imap <silent> <F2> <C-O>:nohlsearch<CR>

" Key mappings to fold line according to syntax

nmap <silent> <F3> :set fdl=1 fdm=syntax<bar>syn sync fromstart<CR>

nmap <C-F3>   zv

nmap <M-F3>   zc

" Key mapping for the VimExplorer (Vim script #1950)

nmap <silent> <F4> :VE %:p:h<CR>

" Key mapping to toggle the display of status line for the last window

nmap <silent> <F6> :if &laststatus == 1<bar>

                     \set laststatus=2<bar>

                     \echo<bar>

                   \else<bar>

                     \set laststatus=1<bar>

                   \endif<CR>

" Key mapping for the taglist.vim plug-in (Vim script #273)

nmap <F9>      :Tlist<CR>

imap <F9> <C-O>:Tlist<CR>

" Key mappings for quickfix commands, tags, and buffers

nmap <F11>   :cn<CR>

nmap <F12>   :cp<CR>

nmap <M-F11> :copen<CR>

nmap <M-F12> :cclose<CR>

nmap <C-F11> :tn<CR>

nmap <C-F12> :tp<CR>

nmap <S-F11> :n<CR>

nmap <S-F12> :prev<CR>

" Function to turn each paragraph to a line (to work with, say, MS Word)

function! ParagraphToLine()

  normal! ma

  if &formatoptions =~ 'w'

    let reg_bak=@"

    normal! G$vy

    if @" =~ '\s'

      normal! o

    endif

    let @"=reg_bak

    silent! %s/\(\S\)$/\1\r/e

  else

    normal! Go

  endif

  silent! g/\S/,/^\s*$/j

  silent! %s/\s\+$//e

  normal! `a

endfunction

" Non-GUI setting

if !has('gui_running')

  " English messages only

  language messages en

  " Do not increase the windows width in taglist

  let Tlist_Inc_Winwidth=0

  " Set text-mode menu

  if has('wildmenu')

    set wildmenu

    set cpoptions-=<

    set wildcharm=<C-Z>

    nmap <F10>      :emenu <C-Z>

    imap <F10> <C-O>:emenu <C-Z>

  endif

  " Change encoding according to the current console code page

  if &termencoding != '' && &termencoding != &encoding

    let &encoding=&termencoding

    let &fileencodings='ucs-bom,utf-8,' . &encoding

  endif

endif

" Display window width and height in GUI

if has('gui_running') && has('statusline')

  let &statusline=substitute(

                 \&statusline, '%=', '%=%{winwidth(0)}x%{winheight(0)}  ', '')

  set laststatus=2

endif

" Set up language and font in GUI

if has('gui_running') && has('multi_byte')

  function! UTF8_East()

    exec 'language messages ' . s:lang_east . '.UTF-8'

    set ambiwidth=double

    set encoding=utf-8

    let s:utf8_east_mode=1

  endfunction

function! UTF8_West()

    exec 'language messages ' . s:lang_west . '.UTF-8'

    set ambiwidth=single

    set encoding=utf-8

    let s:utf8_east_mode=0

  endfunction

function! UTF8_SwitchMode()

    if exists('b:utf8_east_mode')

      unlet b:utf8_east_mode

    endif

    if s:utf8_east_mode

      call UTF8_West()

      call UTF8_SetFont()

    else

      call UTF8_East()

      call UTF8_SetFont()

    endif

  endfunction

function! UTF8_SetFont()

    if &encoding != 'utf-8'

      return

    endif

    if &fileencoding == 'cp936' ||

          \&fileencoding == 'gbk' ||

          \&fileencoding == 'euc-cn'

      let s:font_east=s:font_schinese

    elseif &fileencoding == 'cp950' ||

          \&fileencoding == 'big5' ||

          \&fileencoding == 'euc-tw'

      let s:font_east=s:font_tchinese

    endif

    if exists('b:utf8_east_mode') && s:utf8_east_mode != b:utf8_east_mode

      let s:utf8_east_mode=b:utf8_east_mode

      let &ambiwidth=(s:utf8_east_mode ? 'double' : 'single')

    endif

    if s:utf8_east_mode

      exec 'set guifont=' . s:font_east

      set guifontwide=

    else

      exec 'set guifont=' . s:font_west

      if exists('s:legacy_encoding_is_west')

        exec 'set guifontwide=' . s:font_east

      endif

    endif

  endfunction

function! UTF8_CheckAndSetFont()

    if &fileencoding == 'cp936' ||

          \&fileencoding == 'gbk' ||

          \&fileencoding == 'euc-cn' ||

          \&fileencoding == 'cp950' ||

          \&fileencoding == 'big5' ||

          \&fileencoding == 'euc-tw'

      let b:utf8_east_mode=1

    elseif &fileencoding == 'latin1' ||

          \&fileencoding =~ 'iso-8859-.*' ||

          \&fileencoding =~ 'koi8-.' ||

          \&fileencoding == 'macroman' ||

          \&fileencoding == 'cp437' ||

          \&fileencoding == 'cp850' ||

          \&fileencoding =~ 'cp125.'

      let b:utf8_east_mode=0

    endif

    if (s:utf8_east_mode && &guifont == s:font_east) ||

          \(!s:utf8_east_mode && &guifont == s:font_west)

      call UTF8_SetFont()

    endif

  endfunction

  " Rebuild the menu to make the translations display correctly

  " --------------------------------------------------------------------

  " Uncomment the following code if all of the following conditions

  " hold:

  "   1) Unicode support is wanted (enabled by default for gVim in this

  "      _vimrc);

  "   2) The libintl.dll shipped with gVim for Windows is not updated

  "      with a new one that supports encoding conversion (see also

  "      <URL:http://tinyurl.com/2hnwaq> for issues with this approach);

  "   3) The environment variable LANG is not manually set to something

  "      like "zh_CN.UTF-8", and the default language is not ASCII-based

  "      (English).

  " The reason why the code is not enabled by default is because it can

  " interfere with the localization of menus created by plug-ins.

  " --------------------------------------------------------------------

  "

  "if $LANG !~ '\.' && v:lang !~? '^\(C\|en\)\(_\|\.\|$\)'

  "  runtime! delmenu.vim

  "endif

  " Fonts

  let s:font_schinese='NSimSun:h12:cDEFAULT'

  let s:font_tchinese='MingLiU:h12:cDEFAULT'

  if legacy_encoding == 'cp936'

    let s:font_schinese=''              " Use the system default font

  elseif legacy_encoding == 'cp950'

    let s:font_tchinese=''              " Use the system default font

  else

    let s:legacy_encoding_is_west=1

  endif

  if legacy_encoding != 'cp950'

    let s:font_east=s:font_schinese

  else

    let s:font_east=s:font_tchinese

  endif

  let s:font_west='Courier_New:h10:cDEFAULT'

  " Extract the current Eastern/Western language settings

  if v:lang =~? '^\(zh\)\|\(ja\)\|\(ko\)'

    let s:lang_east=matchstr(v:lang, '^[a-zA-Z_]*\ze\(\.\|$\)')

    let s:lang_west='en'

    let s:utf8_east_mode=1

    if v:lang=~? '^zh_TW'

      let s:font_east=s:font_tchinese

    endif

  else

    let s:lang_east='zh_CN'

    let s:lang_west=matchstr(v:lang, '^[a-zA-Z_]*\ze\(\.\|$\)')

    let s:utf8_east_mode=0

  endif

  " Set a suitable GUI font and the ambiwidth option

  if &encoding == 'utf-8'

    if s:utf8_east_mode

      call UTF8_East()

    else

      call UTF8_West()

    endif

  endif

  call UTF8_SetFont()

  " Key mapping to switch the Eastern/Western UTF-8 mode

  nmap <F8>      :call UTF8_SwitchMode()<CR>

  imap <F8> <C-O>:call UTF8_SwitchMode()<CR>

if has('autocmd')

    " Set the appropriate GUI font according to the fileencoding, but

    " not if user has manually changed it

    au BufWinEnter,WinEnter * call UTF8_CheckAndSetFont()

  endif

endif

" Key mapping to toggle spelling check

if has('syntax')

  nmap <silent> <F7>      :setlocal spell!<CR>

  imap <silent> <F7> <C-O>:setlocal spell!<CR>

  let spellfile_path=FindRuntimeFile('spell/en.' . &encoding . '.add', 'w')

  if spellfile_path != ''

    exec 'nmap <M-F7> :sp ' . spellfile_path . '<CR><bar><C-W>_'

  endif

endif

if has('autocmd')

  function! SetFileEncodings(encodings)

    let b:my_fileencodings_bak=&fileencodings

    let &fileencodings=a:encodings

  endfunction

function! RestoreFileEncodings()

    let &fileencodings=b:my_fileencodings_bak

    unlet b:my_fileencodings_bak

  endfunction

function! GnuIndent()

    setlocal cinoptions=>4,n-2,{2,^-2,:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1

    setlocal shiftwidth=2

    setlocal tabstop=8

  endfunction

function! UpdateLastChangeTime()

    let last_change_anchor='\(" Last Change:\s\+\)\d\{4}-\d\{2}-\d\{2} \d\{2}:\d\{2}:\d\{2}'

    let last_change_line=search('\%^\_.\{-}\(^\zs' . last_change_anchor . '\)', 'n')

    if last_change_line != 0

      let last_change_time=strftime('%Y-%m-%d %H:%M:%S', localtime())

      let last_change_text=substitute(getline(last_change_line), '^' . last_change_anchor, '\1', '') . last_change_time

      call setline(last_change_line, last_change_text)

    endif

  endfunction

function! RemoveTrailingSpace()

    if $VIM_HATE_SPACE_ERRORS != '0' &&

          \(&filetype == 'c' || &filetype == 'cpp' || &filetype == 'vim')

      normal! m`

      silent! :%s/\s\+$//e

      normal! ``

    endif

  endfunction

  " Set default file encodings to the legacy encoding

  "exec 'set fileencoding=' . legacy_encoding

  if legacy_encoding != 'latin1'

    let &fileencodings=substitute(

                      \&fileencodings, '\<default\>', legacy_encoding, '')

  else

    let &fileencodings=substitute(

                      \&fileencodings, ',default,', ',', '')

  endif

  " Set the directory to store _vim_mru_files (Vim script #521)

  let MRU_File=$HOME . '\_vim_mru_files'

  " And exclude the temporary files from being saved

  let MRU_Exclude_Files='\\itsalltext\\.*\|\\temp\\.*'

  " Use the legacy encoding for calling system() in VimExplorer

  let VEConf_systemEncoding=legacy_encoding

  " Use the legacy encoding for CVS in cvsmenu (Vim script #1245)

  let CVScmdencoding=legacy_encoding

  " but the encoding of files in CVS is still UTF-8

  let CVSfileencoding='utf-8'

  " Use automatic encoding detection (Vim script #1708)

  let $FENCVIEW_TELLENC='tellenc'       " See <URL:http://wyw.dcweb.cn/>

  let fencview_auto_patterns='*.txt,*.tex,*.htm{l\=},*.asp'

                           \.',README,CHANGES,INSTALL'

  let fencview_html_filetypes='html,aspvbs'

  " File types to use function echoing (Vim script #1735)

  let EchoFuncLangsUsed=['c', 'cpp']

  " Do not use menu for NERD Commenter

  let NERDMenuMode=0

  " Prevent NERD Commenter from complaining about unknown file types

  let NERDShutUp=1

  " Highlight space errors in C/C++ source files (Vim tip #935)

  if $VIM_HATE_SPACE_ERRORS != '0'

    let c_space_errors=1

  endif

  " Tune for C highlighting

  let c_gnu=1

  let c_no_curly_error=1

  " Load doxygen syntax file for c/cpp/idl files

  let load_doxygen_syntax=1

  " Use Bitstream Vera Sans Mono as special code font in doxygen, which

  " is available at

  " <URL:http://ftp.gnome.org/pub/GNOME/sources/ttf-bitstream-vera/1.10/>

  let doxygen_use_bitsream_vera=1

  " Let TOhtml output <PRE> and style sheet

  let html_use_css=1

  " Show syntax highlighting attributes of character under cursor (Vim

  " script #383)

  map <Leader>a :call SyntaxAttr()<CR>

  " Automatically find scripts in the autoload directory

  au FuncUndefined Syn* exec 'runtime autoload/' . expand('<afile>') . '.vim'

  " File type related autosetting

  au FileType c,cpp      setlocal cinoptions=:0,g0,(0,w1 shiftwidth=4 tabstop=4

  au FileType diff       setlocal shiftwidth=4 tabstop=4

  au FileType changelog  setlocal textwidth=76

  au FileType cvs        setlocal textwidth=72

  au FileType html,xhtml setlocal indentexpr=

  au FileType mail       setlocal expandtab softtabstop=2 textwidth=70

  " Detect file encoding based on file type

  au BufReadPre  *.gb               call SetFileEncodings('cp936')

  au BufReadPre  *.big5             call SetFileEncodings('cp950')

  au BufReadPre  *.nfo              call SetFileEncodings('cp437')

  au BufReadPost *.gb,*.big5,*.nfo  call RestoreFileEncodings()

  " Quickly exiting help files

  au BufRead *.txt      if &buftype=='help'|nmap <buffer> q <C-W>c|endif

  " Setting for files following the GNU coding standard

  au BufEnter D:/mingw*             call GnuIndent()

  " Automatically update change time

  au BufWritePre *vimrc,*.vim       call UpdateLastChangeTime()

  " Remove trailing spaces for C/C++ and Vim files

  au BufWritePre *                  call RemoveTrailingSpace()

endif
推荐(0)
收藏
May
28

通用排序服务器 续
2008-05-28 2:58 pm 作者:tangfl.openid.35.com

代码地址:

通用排序服务器http://code.google.com/p/fulin/source/browse/C++/cntsvr/sdp/?r=13

更新:

1. protocol 层增加缓冲区,以减少 pipeline 的次数。频繁的 pipeline 会因为锁争用的缘故导致性能严重下降。

2. 增加了一层 udp 转发的 so ,在进程之间也可以配置形成新的 pipeline

3. 增加了增量排序的实现:在 mcdb (memcachedb) 里保存快照,排序的 value 为总值减去快照值

4. 增量的时间单位是天,其他的增量,如周,月等,为日增量的合并排序的结果。当前使用 bash 脚本实现,脚本中使用 od ,awk ,sort 等工具。当前的实现由逻辑错误,各相同 id 的日增量没有合并 。(已合并)

5.  mcdb 安装,配置,rotate 脚本等此处暂时省略。

PS: Linux 2.6.9 内核的 syslog 真的很烂。

PS2:mcdb 在主库崩溃的时候,重启辅库,似乎会导致辅库也僵死。计划今天晚些时候重现一次。

推荐(0)
收藏
May
14

squid 日志分析
2008-05-14 12:38 pm 作者:tangfl.openid.35.com

 

通过日志来查看 squid 的一些基本的运行状态:

1. access.log

配置语句:

logformat combined %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh:%tr
cache_access_log /data1/logs/access.log combined

打下的log格式为:

125.71.196.17 - - [14/May/2008:12:16:13 +0800] "GET http://you.video.sina.com.cn/b/13441121-1212188024.html HTTP/1.1" 200 8820 "http://you.video.sina.com.cn/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" TCP_MEM_HIT:NONE:21089

可以通过脚本查看一些统计信息,如各种反应状态所占的比例,通常较好的情况下HIT所占的比例(应该就是所谓的命中率)可以在70%~80%

阅读全文... 

推荐(0)
收藏
May
13

C/C++头文件一览
2008-05-13 6:07 pm 作者:tangfl.openid.35.com

C、传统 C++

#include <assert.h>    //设定插入点
#include <ctype.h>     //字符处理
#include <errno.h>     //定义错误码
#include <float.h>     //浮点数处理
#include <fstream.h>    //文件输入/输出
#include <iomanip.h>    //参数化输入/输出
#include <iostream.h>   //数据流输入/输出
#include <limits.h>    //定义各种数据类型最值常量
#include <locale.h>    //定义本地化函数
#include <math.h>     //定义数学函数
#include <stdio.h>     //定义输入/输出函数
#include <stdlib.h>    //定义杂项函数及内存分配函数
#include <string.h>    //字符串处理
#include <strstrea.h>   //基于数组的输入/输出
#include <time.h>     //定义关于时间的函数
#include <wchar.h>     //宽字符处理及输入/输出
#include <wctype.h>    //宽字符分类

//////////////////////////////////////////////////////////////////////////

标准 C++ (同上的不再注释)

#include <algorithm>    //STL 通用算法
#include <bitset>     //STL 位集容器
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>     //复数类
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>      //STL 双端队列容器
#include <exception>    //异常处理类
#include <fstream>
#include <functional>   //STL 定义运算函数(代替运算符)
#include <limits>
#include <list>      //STL 线性列表容器
#include <map>       //STL 映射容器
#include <iomanip>
#include <ios>       //基本输入/输出支持
#include <iosfwd>     //输入/输出系统使用的前置声明
#include <iostream>
#include <istream>     //基本输入流
#include <ostream>     //基本输出流
#include <queue>      //STL 队列容器
#include <set>       //STL 集合容器
#include <sstream>     //基于字符串的流
#include <stack>      //STL 堆栈容器
#include <stdexcept>    //标准异常类
#include <streambuf>    //底层输入/输出支持
#include <string>     //字符串类
#include <utility>     //STL 通用模板类
#include <vector>     //STL 动态数组容器
#include <cwchar>
#include <cwctype>

using namespace std;

//////////////////////////////////////////////////////////////////////////

C99 增加

#include <complex.h>   //复数处理
#include <fenv.h>    //浮点环境
#include <inttypes.h>  //整数格式转换
#include <stdbool.h>   //布尔环境
#include <stdint.h>   //整型环境
#include <tgmath.h>   //通用类型数学宏

推荐(0)
收藏
May
13

地震来时,你躲在哪里?如果你依照小时候老师教我们的方法乖乖躲在桌子底下,床铺底下,那么,我必须告诉你,你的伤亡率,高达百分之九十八!!那该怎么办?
美国国际搜救队长教你正确的躲避位置。

道格卡普是美国国际搜救队长,自一九八五年至今,他及他的队员己参与全世界七十九次重大灾难的救灾工作,他曾经爬进近七百栋因为地震、爆炸而严重倒塌的建筑物内搜查受困的生还者以及罹难者的遗体。除了参与两年前日本神户大地震及美国俄克拉荷马市联邦大楼爆炸案救搜工作,十二年来国际新闻中的重大灾难救灾,他都没缺席。

本月十九日他离华前,传授在建筑物倒塌时如何求生。

国人从小到大,在防震演习中,老师总是叫学生躲在课桌下,道格得知这点后,很焦急地一再呼吁:不要躲在桌子、床铺下,而要以比桌、床高度更低的姿势,躲在桌子床铺的旁边。他以先前和土耳其go-vern-ment、大学合作拍制的地震逃生录像带,说明不要躲在桌下避震的道理。

透过土耳其go-vern-ment协助,制作单位爆破一栋废弃大楼,仿真地震时建筑物倒塌的情形,工作人员先依据「常识」,在桌子床铺等家具下,放置十具模特儿;他和他的搜救队员在桌子床铺等家具旁,同样放置十具模特儿,de-tona-tor引爆后大楼变成断坦残壁,他和搜救队员依序找到二十具模特儿,在桌床下的十具模特儿有八具被压成全毁,其中一具甚至头、身、脚断成三截;他放置的十具模特儿,则全部安好无事。

他解释,建筑物天花板因强震倒塌时,会将桌床等家具压毁,人如果躲在其中,后果不堪设想, 如果人以低姿势躲在家具旁,家具可以先受倒塌物品的力道,让一旁的人取得生存空间。

道格说,即使开车时遇到地震,也要赶快离开车子,很多地震时在停车场丧命的人,都是在车内被活活压死,在两车之间的人,却毫发未伤(此段话引述图片说明:强烈地震发生时,如果你正在停车场,千万不要留在车内,以免垮下来的天花板压扁汽车,造成伤害;应该以卧姿躲在车旁,掉落的天花压在车上,不致直接撞击人身,可能形成一块『生存空间』,增加存活机会)。

他很慎重地对在场的一百多位我国搜救队员说,搜救队员一要在地震中先能自己求得生存,只有活下来,才能拯救他人性命。他说,希望大家告诉大家,只要传播这么一点求生讯息,地震发生时,建筑物内的伤亡率,可以由百分之九十,遽降为百分之二。

请大家传阅,增加大家在灾难发生时的生存机率,减少伤害。

推荐(0)
收藏
得到OpenID
使用OpenID提供商
35OpenID MyOpenID Flickr
Google Yahoo! AOL
Blogger LiveJournal Verisign
ClaimID Technorati Vidoop
OpenID 帮助