LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
12
返回列表 发新帖
楼主: darksun

再议从buffer中提取word

[复制链接]
发表于 2003-10-17 09:09:59 | 显示全部楼层
学习学习再学习!
向进取精神致敬!!
 楼主| 发表于 2003-10-17 09:38:53 | 显示全部楼层
1、直接修改linebuf,把第n个词后面的分隔符改成'\0',直接返回wordbuf = linebuf + begin
》》这样直接修改了linebuf,破坏linebuf那么如果多次取词就会出现问题。

2、函数内部声明一个static char *buf;每次动态分配,返回wordbuf = buf
》》静态的,不太好吧?

3、函数声明改成int getnword(char *linebuf,char *wordbuf,char *delims, int n),返回第n个词的长度,如果调用者保证wordbuf够大可以直接调用,否则可以用len = getnword(linebuf, NULL, delims, n);
先取得大小,再为wordbuf分配空间,再调用
getnword(linebuf, wordbuf, delims, n);
这里的函数内部要对wordbuf == NULL区别处理  
》》我老是觉得再函数内动态分配空间。如果只是自己用的话,还知道free
》》若是别人用不知道释放,岂不是造成内存泄漏?这样通用性就差了。
要不可不可以折中一下,若是strncpy(wordbuf, linebuf + begin, end - begin);中的end-begin 大于strlen(wordbuf)那么就把第n个词截断。保证不缓冲区溢出。当然了,wordbuf要设计的足够大,保证应用。但是别人想制造缓冲区溢出那么就会起到保护的作用。

》》如果是用动态分配空间的话低N个词的长度就是end-begin呀。
wordbuf = (char*)malloc(end-begin +1);
strncpy(wordbuf, linebuf + begin, end - begin)


个人想法,不知道对不对
发表于 2003-10-18 00:56:45 | 显示全部楼层
因为wordbuf的长度函数内是不知道的,所以麻烦是肯定的,我也认为这三种办法都不太好,不过也还都可以考虑,第一种办法和库函数strtok差不多,主要是要求调用者自己考虑,如果你只用一次就可以直接调用,如果要调用几次就需要备份linebuf,比如
buf = (char *)malloc(strlen(linebuf) + 1);
strcpy(buf, linebuf);
getnword(buf, ……
不过讨厌的是这里buf还不能释放,如果需要使用wordbuf的话还需要拷贝wordbuf以后才能free(buf),不过这一点也可以继续改进。
第二种办法的问题是调用getnword以后要马上拷贝wordbuf,否则下次调用getnword就会破坏上次调用的结果。
类似这两种调用的要求在库函数里也经常能见到。
第三种办法的意思不是在函数内分配空间,而是在函数调用者分配,先用NULL调用一次求得空间大小,再分配空间,再用新分配的wordbuf调用一次:
len = getnword(linebuf, NULL, delims, n);
wordbuf = (char *)malloc(len + 1);
getnword(linebuf, wordbuf, delims, n);
……
free(wordbuf);
当然,如果一开始wordbuf就和linebuf一样大,就可以保证不溢出,不过就是有点浪费。

在函数内分配空间我觉得不好,
wordbuf = (char*)malloc(end-begin +1);
问题在于释放,我一般认为在那一层分配的空间就应该在那里释放,这个实在不好做,如果在函数内分配的空间要在函数外释放的话,总是感觉不好,要不然就忘了,要不然就让人觉得怪怪的,至少让人觉得这个函数设计的有问题。
还有,你说的“end-begin 大于strlen(wordbuf)”,这个很难保证,因为strlen求的是字符串的长度,不是空间大小,它必需遇到结尾的'\0'才行,这个和wordbuf的大小没什么关系,除非你调用以前专门把wordbuf初始化成最后一个字符是'\0',一般解决这个问题是加一个参数,表示空间大小,不过参数感觉有点多。
发表于 2003-10-18 02:48:24 | 显示全部楼层
嗯,想的可能有点多了,其实对第三种方法没有必要搞这么麻烦,把wordbuf直接返回linebuf + begin就可以了,这个函数并没有一定要得到第n个词的拷贝,我们返回第n个词在linebuf中的位置就可以了,反而更实用,如果需要这个词的拷贝的话,反正有返回的长度,可以用strncpy做一个拷贝。
如果函数返回一个分配的wordbuf,那么这个函数就只能有一个用途,就是得到第n个词是什么,wordbuf返回linebuf + begin并配合以函数返回词的长度也可以做到这一点,并且还可以有很多用法,比如改写linebuf之类的,我觉得这样更好。
大概改了一下:

  1. int
  2. getnword(char *linebuf,char **wordbuf,char *delims, int n)
  3. {
  4.   int len, begin, end = 0;
  5.   int num = 0;

  6.   if(!linebuf || !delims || n < 1)
  7.     return(0);
  8.   len = strlen(linebuf);
  9.   begin = strspn(linebuf, delims);
  10.   end = begin + strcspn(linebuf + begin, delims);
  11.   while(begin < end && begin < len){
  12.     if(++num == n){
  13.       if(wordbuf)
  14.         *wordbuf = linebuf + begin;
  15.       return(end - begin);
  16.     }
  17.     begin = end + strspn(linebuf + end, delims);
  18.     end = begin + strcspn(linebuf + begin, delims);
  19.   }
  20.   return(0);
  21. }
复制代码

第二个参数改成了char **,是为了返回位置,函数返回0表示没找到,当然也可以把第一个return(0)改成return(-1),表示错误。
用的时候可以

  1. char *wordbuf; /* linebuf, delims, n 已定义 */
  2. char buf[80];
  3. int len;

  4. if(len = getnword(linebuf, &wordbuf, delims, n)){
  5.   strncpy(buf, wordbuf, len);
  6.   buf[len] = '\0';
  7.   printf("found "%s"\n", buf);
  8. }else
  9.   printf("not found\n");
复制代码

当然,这里也可以用len + 1先给buf分配空间,
发表于 2003-10-18 03:13:50 | 显示全部楼层
我又想了一下,觉得也可以写成
char *getnword(char *linebuf, char *delims, int n, int *len);
返回第n个词的位置,用len返回这个词的长度,如果调用者只需要第n个词的位置,不关心长度的话可以用NULL作为第四个参数
发表于 2003-10-18 03:47:56 | 显示全部楼层
我看了半天才大概懂一点楼主的意图,恕我直言,应该先把问题描述的更清楚些,而不是匆匆忙忙给大堆代码,让别人从代码里去找意图,最好给个示例。
写的代码也是,注释很不详细,也不清楚,看起来非常累。
我稍修改了一下版主的代码,没动算法部分,先确认一下是否理解正确。
[code]
#include <stdio.h>
#include <string.h>

#define OK 1
#define FAIL 0

int getnword(char *linebuf, char *wordbuf, char *delims, int n)
{
    int len;
    int begin;
    int end;
    int num = 0;

    if ( !linebuf || !wordbuf || !delims || n < 1 )
        return FAIL;

    len = strlen( linebuf);
    begin = strspn( linebuf, delims);
    end = begin + strcspn(linebuf + begin, delims);

    while ( begin < end && end < len)
    {
        if ( ++num == n)
        {
            strncpy( wordbuf, linebuf + begin, end - begin);
            wordbuf[end - begin] = '\0';
            return OK;
        }
        begin = end + strspn(linebuf + end, delims);
        end = begin + strcspn(linebuf + begin, delims);
    }
    return FAIL;
}


int main()
{
    char *line= "\tWelcome\tto\tlinux world\t";

    char *delimiter = "\t";

    char word[strlen(line)];

    int status;

    status = getnword( line, word, delimiter, 3);

    if ( status == FAIL)
            printf( "Fail!\n");
    else
            printf( "%s\n", word);

    return 0;
}
如果我的理解没错,请讲一声。
 楼主| 发表于 2003-10-20 08:33:52 | 显示全部楼层
谢谢大家这么热心,
quanliking说的我受教了,刚学编程,凑合点,见谅。

quanliking理解的没有错。

代码想做的事情就是从一个bufline(动态取出的)中取出其中以delims分隔的第n个词。
也就是说
要是bufline=“1,2,3,4,”那么以,分隔的取第1个词就是1;n为2,那么就是取出就是2了这个取出的字符串要放到一个bufword中返回。当然了,是不是返回值是指向它的指针,不是很重要了
 楼主| 发表于 2003-10-20 08:41:44 | 显示全部楼层
char *getnword(char *linebuf, char *delims, int n, int *len);
返回第n个词的位置,用len返回这个词的长度,如果调用者只需要第n个词的位置,不关心长度的话可以用NULL作为第四个参数

谢谢斑竹这么用心。
以上斑竹的做法我觉得不失为一个好的解决办法,这样通用性强了很多也灵活了很多。

最初我是为了从一行配置文件中取出想要的字段,所以就直接返回需要的字段了。
不过这也提出了二个问题,
1。若是真的需要从一个函数中返回一个字符串,怎么来处理空间问题,如:从一个文件中取一行(不知道该行长度)。(不知道库函数是怎么处理的);
2。一般配置文件的读取也是通过这样读取字符串吗?(我读的代码比较少,所有顺便问一句这么弱智的问题,勿怪!)
发表于 2003-10-20 10:52:19 | 显示全部楼层
读配置文件你可以看看这个:
http://www.linuxsir.cn/forum.php?mod=viewthread&tid=59533
处理的配置文件形式是“key=value”,还不够完善,可以参考一下
 楼主| 发表于 2003-10-20 11:52:45 | 显示全部楼层
看到了斑竹的读配置文件的帖子了
谢谢先!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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