【问题标题】:using getline and strtok together in a loop在循环中一起使用 getline 和 strtok
【发布时间】:2012-04-11 19:38:19
【问题描述】:

我是 C 的新手,并尝试实现 whoami,作为我自己的练习。我有以下代码:

#define _POSIX_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h> // strtok

int str_to_int(const char *str)
{
    int acc = 0;
    int i;
    for (i = 0; str[i] != '\0'; ++i) {
        acc = (10 * acc) + (str[i] - 48); // 48 -> 0 in ascii
    }
    return acc;
}

int main()
{
    FILE *passwd;
    char *line = NULL;
    size_t line_size;

    passwd = fopen("/etc/passwd","r");

    uid_t uid = getuid();

    while (getline(&line, &line_size,passwd) != -1) {
        char *name = strtok(line,":");
        strtok(line,":"); // passwd
        char *user_id = strtok(line,":");
        if (str_to_int(user_id) == uid) {
            printf("%s\n",name);
            break;
        }
    }

    fclose(passwd);
    return 0;
}

我是否需要在 while 循环内保存行指针。因为我认为 strtok 以某种方式对其进行了修改,但我不确定是否需要复制该行或该行的起始地址,然后再将其与 strtok 一起使用。

【问题讨论】:

  • 您可以使用atoistrtol 将字符串转换为整数,而不是您的自定义函数。

标签: c pointers getline strtok


【解决方案1】:

strtok 是一个可怕的功能。我不知道您阅读了哪些文档(如果有的话?),但它既修改了传递的缓冲区,又保留了指向缓冲区的内部指针;您应该只在 first 在给定行上使用它时传递缓冲区,然后传递 NULL 以便它知道从中断的地方开始而不是从头开始(这赢了'实际上并不能很好地工作,因为它踩到了缓冲区......)。

最好,找一些其他的方式来解析并远离strtok

【讨论】:

  • 我听过很多人这么说,但我不明白为什么这让它变得“可怕”,正如你所说的那样。在 C 中解析文本既非常方便又干净。如果您不希望它修改原始字符串,只需复制该字符串并将其传递给 strtok。此外,strtok_r 实际上是线程安全的,因此非常适合高性能代码。
【解决方案2】:

使用strtok_r 可能更安全。在多线程情况下更安全。这可能不适用于这种情况,但有时最好假设您编写的任何 sn-p 都可能最终出现在多线程应用程序中。以下是修改为使用strtok_r 的OP 代码。

  char *pos;
  char *name = strtok_r(line,":",&pos);
  strtok_r(NULL,":",&pos); // passwd
  char *user_id = strtok_r(NULL,":",&pos);

而且,是的,strtok(和strtok_r)确实修改了给定的输入缓冲区(第一个参数)。但如果使用得当,它是安全的。由于strtok 返回一个指向给定字符串内的缓冲区的指针,因此您需要注意如何使用它。在您的情况下,当它跳出循环时,nameuser_id 将指向 line 缓冲区内的一个值。

您也许应该阅读getline 的手册页。您使用它的方式,它返回一个分配的缓冲区,您的应用程序负责释放该缓冲区。这可能是您的目标,但我提到它是因为我在发布的代码中没有看到free 调用它。

【讨论】:

    【解决方案3】:

    我完全同意 geekosaur(和 Mark)的观点。套用他的评论,你可以修改上面的代码如下:

    while (getline(&line, &line_size, passwd) != -1) {
        char *name = strtok(line,":");
        strtok(NULL,":"); // passwd
        char *user_id = strtok(NULL,":");
        if (str_to_int(user_id) == uid) {
            printf("%s\n",name);
            break;
        }
    }
    

    您应该为除第一个以外的 strtok 调用传递 NULL。

    【讨论】:

      猜你喜欢
      • 2013-06-25
      • 2011-01-03
      • 1970-01-01
      • 1970-01-01
      • 2019-08-09
      • 1970-01-01
      • 2019-07-27
      • 2010-12-03
      相关资源
      最近更新 更多