【问题标题】:Split string on any number of white spaces in C在 C 中的任意数量的空格上拆分字符串
【发布时间】:2018-05-12 19:54:07
【问题描述】:

我有一个文件,其中每个用户名和密码由不同数量的空格分隔。

bob    passowrd1
saly password2
sam      password2

void parse()
{
    FILE*open;
    open = fopen("file.txt");
    char line[101];
    char*name;
    char*password;

    while(fgets(100,line,open)!=NULL)
    {
       name = strtok(line,"*\\s");
       password = strtok(NULL,"*\\s");
       printf("username : %s",name);
       printf("password : %s",password); 
    }
}

我正在尝试使用 strtok 拆分字符串,但它不接受正则表达式作为分隔符。我能想到的唯一另一种方法是通过对字符串进行循环并在空格之后创建2个新的单独字符串来强制执行。有什么建议吗?

【问题讨论】:

  • 我非常担心您为什么要存储明文密码
  • @tadman 这只是一个介绍类,我知道以明文形式存储密码是不安全的
  • scanf 函数族忽略某些格式的所有前导空格,例如%sif(fscanf(myfile, "%s%s", name, password) != 2) { /* bad input */ }
  • 我的第一条评论更好的解决方案是使用fgets 读取文件的每一行,然后应用sscanf。这使得转储无效条目变得容易。这里有很多 SO 问题和示例代码供您查找。您还没有发布任何尝试,所以问题不在主题范围内。
  • “我正在尝试使用 strtok 拆分字符串,但它不接受正则表达式作为分隔符。” --> 发布该代码。 VTC 没有提供minimal reproducible example

标签: c file parsing


【解决方案1】:

您(以及大多数受访者)对此想得太多。 strtok() 对一个或多个定界符进行定界,所以

name = strtok(line," ");
password = strtok(NULL," ");

会做你想做的事。

【讨论】:

  • 使用fgets() 填充line[],此快速答案将在password 中包含'\n',因为仅在" " 上进行标记不足以满足OP 的“创建2 个新的在 white-spaces" 之后分隔字符串
【解决方案2】:

如果可用,您可以使用strsep(字符串分隔)。 strsep 采用一组分隔符,并将逐步通过一个字符串将其分隔为字段。它优于 strtok,因为它不保留隐藏的全局状态。

void split_fields_strsep( char *string ) {
    char *field;
    const char *delimiters = " \t\n";

    while( (field = strsep(&string, delimiters)) != NULL ) {
        // Multiple spaces will show up as multiple empty fields.
        // Skip them.
        if( *field == '\0' ) {
            continue;
        }

        printf("field: '%s'\n", field);
    }
}

请注意,每个field 都是指向原始string 的指针。 strsep 将通过在每个字段的末尾放置空字节来将字符串拆分为字段。如果stringfoo bar baz \0,您将以foo\0bar\0 baz\0 \0 结尾。因此,如果您想保留该字符串,请务必strdup


如果strsep 不可用,标准strtok 将起作用。它的工作方式类似于strsep,并会通过添加空字节来更改原始字符串。

void split_fields_strtok( char *string ) {
    const char *delimiters = " \t\n";

    for(
        char *field = strtok(string, delimiters);
        field != NULL;
        field = strtok(NULL, delimiters)
    ) {
        printf("field: '%s'\n", field);
    }
}

【讨论】:

  • strsep 正是 OP 想要的:它将每个单独的分隔符视为单独的分隔符。 (“strsep 和 strtok_r 之间的一个区别是,如果输入字符串连续包含多个来自分隔符的字节,则 strsep 为来自分隔符的每一对字节返回一个空字符串。”引用来自 glibc 的信息文件。)。 strtok 做了正确的事,但它的界面很糟糕。如果可能,请使用strtok_r
  • @rici 我在代码中解释了strsep 的行为,还有注释和所有内容。我同意strtok,但我想提供一个标准选项,因为许多课程都坚持 C90。 strtok_r 至少是 POSIX 标准,所以就是这样,但我发现 strsep 在拉开您经常希望保留空字段的分隔字段时更易于使用且更灵活,尽管在这种特殊情况下不是。
  • 在非标准扩展和 Posix 标准函数之间,我个人会选择后者,特别是如果它做正确的事情。当然,您可能有一个实现附录 K strtok_s 接口的 libc。 strsep 确实很常见,但如今strtok_r 也是如此。并为没有阅读您的代码而道歉。
  • @rici 你应该见过what I'd written before。 ;)
【解决方案3】:

在 C 中任意数量的空格上分割字符串

"*\\s" 表示对如何编码令牌字符有误解。 "*\\s" 查找 3 个字符 *\s 作为标记,当与 strtok() 一起使用时,这些字符都不表示空格。


使用显式列表解析输入行中的空白。请务必考虑输入行的尾随'\n' 或其他空白

C 中的

空格 包含许多字符:

标准的空白字符如下:空格 (' ')、换页 ('\f')、换行 ('\n')、回车 ('\r')、水平制表符 (@987654332 @) 和垂直制表符 ('\v')。
C11dr §7.4.1.10 2

// while(fgets(100,line,open)!=NULL)
while(fgets(line, sizeof line, open)) {  // Destination array is first argument

  const char *standard_white_space = " \f\n\r\t\v";
  name = strtok(line, standard_white_space);
  password = strtok(NULL, standard_white_space);
  printf("username : <%s>\n",name);
  printf("password : <%s>\n",password); 

}

或者,在sscanf() 中使用isspace()" "


提示:为确保代码在username, password 中不包含空格,请使用&lt;&gt; 等标记字符打印字符串,以便更轻松地检测编码错误。

  char line[] = "bob    passowrd1\n";
  char*name = strtok(line, " ");
  char* password = strtok(NULL, " ");
  printf("username : <%s>\n", name);
  printf("password : <%s>\n", password);

这个输出

username : <bob>
password : <passowrd1    <-- Oops
>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多