您的位置:首页 > 运维架构 > Linux

Linux下C语言字符串操作之分割字符串总结

2016-02-22 21:37 393 查看
http://biancheng.dnbcw.info/c/452773.html

http://biancheng.dnbcw.info/c/452774.html

http://www.oschina.net/code/snippet_2325404_47570

/article/1950797.html

c/cpp中如何分割字符串,类似于split的功能

1,分割字符串

char *strtok(char *str, const char *delim);

功能:分解字符串为一组字符串。str为要分解的字符串,delim为分隔符字符串。实质上的处理是,strtok在str中查找包含在delim中的字符并用NULL(’\0′)来替换,直到找遍整个字符串。

说明:首次调用时,str指向要分解的字符串,之后再次调用要把str设成NULL。strtok在str中查找包含在delim中的字符并用NULL(’\0′)来替换,直到找遍整个字符串。

返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。

示例:
1. #include <stdio.h>
2. #include <string.h>
3.
4. int main(){
5. //id,姓名,语文,数学,英语
6. charstr[]="2,张三,89,99,66";
7. char*token=strtok(str,",");
8. while(token!=NULL){
9. printf("%s\t",token);
10. token=strtok(NULL,",");
11. }
12. printf("\n");
13. return 0;
14. }
示例程序输出:
1. 2 张三 89 99 66
说明:str参数必须设置为数组的形式,而不是字符串常量(如:char *str="2,张三,89,99,66";),因为strtok在执行过程中会对str进行修改,必须保证str是可写的。

2,分割字符串(还是分割字符串)

char *strsep(char **stringp, const char *delim);
1. #include <stdio.h>
2. #include <string.h>
3.
4. int main(){
5. //id,姓名,语文,数学,英语
6. charstr[]="2,张三,89,99,66";
7. //str是一个指针常量,而strsep的第一个参数需要一个指向指针的指针,所以不能对str做取地址操作,
8. //这里再定义一个指针变量就可以取地址操作了。否则会出现段错误。
9. char *strv=str;
10. char*token=strsep( &strv,",");
11. while(token!=NULL){
12. printf("%s\t",token);
13. token=strsep( &strv,",");
14. }
15. printf("\n");
16. return 0;
17. }
说明:此函数也会修改第一个参数的内容,所以必须保证提供的不是字符串常量。

相关资料:
· c/c++Linux下C程序访问mysql数据库
· c/c++Linux下C语言字符串操作之分割字符串
· c/c++向量空间模型——计算文本(英文)相似度
· c/c++Linux下C语言字符串操作之连接和查找
· c/c++C语言之预处理命令
Linux下C语言字符串操作之分割字符串来源网络,如有侵权请告知,即处理!

废话不多说,直接上代码:
1. #include <stdio.h>
2. #include <mysql/mysql.h>
3.
4. #define HOST "192.168.56.2"
5. #define DB "webdevtest"
6. #define USER "webdev"
7. #define PWD "webdev"
8. #define PORT 3306
9.
10. int main(){
11. MYSQL mysql;
12.
13. //连接之前得先初始化
14. mysql_init(&mysql);
15.
//连接mysql数据库
1. if(!mysql_real_connect(&mysql,HOST,USER,PWD,DB,PORT,NULL,0)){
2. printf("连接数据库发生错误!\n");
3. return-1;
4. }
5. printf("连接数据库成功!\n");
6.
7. //设置查询时的编码
8. mysql_query(&mysql,"setnames utf8");
9.
//执行查询语句

char *sql="select * from test_sell_records limit 20";
1. if(mysql_query(&mysql,sql)!=0){
2. printf("执行mysql语句发生错误!\n");
3. mysql_close(&mysql);
4. return-1;
5. }
6. //获取结果集
7. MYSQL_RES*result=mysql_store_result(&mysql);
8. unsigned inti,num_fields;
9. //获取字段数
10. num_fields=mysql_num_fields(result);
1. //循环打印出各行
2. MYSQL_ROW row;
3. while((row=mysql_fetch_row(result))!=NULL){
4. for(i=0;i<num_fields;i++){
5. printf("%s\t",row[i]);
6. }
7. printf("\n");
8. }
9.
10. //释放结果集
11. mysql_free_result(result);
12. //关闭连接
13. mysql_close(&mysql);
14. return 0;
15. }
以上用到的函数为:

mysql_init()

mysql_real_connect()

mysql_query()

mysql_store_result()

mysql_num_fields()

mysql_fetch_now()

mysql_free_result()

mysql_close()

还有,编译的时候不要忘记了添加 -lmysqlclient 参数:
1. gcc -o mysql_test mysql_test.c-lmysqlclient
在Linux下用C语言开发mysql客户端程序,需要安装mysql开发包,请参考:
http://blog.chinaunix.net/uid-20769015-id-3540362.html
mysql官方C API参考:http://dev.mysql.com/doc/refman/5.1/en/c-api-functions.html
相关资料:
· c/c++Linux下C程序访问mysql数据库
· c/c++Linux下C语言字符串操作之分割字符串
· c/c++向量空间模型——计算文本(英文)相似度
· c/c++Linux下C语言字符串操作之连接和查找
Linux下C程序访问mysql数据库来源网络,如有侵权请告知,即处理!

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <stdlib.h>

#include <errno.h>

#include <fcntl.h>

#include <dirent.h>

#include <pwd.h>

#include <time.h>

#include <grp.h>

#include <mysql/mysql.h>

#define N 7

MYSQL my_sql;

DIR * my_dir = NULL;

char *args
={NULL,NULL,NULL,NULL,NULL,NULL,NULL};

void read_dir(char *);

void connect_mysql();

void set_work();

void get_file_infor(const char *,char *);

void write_mysql();

int main(int argc,char *argv[])

{

if(argc != 2)

{

fprintf(stderr,"arguments is not two!\n");

return -1;

}else

{

connect_mysql();

set_work();

read_dir(argv[1]);

}

mysql_close(&my_sql);

return EXIT_SUCCESS;

}

void read_dir(char *dirname)

{

struct dirent * dir;

int n = 0;

my_dir = opendir(dirname);

if(!my_dir)

{

fprintf(stderr,"open dir fail %s\n",strerror(errno));

}else

{

while((dir = readdir(my_dir)))

{

if(strcmp(".",dir->d_name)&&strcmp("..",dir->d_name))

{

for(;n < N;n++)

{

args
= (char*)malloc(256);

if(!args
)

{

fprintf(stderr,"malloc fail\n");

closedir(my_dir);

exit(-1);

}

}

args[0] = dir->d_name;

get_file_infor(dir->d_name,dirname);

printf("\n");

}

}

}

}

void connect_mysql()

{

mysql_init(&my_sql);

if(mysql_real_connect(&my_sql,"localhost","ma","123456ma",

"student",0,0,0))

{

printf("connect mysql success\n");

}else{

fprintf(stderr,"connect mysql fail%s",mysql_error(&my_sql));

exit(-1);

}

}

void set_work()

{

int res1,res2;

res1 =mysql_query(&my_sql,"set names utf8");

res2 = mysql_query(&my_sql,"use student");

if(res1)

{

fprintf(stderr,"set character fail!%s\n",mysql_error(&my_sql));

}else

printf("set character success !\n");

if(res2)

{

fprintf(stderr,"use student database fail !%s\n",mysql_error(&my_sql));

}else

printf("use database success !\n");

}

void write_mysql()

{

printf("\t#######start write into mysql#######\n");

int res ;

char * infor = (char *)malloc(512);

memset(infor,0,512);

sprintf(infor,"INSERT INTOFile(filename,filemode,filelink,fileown,filegroup,filetime,filesize) VALUES('%s','%s',%s,'%s','%s','%s',%s)",args[0],args[1],args[2],args[3],args[4],args[5],args[6]);

infor[strlen(infor)]= '\0';

// printf("%s\n",infor);

res = mysql_query(&my_sql,infor);

if(!res)

printf("Insert %lu rows\n",(unsignedlong)mysql_affected_rows(&my_sql));

else

fprintf(stderr,"Insert data fail%s\n",mysql_error(&my_sql));

}

void get_file_infor(const char *path1,char*arg)

{

struct stat filestat;

int n = 0;

char *path = (char *)malloc(256);

memset(path,0,256);

sprintf(path,"%s/%s",arg,path1);

if(lstat(path,&filestat))

{

fprintf(stderr,"get inode information fail%s\n",strerror(errno));

return;

}else

{

switch(filestat.st_mode & S_IFMT)

{

case S_IFBLK: args[1] ="block";break;

case S_IFCHR: args[1] = "character";break;

case S_IFDIR: args[1] = "directory";break;

case S_IFIFO: args[1] = "FIFO/pipe";break;

case S_IFLNK: args[1] = "symlink";break;

case S_IFREG: args[1] = "regularfile";break;

case S_IFSOCK: args[1] = "socket";break;

default: args[1] = "unknown";

}

sprintf(args[2],"%ld ",(unsigned long)filestat.st_nlink);

struct passwd * passwd = getpwuid(filestat.st_uid);

sprintf(args[3],"%s ",passwd->pw_name);

struct group * group = getgrgid(filestat.st_gid);

sprintf(args[4],"%s ",group->gr_name);

struct tm *filetime = gmtime(&(filestat.st_mtime));

sprintf(args[5],"%d/%d/%d",filetime->tm_year+1900,filetime->tm_mon,filetime->tm_mday);

sprintf(args[6],"%lld",filestat.st_size);

write_mysql();

}

}

在python中,如果要求当前时间的unix时间戳,我特别喜欢这么用:
[python] view plaincopy

1. import time
2. timestr = time.time()
3. timestamp = int(timestr.split('.')[0])

这里的split函数,我非常喜欢,在java、c#和python中都有,很方便,不用担心踩地雷,但是C/CPP中,就没有了,这点比较遗憾。
如果要处理一个字符串型的“192.168.1.254”,想把每个字段都分开,怎么办呢,C标准库中有函数strtok()的实现,可以一用。
[cpp] view plaincopy

1. #include<stdio.h>
2. #include<stdlib.h>
3. #include<string.h>
4.
5. int main()
6. {
7. char ip_str[] = "192.168.1.250";
8. char *ip_arr[4] ;
9. char * s = strtok(ip_str, ".");
10. int i=0;
11. while(s)
12. {
13. ip_arr[i] = s;
14. s = strtok(NULL, ".");
15. i++;
16. // printf("%s\n",s);
17. }
18.
19. for(i=0; i<4; i++)
20. printf("%s\n",ip_arr[i]);
21. }

在这里,strtok是非线程安全的,这点也可以在程序的第二次strtok调用中看到,因此linux用strsep来替换strtok了,我在linux2.6.22的源码/lib/string.c和linux-3.3中同文件中,c文件开头就是这样一段话:
[html] view plaincopy

1. /*
2. * linux/lib/string.c
3. *
4. * Copyright (C) 1991, 1992 Linus Torvalds
5. */
6.
7. /*
8. * stupid library routines.. The optimized versions should generally be found
9. * as inline code in <asm-xx/string.h>
10. *
11. * These are buggy as well..
12. *
13. * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
14. * - Added strsep() which will replace strtok() soon (because strsep() is
15. * reentrant and should be faster). Use only strsep() in new code, please.
16. *
17. * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
18. * Matthew Hawkins <matt@mh.dropbear.id.au>
19. * - Kissed strtok() goodbye
20. */

因为strsep是线程安全的,并且速度上更快一些,所以采用strsep来替换strtok,接下来我会试一试strsep。在这里感慨下,没事的时候或者写程序的时候,用man和查看源码的方式,能学到很多基本的知识,比如内核源码的lib文件夹下,linux内核使用的rbtree结构,还有lib文件夹的string.c,include下的string.h里的各种strcpy,strcat等基本函数的实现,都是非常经典而且久经考验的。
在strtok使用的代码里,有两处很有意思。
其中一个,修改第7行,如下所示:
[cpp] view plaincopy

1. #include<stdio.h>
2. #include<stdlib.h>
3. #include<string.h>
4.
5. int main()
6. {
7. char *ip_str = "192.168.1.250";
8. char *ip_arr[4] ;
9. char * s = strtok(ip_str, ".");
10. int i=0;
11. while(s)
12. {
13. ip_arr[i] = s;
14. s = strtok(NULL, ".");
15. i++;
16. // printf("%s\n",s);
17. }
18.
19. for(i=0; i<4; i++)
20. printf("%s\n",ip_arr[i]);
21. }

将char ip_str[] ="192.168.1.250";改为char *ip_str = "192.168.1.250";就会core
dump,通过gdb和core文件来看,程序崩溃在了
[cpp] view plaincopy

1. Program terminated with signal 11, Segmentation fault.
2. #0 strtok () at ../sysdeps/i386/i686/strtok.S:245
3. 245 movb $0, (%edx) /* Terminate string. */
4. (gdb) where
5. #0 strtok () at ../sysdeps/i386/i686/strtok.S:245
6. #1 0x0804841e in main () at test.c:9

而这段代码在VS下是没有问题的,所以这个原因需要找一下。
这个原因找到了,在链接http://www.cnblogs.com/longzhao1234/archive/2012/05/31/2528317.html
通过阅读源代码,因为函数内部会修改原字符串变量,所以传入的参数不能是不可变字符串(即文字常量区)。

如 char *tokenremain ="abcdefghij"//编译时为文字常量,不可修改。

strtok(tokenremain,"cde");

strsep(&tokenremain,"cde");

编译通过,运行时会报段错误。
VS在很多情况下要比GCC优秀很多,VS的CPP支持是最全面的,可以这么说。好多CPP的作者啦,大牛啦,都是M$的VC组的,好牛逼的地方。
另外在改一处,这次只改第16行,将printf语句注释掉,代码如下:
[cpp] view plaincopy

1. #include<stdio.h>
2. #include<stdlib.h>
3. #include<string.h>
4.
5. int main()
6. {
7. char ip_str[] = "192.168.1.250";
8. char *ip_arr[4] ;
9. char * s = strtok(ip_str, ".");
10. int i=0;
11. while(s)
12. {
13. ip_arr[i] = s;
14. s = strtok(NULL, ".");
15. i++;
16. printf("%s\n",s);
17. }
18.
19. for(i=0; i<4; i++)
20. printf("%s\n",ip_arr[i]);
21. }

又崩溃了,我也整个人都不好了。
分析core文件,出错如下:
[cpp] view plaincopy

1. Program terminated with signal 11, Segmentation fault.
2. #0 __strlen_ia32 () at ../sysdeps/i386/i586/strlen.S:99
3. 99 movl (%eax), %ecx /* get word (= 4 bytes) in question */
4. (gdb) where
5. #0 __strlen_ia32 () at ../sysdeps/i386/i586/strlen.S:99
6. #1 0x00b9ddd5 in _IO_puts (str=0x0) at ioputs.c:37
7. #2 0x0804846b in main () at test.c:16

令人欣慰的是,VS在这句也崩了。
根据core文件的提示,在#0处,在strlen函数这里崩溃了,我判断,是strtok阶段字符数组到最后,要在printf("%s\n",s);处打印时,由于没有'\0'符号,所以缓冲区无法截断,最后溢出导致printf崩溃,所以我重新声明一个长度为sizeof(ip_str)+1的字符数组,将ip_str复制进去,并将最后一个字符置为'\0',代表字符结束,结果依然崩溃。
如果我把printf("%s\n",s);改为printf("%s\t",s);,因为printf是打印到标准输出中,而标准输出是行缓冲的,对于'\n',代表行缓冲结束,需要输出,如果我不让他输出,会怎样?
打印结果为:
[cpp] view plaincopy

1. 168 1 250 (null)

好吧我也不知道是什么了,而且这个结果与是否有'\0'符号无关。

这两个地方一定要找出来问题,嗯。

接下来我们看看strsep的用法吧
[cpp] view plaincopy

1. #include<stdio.h>
2. #include<stdlib.h>
3. #include<string.h>
4.
5. int main()
6. {
7. char ip_str[] = "192.168.1.250";
8. char *p = ip_str;
9. char *ip_arr[4] ;
10. char * s = strsep(&p, ".");
11. int i=0;
12. while(s)
13. {
14. ip_arr[i] = s;
15. s = strsep(&p, ".");
16. i++;
17. // printf("%s\n",s);
18. }
19.
20. for(i=0; i<4; i++)
21. printf("%s\n",ip_arr[i]);
22. }

用法也差不多。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: