2011年11月28日月曜日

[C 语言] fgetpos, fsetpos 文件随机访问

int fgetpos(FILE *fp, fpos_t *pos)
int fsetpos(FILE *fp, fpos_t *pos)

1. 可以处理大于 2G 的文件
2. 调用成功时则返回 0, 错误返回非 0, errno 存放错误代码

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp;
    fpos_t fpos, tmppos;
    char s[256];
    long pos;

    if((fp = fopen("in", "aw+")) == NULL)
    {
        fprintf(stderr,"can't open file\n");
        exit(EXIT_FAILURE);
    }

    fgetpos(fp, &fpos);
    pos = fpos.__pos;
    printf("first position: %ld\n", pos);

    fputs("ABCDEFG", fp);

    fgetpos(fp, &tmppos);
    pos = tmppos.__pos;
    printf("last position: %ld\n", pos);

    if(fsetpos(fp, &fpos) == 0)
    {
        fgetpos(fp, &tmppos);
        pos = tmppos.__pos;
        printf("reset position: %ld\n", pos);
        fgets(s, sizeof(s), fp);
        printf("file contents: %s\n", s);
    }
    else
    {
        fprintf(stderr, "fsetpos failure.\n");
        exit(EXIT_FAILURE);
    }

    fclose(fp);
    return EXIT_SUCCESS;
}

[C 语言] fseek, ftell 文件随机访问

1. fseek(), 用来移动文件流的读写位置
1-1. int fseek(FILE * stream, long offset, int whence)
whence 取值:
SEEK_SET: 文件开头 + offset (字节)
SEEK_CUR: 当前位置 + offset (字节), offset 可以负值
SEEK_END: 文件末尾 + offset (字节), offset 可以负值

1-2. 调用成功时则返回 0, 错误返回 -1, errno 存放错误代码

2. ftell(), 用来取得文件流目前的读写位置
2-1. long ftell(FILE * stream)
2-2. 调用成功返回目前的读写位置, 错误返回 -1, errno 会存放错误代码

3. 限制文件的大小只能在 long 类型的表示范围以内, 只能处理 2,000,000,000 (2G) 字节文件

#include <stdio.h>
#include <stdlib.h>
#define SLEN 50
int main(int argc, char *argv[])
{
    char file[SLEN];
    char ch;
    FILE *fp;
    long count, last;

    puts("Enter the name of the file to be processed: ");
    gets(file);

    if((fp = fopen(file, "rb")) == NULL)
    {
        printf("can't open %s\n", file);
        exit(EXIT_FAILURE);
    }

    fseek(fp, 0L, SEEK_SET); /* 移动到开头 */
    last = ftell(fp);
    printf("fseek(fp, 0L, SEEK_SET) , fteel(p): %ld\n", last);

    fseek(fp, 0L, SEEK_END); /* 移动到结尾 */
    last = ftell(fp);
    printf("fseek(fp, 0L, SEEK_END) , fteel(p): %ld\n", last);

    for(count = 1L; count <= last; count++)
    {
        fseek(fp, -count, SEEK_END);
        ch = getc(fp);

        if(ch != EOF)
            putchar(ch);
    }

    putchar('\n');
    fclose(fp);

    return EXIT_SUCCESS;
}

[LINUX 基础] tar 压缩解压缩

1. 解压
tar zxvf foo.tar.gz  (tar.gz)
tar zxvf foo.tar.Z   (tar.Z)
tar jxvf foo.tar.bz2 (tar.bz2)
bzcat foo.tar.bz2 | tar xvf - (tar.bz2)
tar Jxvf foo.tar.xz  (tar.xz)
tar pxvf foo.tar -C /tmp/foo (指定解压缩保存路径, 路径必须存在)
tar xvf foo.tar foo/README (只解压缩出 README 文件)

z: gzip
p: 保持权限, 所属, 组信息
x: 展开 tar 文件
v: 执行过程中的详细信息
f: 原来的 tar 命令是写向磁带设备的, 指定 f 后写向磁盘

2. 压缩
tar czf foo.tar.gz foo  (tar + gzip = .tar.gz)
tar cjf foo.tar.bz2 foo (tar + bzip2 = .tar.bz2

c: 创建压缩文件

2-1. 不包含指定文件
$ cat exclude_file
*.log
*.tmp

$ tar cvf foo.tar foo/ -X exclude_file

2011年11月27日日曜日

[C 语言] 文件复制

1. 二进制文件复制
1-1. 处理超大文件用 fopen64()
1-2. fread, fwrite 可以处理任何类型数据 (struct 等), 而不会分裂数据

int binaryFileCopy()
{
    const int BUFFE_RSIZE = 256 * 1024;
    char buffer[BUFFE_RSIZE];
    FILE *in, *out;
    int len;

    if((in = fopen("in", "rb")) == NULL)
    {
        printf("the file1 can not open\n");
        return EXIT_FAILURE;
    }

    if((out = fopen("out", "wb")) == NULL)
    {
        printf("the new file can not open\n");
        return EXIT_FAILURE;
    }

    while((len = fread(buffer, sizeof(char), BUFFE_RSIZE, in)) > 0)
    {
        fwrite(buffer, sizeof(char), len, out);
        memset(buffer, 0, BUFFE_RSIZE);
    }

    fclose(out);
    fclose(in);

    return EXIT_SUCCESS;
}

2. 文本文件复制
int textFileCopy()
{
    char ch;
    FILE *in;
    FILE *out;

    if((in = fopen("in", "r")) == NULL)
    {
        printf("can't open the file\n");
        return (EXIT_FAILURE);
    }

    if((out = fopen("out", "w")) == NULL)
    {
        printf("can't open the file\n");
        return (EXIT_FAILURE);
    }

    while(!feof(in))
    {
        if ((ch = fgetc(in)) != EOF)
            fputc(ch, out);
    }

    /*
        while((ch = fgetc(in)) != EOF)
        {
            fputc(ch, out);
        }
    */

    fclose(in);
    fclose(out);

    return EXIT_SUCCESS;
}

2011年11月26日土曜日

[C 语言] 文件操作模式

1. "r" 或 "rt"
只读打开一个文本文件, 不存在返回空指针

2. "w" 或 "wt"
只写打开或建立一个文本文件, 不存在则创建

3. "a" 或 "at"
只写 (追加) 打开一个文本文件, 不存在则创建

4. "rb"
只读打开一个二进制文件, 不存在返回空指针

5. "wb"
只写打开或建立一个二进制文件, 不存在则创建

6. "ab"
只写 (追加) 打开一个二进制文件, 不存在则创建

7. "r+" 或 "rt+"
读写打开一个文本文件, 不存在返回空指针

8. "w+" 或 "wt+"
读写打开或建立一个文本文件, 不存在则创建

9. "a+" 或 "at+"
读写 (追加) 打开一个文本文件, 不存在则创建

10. "rb+"
读写打开一个二进制文件, 不存在返回空指针

11. "wb+"
读写打开或建立一个二进制文件, 不存在则创建

12. "ab+"
读写 (追加) 打开一个二进制文件, 不存在则创建

[基础知识] 文本文件与二进制文件

1.
文本文件和二进制文件在 Windows 与 Linux 下, 物理上是相同的 (二进制串存储), 但逻辑都是不同的

2
计算机的存储在物理上是二进制的, 所以文本文件与二进制文件的区别并不是物理上的, 而是逻辑上的, 这两者只是在编码层次上有差异
文本文件, 是字符序列, 存储的是基于字符的编码, 如 1 个 unicode 字符存储为 2 字节, 1 一个 ascii 字符存储为 1 字节, 在读取或写入时需编码转换 (二进制到指定编码)
二进制文件, 存储的就是对应的值在内存中的形式, 值编码

3.
以文本方式或以二进制方式打开, 与此文件是文本文件还是二进制文件是根本无关的概念

4.
以什么方式打开, 对于 Linux 没有不同, 只对 Windows 不同, 并且不同也仅仅影响到在 Windows 下以文本方式打开时, 并且以文本方式写入时, 将内存缓冲区中的 '\n' 转换成 '\r\n', 再写入文件, 以文本方式读取时, 将 '\n' 转换成 '\r\n', 送入内存缓冲区

+-------+-----------+-------+----+--------------------------+
| 平台   | 打开方式   | 写入方式|影响 |        说明               |
+-------+-----------+-------+----+--------------------------+
|Win/Lin|二进制/文本  |二进制  |无   |                          |
|Lin    |二进制/文本  |文本    |无   |                          |
|Win    |二进制      |文本    |无   |                          |
|Win    |文本        |文本    |有*  |\n (内存) <--> \r\n (文件) |
+-------+-----------+--------+----+-------------------------+
* windows 下, 以文本方式打开, 并且文本方式写入时, 会有 '\n' 到 '\r\n' 的转换

5. 文本工具打开一个文件的过程
记事本, 它首先读取文件物理上所对应的二进制比特流, 然后按照所选择的解码方式来解释这个流, 在将解释结果显示出来. 一般来说,如果选取的解码是 ASCII 码, 它 8 bit 8 bit 地来解释这个文件流. 文本文件的存储与其读取基本上是个逆过程, 而二进制文件的存取与文本文件的存取差不多, 只是编解码方式不同而已

6. 文本文件与二进制文件的优缺点
文本文件与二进制文件的区别仅仅是编码上不同, 他们的优缺点就是编码的优缺点
文本文件优点: 将字符输出的操作中不需转换直接输出
文本文件缺点: 一个字符占一个字节, 文件占用的存储空间较多, 读写时需要转换, 访问的时空效率不高
二进制文件优点: 数据与数据在内存中的表示形式一致, 占单元数与操作系统和数据类型有关, 存储数据时非常紧凑, 占用存储空间较少, 在读写时不需进行转换, 具有较高的时空效率
二进制文件的缺点: 二进制文件无法直接以字符形式输出, 必须要经过转换

2011年11月25日金曜日

[工具使用] Eclipse 设置

1. 设置 debug 或 run 启动参数
项目右键 -> Debug As -> Debug Configurations... -> Arguments
Program Arguments: 命令行参数 (文件名等)
VM Arguments: VM 参数 (-D 等, java --help)

2. 网络设置 (代理)
Menu -> Window -> Preferences General -> Network Connection 的
Active Provider 设置为 Manual, 设定 HTTP, HTTPS, SOCKS proxy 主机名和端口后 OK

就可以 Help -> Install New Software 了

3. 编码方式
3-1. 设置 jsp 等文件模板中 ${encoding} 的默认编码方式
Windows -> Preferences -> Web -> JSP Files
在编码下拉框中选择 UTF-8 即可
同样适用与其他文件 (css, html 等)

3-2. 设置默认打开或保存代码文件的编码方式

3-2-1. 把某一类文件 (jsp) 全部转化为某种编码
Windows -> Preferences -> General -> Content Types
选择所需要更改的文件类型, 在 Default encoding 中输想要的编码 (UTF-8), 按下 Update, 则所有 jsp 文件将被自动转化编码.

3-2-2. 更改工作区的编码 (打开, 读取文件的编码方式)
Windows -> Preferences -> General -> Workspace
在 Text file encoding 中选择 Other, 并输入想要的编码 (这里是 UTF-8), 按确定即可.


4. Tomcat 在控制台上显示乱码

window->preferences->tomcat->tomcatx.x->jdk->optional java VM argume
-Dfile.encoding=SJIS <= 操作系统编码

[项目管理] CVS 的使用

1. 项目初期导入

1-1. 初始化一个版本库
版本库 repository 可以包含多个 模块 module, 每个模块可以当作一个项目树
$ cvs -d /path/to/repo init

1-2. 服务器端导入
$ cd /path/to/src
$ cvs -d /path/to/repo import -I "*.class" -m "pjname first import by local" pjname vender_tag release_tag

$ cd /path/to/src
$ export CVSROOT=/path/to/repo
$ cvs import -I "*.class" pjname vender_tag release_tag

1-3. 客户端导入
$ cd /path/to/src
$ cvs -d :pserver:go@hostname:/path/to/repo login
$ cvs -d :pserver:go@hostname:/path/to/repo import -m "pjname first import by remoting" pjname vender_tag release_tag

参数说明
cvs -d: 指定项目本地目录
-I: 指定被忽略的文件 (仅在本次操作时生效, 可设置 cvsignore, 所有处理都生效)
pjname: 导入模块名 (checkout 时将使用的模块名)

2. Checkout
$ cvs -d :pserver:go@localhost:/home/cvsroot/project1 co -d src_cvs src

cvs -d: 指定项目地址
co -d src_cvs: 指定 checkout 时本地工作目录
src: cvs 中的模块名 (import 时指定)

3. 设置 cvs 忽略文件或目录 ???

3-1. ~/.cvsignore
执行 cvs 命令的用户 home 目录, 对所有操作生效

3-2. .cvsignore
某用作目录 (子目录), 只对本目录生效, 不影响子目录

3-3. 内容
$ cat .cvsignore

.cvsignore
*.jar
*.class

4. 删除文件或文件夹

4-1. 删除项目仓库
直接在服务器上删除文件夹即可

4-2. 删除文件
$ cvs remove -f filename
$ cvs add filename (未 commit 之前, 撤消删除)
$ cvs commit

4-3. 删除文件夹
$ cvs remove -f dirname
$ cvs add dirname (未 commit 之前, 撤消删除)
$ cvs commit
$ cvs up -P

5. 添加文件或文件夹
5-1. 添加文件
$ cvs add filename
$ cvs remove -f filename (未 commit 之前, 撤消添加文件)
$ cvs commit filename

5-2. 添加文件夹 (文件, 子文件夹等要分别添加)
$ cvs add dirname
$ cvs remove -f dirname (未 commit 之前, 撤消添加文件夹)
$ cvs add dirname/*.java (最好指定后缀)

5-3. 添加二进制文件
5-3-1. 客户端提交时指定
$ cvs add -kb filename

5-3-2. 提交后指定在服务器端指定
$ cvs admin -kb filename
$ cvs up -A (之后需要更新客户端本地工作目录)

6. 修改文件或文件夹名称 (去旧加新)
$ mv oldname newname
$ cvs remove oldname
$ cvs add newname
$ cvs commit -m "renamed oldname to newname" oldname newname

7. 更新本地工作目录

7-1. 对比本地与服务器之间有无不同
$ cvs -q -n up
-q: 不显示一般信息
-Q: 显示更少的信息
-n: 不真正执行写入之类的操作

7-2. 强制更新本地文件, 覆盖本地
$ cvs up filename -C

7-3. 将 cvs 中添加删除文件夹反映到本地工作目录
$ cvs up -d -P
-d: 将 cvs 中增加的文件夹增加到本地工作目录
-P: 将 cvs 中删除的文件夹从本地工作目录删除

7-4. 改变本地工作目录所工作的分支

7-4-1. 指定分支
$ cvs up -r branch_name

7-4-2. 恢复到最新 HEAD 分支 (none)
$ cvs up -A

7. 2 版本间比较
$ cvs diff -r rev1 -r rev2 filename

8. 显示文件每行版本
$ cvs ann filename (显示文件中每行是最终是由哪个版本增加或更新的, 删除的行不显示)

9. 显示 tag, branch 等信息
$ cvs status -v
Sticky Tag: 当前本地工作目录工作在哪个 branch 上, 默认是 trunk (none)

10. 管理 tag 和 branch

10-1. 创建, 删除 tag
$ cvs tag tag_release_1_0
$ cvs tag -d tag_release_1_0 (删除)

10-2. 创建, 删除 branch
$ cvs tag -b branch_release_1_0 (以当前主干最新版本创建分支)
$ cvs tag -d -B branch_release_1_0 (删除)

11. 项目 release 之后的 bug 修改
为了项目发布后的 bug 修改, 创建版本分支, 3.1 版本的开发继续在 trunk 中进行

11-1. 从 tag_release_3_0 导出一个 branch_bugfix_3_0
$ cvs tag -b -r tag_release_3_0 branch_bugfix_3_0 [module]

11-2. checkout 出指定 branch 的代码 (按 tag checkout 的代码是只读的, 不能 commit)
$ cvs checkout -r branch_bugfix_3_0 [module]

11-3. 修改 bug 后, 创建新的 tag
$ cvs tag tag_release_3_0_1

11-4. 如果所修改的 bug, 在 3.1 版本中也需要对应, 可以合并修改到 3.1 当前代码中
进入 3.1 版本中所需要合并修改的目录
$ cvs update -j tag_release_3_0_1
$ cvs commit

12. 代理设置
$ cvs "-d:pserver;proxy=proxy.host.com;proxyport=80:anonymous@cvs.pgfoundry.org:/cvsroot/textsearch-ja" login
$ cvs "-d:pserver;proxy=proxy.host.com;proxyport=80:anonymous@cvs.pgfoundry.org:/cvsroot/textsearch-ja" checkout <module_name>

13. 查看 cvs 服务器 pserver 的 2401 端口是否打开
$ telnet www.cvs.com 2401

14. 帮助信息
$ cvs --help-options
$ cvs --help-commands
$ cvs --help import

15. 查看 CVS 中存在的分支 (Branch)
$ cvs log -h | awk -F"[.:]" '/^\t/&&$(NF-1)==0 { print $1}' | sort -u

16. 在 CVS 中使用 SSH
[客户端]
$ export CVS_RSH=ssh
$ cvs -d server:/proj/cvsroot co <module_name>

[服务器端设置 TODO]

17. 从服务器导出不包含 CVS 目录的文件
$ cvs --help export

18. $HOME/.cvsrc 文件
存在用户主目录下, 指定 cvs 命令默认执行的选项

$ cat $HOME/.cvsrc
cvs -q
diff -u -b -B
checkout -P
update -d -P

19. 修改提交后的注释
cvs admin -m 1.3:"write some comments here" file_name
1.3 : 版本号

20. rcsinfo rcstemplate
...

21. 恢复旧版本
21-1. 文本文件
$ cvs -Q update -p -r 1.35 file_name > file_name
$ cvs commit -m "コメント" file_name

21-2. 二进制文件 (手动恢复到旧版本)
$ mv file_name file_name.new
$ cvs up -r1.1 file_name
$ mv file_name file_name.old
$ cvs up -A file_name
$ mv file_name.old file_name
$ cvs commit file_name

2011年11月24日木曜日

[项目管理] CVS 在 CentOS5 上的安装

基本概念

1. trunk (主干)
开发的主线, 保存最新的代码, 默认 branch 是 main, 默认 tag 是 head

2. tag (标记)
特定阶段的里程碑, 反映历史记录, 根据 tag checkout 的文件不可修改

3. branch (分支)
基于 tag 创建, checkout 时指定版本的 branch, 可以取出该 branch 的最新代码, 修改后可提交

例如,
3.0 开发完成, 这个时候要做一个 tag (tag_release_3_0), 然后基于这个 tag 做 release, 比如安装程序等. trunk 进入 3.1 的开发.但是 3.0 发现了 bug, 那么就需要基于 tag_release_3_0 做一个 branch (branch_bugfix_3_0), 基于这个 branch 进行 bugfix, 等到 bugfix 结束, 做一个 tag (tag_release_3_0_1), 基于 tag_release_3_0_1 进行 release. 然后, 根据需要决定 branch_bugfix_3_0是否并入 trunk.
--------------------------------------------------------------------------------
安装

1. CVS 服务器的安装

1-1. 查看你的操作系统上是否安装了 CVS
rpm -qa | grep cvs
rpm -qa | grep xinetd

1-2. 建立 cvs 用户组
groupadd cvs

1-3. 建立 cvs 组的 cvsroot 用户 (所属目录 /home/cvsroot)
useradd cvsroot
usermod -g cvs -G cvs cvsroot

1-4. 为 cvsroot 用户添加密码
passwd cvsroot

1-5. 改变 /home/cvsroot/ 的目录属性
chown cvsroot.cvs -R /home/cvsroot

1-6. 创建项目仓库目录
su - cvsroot
mkdir /home/cvsroot/project1
mkdir /home/cvsroot/project2

1-7. 建立仓库
cvs –d /home/cvsroot/project1 init
cvs –d /home/cvsroot/project2 init
chmod –R 770 /home/cvsroot/project1 /home/cvsroot/project2

1-8. 建立 CVS 服务启动文件 (xinetd 方式)
su -
yum install xinetd
cd /etc/xinetd.d

1-8-1. 创建配置文件
vi cvspserver
service cvspserver
{
    disable = no
    flags = REUSE
    socket_type = stream
    wait = no
    user = root
    server= /usr/bin/cvs
    server_args= -f --allow-root=/home2/cvsroot/project1 --allow-root=/home2/cvsroot/project2 pserver
    log_on_failure += USERID
}

或者

1-8-2. 创建配置文件
vi cvspserver
service cvspserver
{
    disable = no
    flags = REUSE
    socket_type = stream
    wait = no
    user = root
    server = /home/cvsroot/cvs.run
    log_on_failure += USERID
}

vi /home/cvsroot/cvs.run
#!/bin/bash
/usr/bin/cvs -f \
    --allow-root=/home/cvsroot/project1 \
    --allow-root=/home/cvsroot/project2 \
    pserver

chmod +x /home/cvsroot/cvs.run

1-9. 配置自启动
chkconfig xinetd on
chkconfig --list xinetd
cat /etc/services | grep cvspserver

cvspserver 2401/tcp #pserver cvs service
cvspserver 2401/udp #pserver cvs service

1-10. 启动 cvs 服务
/etc/init.d/xinetd restart

1-11. 检查 cvspserver 服务是否已经启动
netstat -l |grep cvspserver


2. CVS 服务的用户管理

2-1. 使用 Linux 系统用户
cvs 搭建好之后, 它直接读取 Linux 登陆用户管理权限, 为了让用户能够正常的 check out, 必须增加用户的附加组
usermod -G cvs username

2-2. 创建可以登陆 cvs 服务器的用户名和密码 (不同于系统用户)
su cvsroot
vi /home/cvsroot/project1/CVSROOT/passwd

go:*****:cvsroot
p1:*****:cvsroot

#>vi /cvsroot/project2/CVSROOT/passwd

trotter:*****:cvsroot
gary:*****:cvsroot

***** 为密码, 由以下文件生成
vi /home/cvsroot/passwd.pl

#!/usr/bin/perl
srand (time());
my $randletter = "(int (rand (26)) + (int (rand (1) + .5) % 2 ? 65 : 97))";
my $salt = sprintf ("%c%c", eval $randletter, eval $randletter);
my $plaintext = shift;
my $crypttext = crypt ($plaintext, $salt);
print "${crypttext}
";

chmod a+x /home/cvsroot/passwd.pl

生成一个密码是 123456, 用其替换 passwd 文件中的 *****
/home/cvsroot/passwd.pl "123456"


3. 测试
cvs -d :pserver:go@localhost:/home/cvsroot/project1 login
cvs -d :pserver:go@localhost:/home/cvsroot/project1 logout


4. 日志
tail /var/log/messages
--------------------------------------------------------------------------------

[C 语言] codeblocks 配置使用 gnu readline

1. 使用 readline (设置 Linker)
1-1. 项目右键 -> Buil Options... -> Linker Settings -> Add -> OK
1-2. Settings -> Compiler and debugger... -> Global compiler settings -> Linker Settings -> Add -> OK
添加 readline, curses 等

2. 测试代码
#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

int main()
{
    char *ptr;
    using_history();
    read_history(NULL);
    while((ptr = readline("> ")))
    {
        add_history(ptr);
        puts(ptr);
        free(ptr);
    }
    write_history(NULL);

    return 0;
}

2011年11月21日月曜日

[C 语言] 字符串处理函数

1. 头文件
#include <string.h>


2. 字符串复制

2-1. strcpy(str1, str2)
2-1-1. strlen(str1) >= strlen(str2) + 1 ('\0')
2-1-2. str1的内容完全被覆盖
2-1-3. str2 可以是 char[] 或 char*
char str1[] = "abc", str2[] = "123";
strcpy(str1, str2); # str2 -> str1

2-2. strncpy(str1, str2, n)
2-2-1. strlen(str1) >= (n 或 strlen(str2) + 1)
2-2-2. str1的内容部分或完全被覆盖
2-1-3. str2 可以是 char[] 或 char*
n >  strlen(str2) : 不足 n 的部分用 '\0' 补齐, 最后再加 '\0',完全覆盖 str1
n <= strlen(str2) : 复制 n 个字符, 最后不添加 '\0', 部分覆盖 str1


3. 字符串连结

3-1. strcat
char str1[] = "abc", str2[] = "123", p[6];
strcat(str1, str2); # 相当于 str1 = str1 + str2

3-2. strncat
strncat(str1, str2, n);
n >  strlen(str2) : 连结 str2 直到末尾 (其中要包括结尾的 '\0')
n <= strlen(str2) : 连结 str2 中前 n 个字符, 再在末尾添加 '\0'


4. 字符串比较 (大小关系按照 abc 顺序比较)
char *str1 = "abc", *str2 = "abc";
int res = strcmp(str1, str2);
int res = strncmp(str1, str2, n); ('\0' 以后不做比较)
=0 : str1 = str2
<0 : str1 < str2
>0 : str1 > str2


5. 字符串长度
得出的长度不包含结尾的 '\0', 但在 char[] 里保存字符串时要包含 '\0' 的长度 1
char *str1 = "abc";
int len = strlen(str1); # 3


6. 字符查找
字符若存在, 则返回字符在串中的指针, 不存在则返回 NULL
char *str1 = "abcba";
char *p = strchr(str1, 'b'); (从左端开始查找)
char *p = strchr(str1, 0); (返回字符串末尾 \0 的指针)
char *p = strrchr(str1, 'b'); (从右端开始查找, 可以指定 strrchr(str1, 0))


7. 字符串查找
子串若存在, 则返回子串在父串中的指针, 不存在则返回 NULL
char *str1 = "abcba";
char *p = strstr(str1, "bc"); (从左端开始查找, 子串是 '\0' 时, 返回 str1)
char *p = strpbrk(str1, "cb"); (从左端开始查找, 子串中任意一字符在父串中出现的最靠前的指针)


8. 字串在父串中的长度

8-1. 从左端开始, 字串在父串中出现的长度, 不包含 '\0' 以及其后的字符
char *str1 = "abcba";
int len = strspn(str1, "acd"); (a -> 1)
int len = strspn(str1, "abcde"); (abc -> 3)

8-2. 从左端开始, 不包含子串中任意字符的长度
char *str1 = "abcba";
int len = strcspn(str1, "db"); (1, 直到 b 出现)
int len = strcspn(str1, "12"); (5, 1 2 均未出现)
int len = strcspn(str1, "1b"); (1, 直到 b 出现)


9. 字符串分割
可以指定复数个分割符
char str[] = "ABCD-ef.1234-G", *tp;
tp = strtok(str, "-."); puts(tp);
while (tp != NULL)
{
    tp = strtok(NULL,"-.");
    if (tp != NULL) puts(tp);
}


10. 在 n 字节 buff 中查找字符
可以包括 \0 -> 当作普通字符进行查找, 返回字符指针或 NULL
char str[] = "abcdef\0ghij";
char *p = memchr(str, 'h', 11);


11. 比较 n 字节中的 buff 串
以 unsigned char 的形式比较
char buf1[] = "\0abc\0de";
char buf2[] = "\0abc\0de";
char buf3[] = "\0abcdef";
memcmp(buf1, buf2, 7); (buf1 == buf2)
memcmp(buf1, buf3, 7); (buf1 != buf3)


12. 按照错误号返回错误信息
strerror(0)
strerror(10)