【问题标题】:How to safety parse tab-delimited string ?如何安全解析制表符分隔的字符串?
【发布时间】:2010-10-01 13:35:17
【问题描述】:

如何安全解析制表符分隔符字符串?例如: 测试\tbla-bla-bla\t2332 ?

【问题讨论】:

  • 这个问题有点笼统......你能再解释一下吗?顺便说一句,我想你的意思是“安全”......
  • 有什么问题?您扫描字符串并对 TAB 做出反应。

标签: c parsing csv


【解决方案1】:

strtok() 是用于解析带有任意分隔符的字符串的标准函数。但是,它不是线程安全的。您选择的 C 库可能具有线程安全的变体。

另一种符合标准的方式(只是写了这个,它未经测试):

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

int main()
{
    char string[] = "foo\tbar\tbaz";
    char * start = string;
    char * end;
    while ( ( end = strchr( start, '\t' ) ) != NULL )
    {
        // %s prints a number of characters, * takes number from stack
        // (your token is not zero-terminated!)
        printf( "%.*s\n", end - start, start );
        start = end + 1;
    }
    // start points to last token, zero-terminated
    printf( "%s", start );
    return 0;
}

【讨论】:

    【解决方案2】:

    使用 strtok_r 代替 strtok(如果可用)。它具有类似的用法,除了它是可重入的,并且它 像 strtok 那样修改字符串。 [编辑:实际上,我说错了。正如 Christoph 指出的那样, strtok_r 确实将分隔符替换为 '\0'。因此,如果要保留原始字符串,则应该对字符串的副本进行操作。但最好使用 strtok,因为它是可重入且线程安全的]

    strtok 将修改您的原始字符串。它将分隔符替换为“\0”。如果您的字符串恰好是一个常量,存储在只读内存中(某些编译器会这样做),您实际上可能会遇到访问冲突。

    【讨论】:

    • afaik strtok_r()strtok() 一样工作 - 即它将修改字符串,用零替换分隔符!函数之间的区别在于strtok_r() 不使用内部static 变量,而是使用用户提供的变量来存储其状态
    • 你是对的!我错过了。因此,您需要对字符串的副本进行操作。但是 strtok_r 仍然是可取的,因为它是可重入的。
    【解决方案3】:

    使用来自string.hstrtok()

    #include <stdio.h>
    #include <string.h>
    
    int main ()
    {
        char str[] = "test\tbla-bla-bla\t2332";
        char * pch;
        pch = strtok (str," \t");
        while (pch != NULL)
        {
            printf ("%s\n",pch);
            pch = strtok (NULL, " \t");
        }
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      您可以使用任何正则表达式库甚至 GLib GScanner,有关详细信息,请参阅 herehere

      【讨论】:

        【解决方案5】:

        又一个版本;这个把逻辑分离成一个新的函数

        #include <stdio.h>
        
        static _Bool next_token(const char **start, const char **end)
        {
            if(!*end) *end = *start;    // first call
            else if(!**end)             // check for terminating zero
                return 0;
            else *start = ++*end;       // skip tab
        
            // advance to terminating zero or next tab
            while(**end && **end != '\t')
                ++*end;
        
            return 1;
        }
        
        int main(void)
        {
            const char *string = "foo\tbar\tbaz";
        
            const char *start = string;
            const char *end = NULL; // NULL value indicates first call
        
            while(next_token(&start, &end))
            {
                // print substring [start,end[
                printf("%.*s\n", end - start, start);
            }
        
            return 0;
        }
        

        【讨论】:

          【解决方案6】:

          如果您需要一种二进制安全的方式来标记给定的字符串:

          #include <string.h>
          #include <stdio.h>
          
          void tokenize(const char *str, const char delim, const size_t size)
          {
                  const char *start = str, *next;
                  const char *end = str + size;
          
                  while (start < end) {
                          if ((next = memchr(start, delim, end - start)) == NULL) {
                                  next = end;
                          }
          
                          printf("%.*s\n", next - start, start);
                          start = next + 1;
                  }
          }
          
          int main(void)
          {
                  char str[] = "test\tbla-bla-bla\t2332";
                  int len = strlen(str);
          
                  tokenize(str, '\t', len);
          
                  return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-06-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多