LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: darksun

简单的从一个buffer中提取word的编程

[复制链接]
发表于 2003-9-9 19:47:31 | 显示全部楼层
最初由 darksun 发表
我编写的函数编译成功可是会有一个
warning: function returns address of local variable
不影响执行。我想是因为我在主函数中定义的一个
char *bufline
bufline = getword(buf);
是不是因为bufline本身没有分配内存,而getword函数执行后释放内存,那么bufline中还能保存返回值吗?在哪里呢?


老兄写的当然不行了,你把 word[] 定义成局部数组,它的生命周期仅局限于 getword() 体内,当 getword() 调用时, 有个压栈动作,结束调用时,有个出栈动作,也就是说这时 word[] 分配的空间被释放了,而你这时把 word[] 的地址返回给外部指针 bufline ,一运行就出现段错误。
我觉得类似这种例子,不妨通过传递参数的方法,结构上要清晰一些:

  1. #include <stdio.h>

  2. void getword(char *p, char *q)
  3. {
  4.         while( isspace(*p)) p++;
  5.         while( !isspace(*p)) *q++ = *p++;
  6.         *q = '\0';
  7. }

  8. int main()
  9. {
  10.         char *buf = "aaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
  11.         char word[] = "...";

  12.         getword(buf, word);
  13.         printf("%s\n", word);
  14.         return 0;
  15. }
复制代码

运行结果:
$ aaa
发表于 2003-9-9 20:26:50 | 显示全部楼层
最初由 darksun 发表
是有缓冲区溢出的可能性,谢谢老兄。受教了。
可是---
这个只能取第一word吧?第二次执行取不了第二个word吧


可是取得第二个,因为p用的是static类型。他是从上次结束的地方重新开始

  1. char *
  2. getword(char *buf)
  3. {
  4.     static char *p = buf;
  5.     char *q;
  6.     static char word[128];
  7.     q = word;
  8.     while(isspace(*p)) p++;
  9.     while(!isspace(*p) && (q < word + 128)) *q++ =*p++;
  10.     *q = '\0';
  11.     return(word);
  12. }
复制代码


应该把word也定义成static,这样就不会有问题了。
或许应该像libinary所说的,尽量使用库函数。
发表于 2003-9-9 21:34:17 | 显示全部楼层
刚才只调试了一下楼主得程序,仔细看了一下,pupilzeng兄设置了 static 也很不错,那么在函数体外用一个指针来接收,参数可以少一些。
发表于 2003-9-9 21:46:18 | 显示全部楼层
比较麻烦但是稳妥的办法:  四个接口,一个类型:

  1. typedef struct {
  2.       char* buf;
  3.       char* cur;
  4.       size_t count;
  5. }CTX_word;

  6. int WordGetInit (CTX_word* ctx)
  7. {
  8.     ctx->buf = 0L;
  9.     ctx->count = 0;
  10.     ctx->cur = 0L;
  11.     return 0;
  12. }

  13. int WordBeginParse (CTX_word* ctx, char* buf)
  14. {
  15.     char *p;
  16.     ctx->buf = calloc ( sizeof (char), strlen (buf) );
  17.     if ( !ctx->buf )
  18.         return -1;
  19.     memcpy ( ctx->buf, buf, strlen (buf) );
  20.     ctx->count = strlen (buf);
  21.     ctx->cur = ctx->buf;
  22.     p = ctx->buf;
  23.     while ( *p ) {
  24.         if ( isspace (*p) )
  25.               *p = '\0';
  26.         p ++;
  27.     }

  28.     return 0;
  29. }

  30. char * GetNextWord (CTX_word* ctx)
  31. {
  32.     char *p;
  33.     while ( !(*(ctx->cur)) && (ctx->cur < (ctx->buf + ctx->count)) ) {
  34.         ctx->cur ++;
  35.     }

  36.     p = ctx->cur;

  37.     while ( *(ctx->cur) && ctx->cur < (ctx->buf + ctx->count)) ) {
  38.         ctx->cur ++;
  39.     }

  40.     return (*p) ? p : 0L;
  41. }

  42. void WordGetClenup (CTX_word* ctx)
  43. {
  44.     if ( ctx-> buf ) {
  45.         free (ctx->buf);
  46.         ctx->buf = 0L;
  47.     }

  48.     ctx->count = 0;
  49.     ctx->cur = 0L;
  50.     return;
  51. }
复制代码

使用的时候,现调用WordGetInit,然后设置WordGetParse, 通过GetNextWord  取得每个单词。最后使用WordGetCleanup 释放资源。    繁琐了一点,如果不追求效率的话,还是可以接受的,并且这样的好处就是避免了static带来的不可重入性。前几天写代码用strtok的时候一直出错,后来发现是因为我在调用strtok的时候,调用的别人的模块也调用了它,我faint.  上面的代码我没有验证过,呵呵,甚至没有编译,随手写的,希望对你有用。[/CODE]
 楼主| 发表于 2003-9-10 08:24:09 | 显示全部楼层
t = strtok(buf, " \t"); /* t指向第一个word的第一个字符,word由空格或\t分隔 */
while(t){
  ...; /* 提取word,如strcpy(dest, t); */
  t = strtok(NULL, " \t"); /* t指向下一个word */

strtok不行吗?用库函数好一点吧

用strtok是可以的。但是取word时,把不同的word写入不同的变量是
很不方便的。我调试了,如果只是打印出来还是很方便的。
 楼主| 发表于 2003-9-10 08:27:12 | 显示全部楼层
老兄写的当然不行了,你把 word[] 定义成局部数组,它的生命周期仅局限于 getword() 体内,当 getword() 调用时, 有个压栈动作,结束调用时,有个出栈动作,也就是说这时 word[] 分配的空间被释放了,而你这时把 word[] 的地址返回给外部指针 bufline ,一运行就出现段错误。
我觉得类似这种例子,不妨通过传递参数的方法,结构上要清晰一些:

我也知道有问题,可是奇怪的就是虽然出现了上面的警告,但是不影响执行,并且正确,奇怪奇怪!
 楼主| 发表于 2003-9-10 08:45:02 | 显示全部楼层
theseus老兄的程序我嫌太麻烦了,勿怪
pupilzeng老兄的“static char *p = buf;”这句编译通不过。
我改为static char *p;
      p =buf;
编译通过但是还是出现了“段错误”并且输出结果不正确。

发表于 2003-9-10 12:45:52 | 显示全部楼层
最初由 darksun 发表
theseus老兄的程序我嫌太麻烦了,勿怪
pupilzeng老兄的“static char *p = buf;”这句编译通不过。
我改为static char *p;
      p =buf;
编译通过但是还是出现了“段错误”并且输出结果不正确。


我这里是可以通过编译的,你那个改过之后的,当然会出错了,因为static必须初始化的。
不过我前面的函数有点问题,就是对句子结尾的处理上面。该了一下,下面是完整的一个例子。

  1. #include <iostream>
  2. #include <stdlib.h>

  3. using namespace std;

  4. char *
  5. getword(char *buf)
  6. {
  7. //返回句子buf中的一个单词,当遇到结束时,返回一个空串。
  8. //但此长度不能超过127个字母,如果超过将在127处截断,分成两个单词。
  9. //句子中只含有字母或者空格。
  10. //不做输入检查。
  11.     static char *p = buf;
  12.     char *q;
  13.     static char word[128];
  14.     q = word;
  15.     while(isspace(*p)) p++;
  16.     while(!isspace(*p) && (q < word + 128) && (*p != '\0')) *q++ =*p++;
  17.     *q = '\0';
  18.     return(word);
  19. }

  20. int main(int argc, char *argv[])
  21. {
  22.   char str[]="this is a sentence";
  23.   char word[128];
  24.   while (strcmp(strcpy(word,getword(str)), ""))
  25.   cout << word << '\t';
  26.   cout << endl;
  27.   system("PAUSE");       
  28.   return 0;
  29. }
复制代码

输出结果:
this    is      a       sentence
请按任意键继续. . .

我的编译环境:dev-c++(g++-3.2)
发表于 2003-9-10 13:59:15 | 显示全部楼层
感觉 theseus 兄那段代码写的很棒。
比较欣赏采用 struct 和 calloc() 动态内存分配。
有几点不明,何为 static 带来的不可重入性?
还有 calloc() 把数据都散布在不连续的堆上,找起来个人估计比连续存放显得要麻烦写,能具体讲讲好处吗?
 楼主| 发表于 2003-9-10 16:37:04 | 显示全部楼层
最初由 pupilzeng 发表
我这里是可以通过编译的,你那个改过之后的,当然会出错了,因为static必须初始化的。
不过我前面的函数有点问题,就是对句子结尾的处理上面。该了一下,下面是完整的一个例子。


我知道了,我的编译环境是gcc了。我用纯c编程的了。
谢谢老兄的程序对我启发很大。
我需要的这个buf是个动态了,所以不知道行不行。

我的需求就是通过一个程序去读取一个日志文件,然后把这个日志文件中的各段
写入一个数据库中。
首先,getline(*buf)读取一行日志,存到buf中。然后getword(*buf)从buf中分离各个字段。再用一个writedata写库。还有一个子进程调用judgelogmodify()判断日志文件的变化。我通过stat()函数的st_mtime修改时间来判定日志的修改不知道还有没有更好的办法。还有就是怎么才可以把指针移到最新追加的日志前面不知道大家有什么好方法来判断。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表