【问题标题】:In C, parsing a string of multiple whitespace separated integers在 C 中,解析由多个空格分隔的整数组成的字符串
【发布时间】:2015-03-29 18:50:03
【问题描述】:

我正在尝试使用 C 将包含多行空格分隔整数的文件解析为动态 int 数组的动态数组。每行将是数组数组中的一个数组。行数和每行中的元素是非常量的。

到目前为止,我所做的是使用 fgets 将每一行作为字符串抓取。

但是,我无法弄清楚如何解析一串由空格分隔的整数。

我认为我可以使用 sscanf(因为 fscanf 可用于解析由空格分隔的整数组成的整个文件)。但是,似乎 sscanf 具有不同的功能。 sscanf 只解析字符串中的第一个数字。我的猜测是,因为行是字符串不是流。

我一直在寻找一种从字符串中生成流的方法,但它看起来不像 C 中可用的那样(我无法使用非标准库)。

char* line;
char lineBuffer[BUFFER_SIZE];
FILE *filePtr;
int value;

...

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) {

    printf("%s\n", lineBuffer);

    while(sscanf(lineBuffer, "%d ", &value) > 0) {
        printf("%d\n", value);
    }
}

有什么东西可以用来解析字符串吗?如果没有,是否有替代整个系统的方法?我宁愿不使用正则表达式。

【问题讨论】:

  • 您可以使用例如strtok 在空格上拆分字符串,然后使用sscanfstrtol 获取值。
  • 相关 - stackoverflow.com/questions/10826953/…stackoverflow.com/questions/13503135/…。我投票结束重复,因为答案就是您问题的答案。
  • 另请注意"%d" 格式会跳过前导空格,因此无需在格式中手动执行。
  • 另一种可能是使用sscanf扫描字符串,然后将传递给sscanf的指针增加字符串的长度。
  • @JoachimPileborg 是的,我链接的问题就是这样做的。那里的答案也为他的问题提供了解决方案。

标签: c string file parsing io


【解决方案1】:

通过fgets() 阅读一行是很好的第一步。

2 种方法:strtol()(更好的错误处理)和sscanf()

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) {
  char *endptr;
  while (1) {  
    errno = 0;
    long num = strtol(line, &endptr, 10);
    if (line == endptr) break;  // no conversion
    if (errno) break;  // out of range or other error

    #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
    // long and int may have different ranges
    if (num < INT_MIN || num > INT_MAX) {
      errno = ERANGE; 
      break;  // out of range
    }
    #endif

    int value = (int) num;
    printf("%d\n", value);
    line = endptr;
  } 
  while (isspace((unsigned char) *endptr)) endptr++;
  if (*endptr != '\0') Handle_ExtraGarbageAtEndOfLine();
}

" sscanf 只解析字符串中的第一个数字。"并非如此。使用sscanf()"%n" 记录扫描停止的位置。

while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) {
  int n;
  while (1) {  
    n = 0;
    int value;
    if (sscanf(line, "%d %n", &value, &n) != 1) break;
    printf("%d\n", value);
    line += n;
  } 
  if (line[n] != '\0') Handle_ExtraGarbageAtEndOfLine();
}

【讨论】:

    【解决方案2】:

    只需在输入行上使用一个循环,利用 atol() 无论如何都会在下一个空格分隔符处停止。仅适用于正整数;)但它很快,您无需阅读大量的 strtok 和 sscanf 文档,并且在整数之间散布“噪音”的情况下它甚至是健壮的。
    要使其也适用于负整数,请将 isdigit() 替换为 !isspace() 即可。

    void bla()
    {
        const char * input = "    1           3           4       6     ";
        size_t i;
        size_t len = strlen(input);
        for (i = 0; i < len; ++i)
        {
            if (isdigit(input[i]))
            {
                printf("%d\n", atol(&input[i]));
                while (i < len && isdigit(input[i]))
                    ++i;
            }
    
        }
    }
    
    void bla1()
    { // positive and negative ints version
        const char * input = "    10           -3           42       6     ";
        size_t i;
        size_t len = strlen(input);
        for (i = 0; i < len; ++i)
        {
            if (!isspace(input[i]))
            {
                printf("%d\n", atol(&input[i]));
                while (i < len && !isspace(input[i]))
                    ++i;
            }
        }
        /* Output: 
            10
            -3
            42
            6
    
        */
    }
    

    您的问题的下一部分是(隐含地),如何处理动态数组以将解析的 int 值存储在其中。这里是基于上述代码的解决方案。对于输入,chunkSize 设置得太小,所以我可以测试 realloc 代码部分是否也有效。

    typedef struct DataRow_tag
    {
        int32_t *data;
        size_t length;
    } DataRow_t;
    
    // Returns a "bool" in C-style. Yes, there is stdbool.h in ansi c99 but it is disadviced.
    // (Platform dependent trouble in the context of C/C++ interaction, often across library/DLL boundaries.
    // Especially if you compile C with a C-compiler and the C++ code with C++ compiler. Which happens.
    // Every now and then, sizeof(c++ bool) != sizeof(C bool) and you waste a lot of time finding the problem.)
    // The caller takes ownership of the DataRow_t::data pointer and has to free() it when done using it.
    // 0: false -> fail
    // 1: true -> success!
    int 
    ReadRowWithUnknownNumberOfColumnsOfInt32
        ( const char * row      // Zero terminated string containing 1 row worth of data.
        , DataRow_t *result     // Pointer to the place the data will be stored at.
        )
    {
        int success = 0;
        size_t chunkSize = 10; // Set this value to something most likely large enough for your application.
    
        // This function is not cleaning up your garbage, dude ;) Gimme a clean result structure!
        assert(NULL != result && NULL == result->data);
        if (NULL != result && NULL == result->data)
        {
            result->length = 0;
            size_t rowLength = strlen(row);
            const char *pInput = row;
            const char *pEnd = &row[rowLength-1];
    
            result->data = (int32_t*)malloc(chunkSize * sizeof(int32_t));
            if (NULL != result->data )
            {
                for (; pInput < pEnd; ++pInput)
                {
                    assert(pInput <= pEnd);
                    assert(*pInput != 0);
                    if (!isspace(*pInput)) // ultra correct would be to cast to unsigned char first...says microsoft code analyzer in paranoia mode.
                    {
                        long lval = atol(pInput); // what is a long anyway? 4 bytes, 2 bytes, 8 bytes? We only hope it will fit into our int32_t...
                        // TODO: we could test here if lval value fits in an int32_t...platform dependent!
                        result->data[result->length++] = lval;
                        if (result->length == chunkSize)
                        { // our buffer was too small... we need a bigger one.
                            chunkSize = chunkSize + chunkSize; // doubling our buffer, hoping it will be enough, now.
                            int32_t * temp = (int32_t*)realloc(result->data, chunkSize * sizeof(int32_t));
                            if (NULL == temp)
                            { // realloc is a funny function from the dark ages of c. It returns NULL if out of memory.
                                // So we cannot simply use result->data pointer for realloc call as this might end up with a memory leak.
                                free(result->data);
                                result->length = 0;
                                break;
                            }
                            else
                            {
                                result->data = temp;
                            }
                        }
                        while (pInput < pEnd && !isspace(*pInput))
                            ++pInput;
                    }
                }
                if (pInput >= pEnd)
                    success = 1;
                else
                { // make sure we do not leave result in some funny state.
                    result->length = 0;
                    free(result->data); // free(NULL) legal. If memblock is NULL, the pointer is ignored and free immediately returns.
                    result->data = NULL;
                }
            }
        }
    
        return success;
    }
    void Bla2()
    {
        const char * input = "-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13";
        DataRow_t dataRow = { 0 };
        if (ReadRowWithUnknownNumberOfColumnsOfInt32(input, &dataRow))
        {
            for (size_t i = 0; i < dataRow.length; ++i)
            {
                printf("%d ", dataRow.data[i]);
            }
            printf("\n");
    
            free(dataRow.data);
            dataRow.data = NULL;
            dataRow.length = 0;
        }
    }
    

    【讨论】:

    • 周围有自动投票机器人吗?在我的浏览器甚至在点击“发布”按钮后加载之前得到 -1...
    • @chux 在这种情况下,您需要应用答案文本的最后一行;)
    • @chux 和顺便说一句...在这些情况下它确实进步了。只有 -123 作为 123 返回。while 在 if 中,因此它已经超过任何“-”或“+”。
    • 是的,如果你想要负整数,健壮性就会受到影响;) ``123`` 没问题 - 为什么会这样?
    【解决方案3】:

    你应该使用:

    lineBuffer = (char *)malloc(sizeof(BUFFER_SIZE + 1));
    

    比:

    char lineBuffer[BUFFER_SIZE];
    

    你的堆栈会感谢你的!

    【讨论】:

    • 不一定正确。例如,在 32 位 Linux 中,可以有 more space for the stack than there is for the heap. 在 64 位内存模型中,在可用堆或堆栈地址空间用完之前,您几乎肯定会用完实际内存。没关系堆性能受到影响。
    • 你完全正确@AndrewHenle,感谢通知
    【解决方案4】:

    使用strtok() 函数和" "(space) 作为分隔符,并将其放在循环中,当strtok() 返回NULL 时终止以获取每个令牌,然后打印每个令牌中的每个数字:

    while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) {
    
        printf("%s\n", lineBuffer);
    
        char *token=strtok(line," ");
    
        while(token!=NULL)
        {
            if(sscanf(token, "%d", &value) > 0)
                 printf("%d\n", value);
             token=strtok(NULL," ");
        }
    }
    

    【讨论】:

      【解决方案5】:

      使用strtol(),如果有的话,它给出一个指向匹配结束的指针,以及一个存储当前位置的char指针:

          while((line = fgets(lineBuffer, BUFFER_SIZE, filePtr)) != NULL) {
      
          printf("%s\n", lineBuffer);
          char* p = lineBuffer;
          while(p < lineBuffer+BUFFER_SIZE ) {
              char* end;
              long int value = strtol( p , &end , 10 );
              if( value == 0L && end == p )  //docs also suggest checking errno value
                  break;
      
              printf("%ld\n", value);
              p = end ;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-20
        • 1970-01-01
        • 1970-01-01
        • 2021-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多