【问题标题】:Split string using more than one char as delimeter使用多个字符作为分隔符拆分字符串
【发布时间】:2016-08-11 14:26:08
【问题描述】:

假设我有一个字符串"file1.h: file2.c,file3.cpp",我想将其拆分为"file1.h""file2.c,file3.cpp"——即使用:: 和空格)作为分隔符。我该怎么做?

我在没有帮助的情况下尝试了这段代码:

int main(int argc, char *argv[]) {
   char str[] = "file1.h: file2.c,file3.cpp";
   char name[100];
   char depends[100];
   sscanf(str, "%s: %s", name, depends);
   printf("Name: %s\n", name);
   printf("Deps: %s\n", depends);
}

我得到的输出是:

名称:file1.h:

部门:

【问题讨论】:

  • 您可能想检查sscanf() 的返回值,并考虑"%s" 是否真的':' 不匹配,正如您所假设的那样。
  • : and backspace.. 你确定吗?
  • 如果是:和` `(空格),你为什么不试试strtok()呢?它一次接受更多的分隔符,FWIW。
  • 我也试过 strtok,但我在第二个字符串的开头有一个空格(取决于)
  • sscanf(str, "%99[^:]: %99[^\n]", name, depends);

标签: c string split


【解决方案1】:

您似乎需要的是strtok()。在man page 中了解它。来自C11 的相关引用,第 7.24.5.8 章

strtok 函数的一系列调用将s1 指向的字符串分解为一个 标记序列,每个标记都由指向的字符串中的一个字符分隔 s2。 [...]

在您的情况下,您可以使用分隔符

  char * delim = ": "; //combination of : and a space

去完成工作。

需要补充的,

  • strtok() 的输入需要可修改(在您的情况下)
  • 它实际上会破坏输入给它的输入,如果您以后需要实际的,请保留一份副本。

【讨论】:

  • 注意strtok()有很多问题。我推荐strpbrk()。它不是可重入的,不是线程安全的,它的设计方式令人困惑,在第一次之后传递NULL 等等。它修改了输入字符串。糟透了。
【解决方案2】:

这是另一种方法,它使用strchr(),但这假设输入字符串始终具有格式

name: item1,item2,item3,...,itemN

这是程序

#include <string.h>
#include <stdio.h>

int
main(void)
{
    const char *const string = "file1.h: file2.c,file3.cpp ";

    const char *head;
    const char *tail;
    const char *next;

    // This basically makes a pointer to the `:'
    head = string;  
    // If there is no `:' this string does not follow
    // the assumption that the format is
    //
    //    name: item1,item2,item3,...,itemN
    //    
    if ((tail = strchr(head, ':')) == NULL)
        return -1;
    // Save a pointer to the next character after the `:'
    next = tail + 1;
    // Strip leading spaces
    while (isspace((unsigned char) *head) != 0)
        ++head;
    // Strip trailing spaces
    while (isspace((unsigned char) *(tail - 1)) != 0)
        --tail;     

    fputc('*', stdout);
    // Simply print the characters between `head' and `tail'
    // you could as well copy them, or whatever
    fwrite(head, 1, tail - head, stdout);
    fputc('*', stdout);
    fputc('\n', stdout);

    head = next;
    while (head != NULL) {      
        tail = strchr(head, ',');
        if (tail == NULL) {
            // This means there are no more `,'
            // so we now try to point to the end
            // of the string
            tail = strchr(head, '\0');
        }
        // This is basically the same algorithm
        // just with a different delimiter which
        // will presumably be the same from
        // here
        next = tail + 1;
        // Strip leading spaces
        while (isspace((unsigned char) *head) != 0)
            ++head;
        // Strip trailing spaces
        while (isspace((unsigned char) *(tail - 1)) != 0)
            --tail;     
        // Here is where you can extract the string
        // I print it surrounded by `*' to show that
        // it's stripping white spaces
        fputc('*', stdout);
        fwrite(head, 1, tail - head, stdout);
        fputc('*', stdout);
        fputc('\n', stdout);
        // Try to point to the next one
        // or make head `NULL' if this is
        // the end of the string
        //
        // Note that the original `tail' pointer
        // that was pointing to the next `,' or
        // the end of the string, has changed but
        // we have saved it's original value
        // plus one, we now inspect what was
        // there
        if (*(next - 1) == '\0') {
            head = NULL;
        } else {
            head = next;
        }
    }
    fputc('\n', stderr);
    return 0;
}

为了引导读者,注释过多。

【讨论】:

    【解决方案3】:

    正如 Sourav 所说,您确实需要使用 strtok 来标记字符串。但这并不能解释为什么您现有的代码不起作用。

    答案在于sscanf 的规范以及它如何处理格式字符串中的'%s'

    来自man 页面:

    s 匹配一系列非空白字符;

    因此,格式字符串中是否存在冒号空格与计算第一个 '%s' 基本无关。当sscanf 看到第一个%s 时,它只会使用输入字符串,直到遇到空白字符,从而为您提供name"file1.h:" 的值(注意包含冒号)。

    接下来它会尝试处理格式字符串中的冒号空格序列。

    再次,来自man 页面

    格式字符串由一系列指令组成,这些指令描述了如何处理输入字符序列。

    冒号空格序列不匹配任何已知指令(即“%”后跟一些东西),因此您会遇到匹配失败。

    相反,如果您的格式字符串只是 "%s%s",那么 sscanf 将几乎完全符合您的要求。

    int main(int argc, char *argv[]) {
       char str[] = "file1.h: file2.c,file3.cpp";
       char name[100];
       char depends[100];
       sscanf(str, "%s%s", name, depends);
       printf("str: '%s'\n", str);
       printf("Name: %s\n", name);
       printf("Deps: %s\n", depends);
       return 0;
    }
    

    给出这个输出:

    str: 'file1.h: file2.c,file3.cpp'
    Name: file1.h:
    Deps: file2.c,file3.cpp
    

    此时,您可以简单地检查 sscanf 的返回值是否为 2(即它找到了两个值),并且 name 的最后一个字符是冒号。然后截断name,你就有答案了。

    当然,按照这种逻辑,您将无法使用sscanf 将您的depends 变量解析为多个字符串...这就是为什么其他人建议使用strtokstrpbrk等等,因为您正在解析和标记您的输入。

    【讨论】:

      【解决方案4】:

      好吧,我来晚了。我对 C 中的内置函数知之甚少。所以我开始为您编写一个解决方案。我认为你现在不需要这个。但是,无论如何here it is 并根据您的需要进行修改。如果您发现任何错误,请随时告知。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-26
        • 2018-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-04
        • 1970-01-01
        相关资源
        最近更新 更多