【问题标题】:Progress string parsing in CC中的进度字符串解析
【发布时间】:2010-09-16 16:04:13
【问题描述】:

我有以下字符串:

"..1....10..20....30...40....50...80..." 

我需要将其中的所有数字提取到数组中。

在 C 中最好的方法是什么?

【问题讨论】:

    标签: c string


    【解决方案1】:

    也许最简单的方法是使用strtok() 函数(或strtok_r(),如果需要考虑可重入性):

    char str[] = "..1...10...20";
    char *p = strtok(str, ".");
    while (p != NULL) {
        printf("%d\n", atoi(p));
        p = strtok(NULL, ".");
    }
    

    一旦你得到了调用atoi()的结果,将这些整数保存到一个数组中应该是一件简单的事情。

    【讨论】:

      【解决方案2】:

      您可以使用带有抑制赋值 (%*[.]) 的 sscanf 代码来跳过点(或您想要的任何其他字符),并使用扫描的字符计数代码 %n 来推进字符串指针。

      const char *s = "..1....10..20....30...40....50...80...";
      int num, nc;
      
      while (sscanf(s, "%*[.]%d%n", &num, &nc) == 1) {
          printf("%d\n", num);
          s += nc;
      }
      

      【讨论】:

      • 为了解决 Robert Gamble 的抱怨,即它不能处理没有前导点的字符串,您可以选择这样写:while (sscanf(s, "%d%n", &num, &nc) == 1 || sscanf(s, "%*[.]%d%n", &num, &nc) == 1) /* 短路评估很棒 */
      【解决方案3】:

      我更喜欢在 for 循环中使用 strtok。让它感觉更自然,虽然语法看起来有点奇怪。

      char str[] = "..1....10..20....30...40....50...80..."
      for ( char* p = strtok( strtok, "." ); p != NULL; p = strtok( NULL, "." ) )
      {
          printf( "%d\n", atoi( p ) );
      }
      

      【讨论】:

      • 确实,它看起来很奇怪。我本来期望像 for (p=strtok(str,","); p=strtok(NULL,"."); ) 的东西,第一行后的分号和 p 的声明。
      • 是的 - 代码是在我脑海中写下的。已更正。
      • 我认为在 for 循环的测试位置进行初始化是行不通的。这将在每次循环开始时执行。我不认为那是你想要的。 mweerden 的版本更好,但我在测试位置使用了显式测试,并将下一个令牌提取移动到末尾。
      • 澄清一下:(p=strtok(str,"."); p != NULL; p=strtok(NULL,"."))
      • 就像我说的那样,它是从我的头顶上写下来的——别再记下来了!
      【解决方案4】:

      这是正确的方法,它比最简单的方法长一点,但如果读取的值超出范围,它不会出现未定义的行为,如果第一个字符不是点,则可以正常工作,等等。您没有指定数字是否可以是负数,所以我使用了有符号类型,但只允许正值,您可以通过允许内部 while 循环顶部的负号轻松更改此设置。此版本允许任何非数字字符分隔整数,如果您只想允许点,您可以修改内部循环以仅跳过点,然后检查数字。

      #include <stdio.h>
      #include <stdlib.h>
      #include <ctype.h>
      #include <errno.h>
      
      #define ARRAY_SIZE 10
      
      size_t store_numbers (const char *s, long *array, size_t elems)
      {
        /* Scan string s, returning the number of integers found, delimited by
         * non-digit characters.  If array is not null, store the first elems
         * numbers into the provided array */
      
        long value;
        char *endptr;
        size_t index = 0;
      
        while (*s)
        {
          /* Skip any non-digits, add '-' to support negative numbers */
          while (!isdigit(*s) && *s != '\0')
            s++;
      
          /* Try to read a number with strtol, set errno to 0 first as
           * we need it to detect a range error. */
          errno = 0;
          value = strtol(s, &endptr, 10);
      
          if (s == endptr) break; /* Conversion failed, end of input */
          if (errno != 0) { /* Error handling for out of range values here */ }
      
          /* Store value if array is not null and index is within array bounds */
          if (array && index < elems) array[index] = value;
          index++;
      
          /* Update s to point to the first character not processed by strtol */
          s = endptr;
        }
      
        /* Return the number of numbers found which may be more than were stored */
        return index;
      }
      
      void print_numbers (const long *a, size_t elems)
      {
        size_t idx;
        for (idx = 0; idx < elems; idx++) printf("%ld\n", a[idx]);
        return;
      }
      
      int main (void)
      {
        size_t found, stored;
        long numbers[ARRAY_SIZE];
        found = store_numbers("..1....10..20....30...40....50...80...", numbers, ARRAY_SIZE);
      
        if (found > ARRAY_SIZE)
          stored = ARRAY_SIZE;
        else
          stored = found;
      
        printf("Found %zu numbers, stored %zu numbers:\n", found, stored);
        print_numbers(numbers, stored);
      
        return 0;
      }
      

      【讨论】:

      • 首先,如果没有前导点,简单的答案不会中断,并且根据定义,因为这是解析进度指示器,所以输入在数字范围方面有很好的界限。使用 strtol() 或 strtoul() 可能是对 atoi() 的改进,但您的解决方案过于复杂。
      • strtok 版本不适用于字符串文字,不会将数字存储在数组中,并使用有问题的 atoi。 sscanf 解决方案在没有前导点的情况下不起作用,并且存在与 atoi 类似的问题。我的解决方案更长,但它也是一个带有错误检查和 cmets 的完整程序。
      猜你喜欢
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 2015-03-23
      • 2019-08-03
      • 1970-01-01
      • 2010-10-11
      • 1970-01-01
      相关资源
      最近更新 更多