【问题标题】:Parsing string to get comma-separated integer character pairs解析字符串以获取逗号分隔的整数字符对
【发布时间】:2016-11-13 06:49:12
【问题描述】:

我正在处理一个项目,在该项目中,我得到了一个以这种格式的标题开头的文件:a1,b3,t11, 2,,5,\3,*4,344,00,。它始终是一个由单个 ASCII 字符组成的序列,后跟一个用逗号分隔的整数,该序列始终以 00, 结尾。

基本上我要做的就是完成这个并将每个字符/整数对放入我拥有的数据类型中,该数据类型将这两个都作为参数并制作它们的向量。例如,我上面给出的标题是一个以('a',1), ('b',3),('t',11),(',',5)(' ',2),('\',3),('*',4),('3',44) 为元素的向量。

我只是在解析它时遇到了麻烦。到目前为止,我已经:

  • 从我的文本文件中提取标题,从第一个字符直到标题结束的“,00”之前。我可以获取字符串格式的标题字符串或字符向量(以更容易解析的为准)
  • 尝试使用 sscanf 解析下一个字符和下一个 int,然后将它们添加到我的向量中,然后使用子字符串删除我已经分析过的字符串部分(这很混乱,没有得到正确的结果)
  • 尝试将字符串作为向量遍历并检查每个元素以查看它是整数、字符还是逗号并采取相应措施,但这不适用于多位整数或字符本身是整数

    我知道我可以很容易地根据逗号拆分我的字符串,但我不确定如何做到这一点,并且仍然从字符中拆分整数,同时保留两者并考虑我需要视为字符的整数。

    任何建议或有用的标准库或字符串函数将不胜感激。

  • 【问题讨论】:

    • 你已经用 [c] 和 [c++] 标记了这个。你能澄清一下为什么吗? (请注意,最好的 C 实现不一定是最好的 C++ 实现。)
    • 欢迎来到 Stack Overflow。请尽快阅读About 和How to Ask 页面。到目前为止,您真的应该向我们展示您的最大努力——这也有助于我们校准响应。您还应该阅读有关如何创建 MCVE (minimal reproducible example) 的信息。在这个阶段,您可以简单地拥有一个给定字符数组的函数,以及一个调用该函数并给它一个字符数组的main()。如果您的函数可能会修改字符串,请不要给它一个字符串文字来使用。 34400 序列似乎与您的描述不符;如果是344,00,它会的。你能澄清一下吗? ,5 这对很有趣。
    • 请,发帖前请先搜索。已经有很多类似的问题,可以通过搜索找到“stackoverflow c++ read file csv”或“stackoverflow c++ read file struct”
    • 你能有一个以a100,结尾的字符串吗? a12300,呢?
    • @ruakh 我只是认为这两种实现都适合我。

    标签: c++ c string data-structures split


    【解决方案1】:

    其中一种可能性是将数据存储在一个结构中。这使用了一个结构数组,但可以根据需要使用malloc 和realloc 分配结构。
    解析字符串可以使用指针和strtol 来完成,它将解析整数并给出指向整数后面字符的指针。该指针可以在下一次迭代中使用以获取 ASCII 字符和整数。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define SIZE 100
    
    struct pair {
        char ascii;
        int  integer;
    };
    
    int main( void) {
        char input[] = "a1,b3,!0,t11, 2,,5,\\3,*4,34400,";
        char *pt = input;//start with pt pointing to first character of input
        char *end = input;
        int each = 0;
        int loop = 0;
        int length = 0;
        struct pair pairs[SIZE] = { { '\0', 0}};
    
        //assuming input will always end in 00, ( or ,00,)
        //remove those three ( or 4 ??) characters
        length = strlen ( input);
        if ( length > 3) {
            input[length - 3] = '\0';
        }
        for ( each = 0; each < SIZE; each++) {
            //get the ASCII character and advance one character
            pairs[each].ascii = *pt;
            pt++;
            //get the integer
            pairs[each].integer = strtol ( pt, &end, 10);
            //end==pt indicates the expected integer is missing
            if ( end == pt) {
                printf ( "expected an integer\n");
                break;
            }
            //at the end of the string?
            if ( *end == '\0') {
                //if there are elements remaining, add one to each as one more was used
                if ( each < SIZE - 1) {
                    each++;
                }
                break;
            }
            //the character following the integer should be a comma
            if ( *end != ',') {
                //if there are elements remaining, add one to each as one more was used
                if ( each < SIZE - 1) {
                    each++;
                }
                printf ( "format problem\n");
                break;
            }
            //for the next iteration, advance pt by one character past end
            pt = end + 1;
        }
        //loop through and print the used structures
        for ( loop = 0; loop < each; loop++) {
            printf ( "ascii[%d] = %c   ", loop, pairs[loop].ascii);
            printf ( "integer[%d] = %d\n", loop, pairs[loop].integer);
        }
    
        return 0;
    }
    

    另一种选择是使用动态分配。
    这也使用 sscanf 来解析输入。 %n 将捕获扫描处理的字符数。然后可以使用offset 和add 变量来遍历输入。最后一次扫描只会捕获 ascii 字符和整数,sscanf 的返回值为 2。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    struct pair {
        char ascii;
        int  integer;
    };
    
    int main( void) {
        char input[] = "a1,b3,!0,t11, 2,,5,\\3,*4,34400,";
        char comma = '\0';
        char ascii = '\0';
        int integer = 0;
        int result = 0;
        int loop = 0;
        int length = 0;
        int used = 0;
        int add = 0;
        int offset = 0;
        struct pair *pairs = NULL;//so realloc will work on first call
        struct pair *temp = NULL;
    
        //assuming input will always end in 00, ( or ,00,)
        //remove those three ( or 4 ??) characters
        length = strlen ( input);
        if ( length > 3) {
            input[length - 3] = '\0';
        }
        while ( ( result = sscanf ( &input[offset], "%c%d%c%n"
        , &ascii, &integer, &comma, &add)) >= 2) {//the last scan will only get two items
            if ( ( temp = realloc ( pairs, ( used + 1) * sizeof ( *pairs))) == NULL) {
                fprintf ( stderr, "problem allocating\n");
                break;
            }
            pairs = temp;
    
            pairs[used].ascii = ascii;
            pairs[used].integer = integer;
            //one more element was used
            used++;
            //the character following the integer should be a comma
            if ( result == 3 && comma != ',') {
                printf ( "format problem\n");
                break;
            }
            //for the next iteration, add to offset
            offset += add;
        }
        for ( loop = 0; loop < used; loop++) {
            printf ( "ascii[%d] = %c   ", loop, pairs[loop].ascii);
            printf ( "value[%d] = %d\n", loop, pairs[loop].integer);
        }
    
        free ( pairs);
    
        return 0;
    }
    

    【讨论】:

    • 谢谢!这两种方法看起来都不错,但我最终找到了一个使用嵌套 while 循环的简单解决方案。我保存了字符,然后基本上迭代并求和整数(每次乘以 10 以将每个数字放在正确的位置),然后加一个以跳过逗号并重复。
    【解决方案2】:

    既然您已经发现可以忽略最后 3 个字符,使用 sscanf 就足够了。 您可以使用 sscanf 读取一个字符(或 getch 函数),使用 sscanf 读取一个整数,最后甚至忽略一个字符。

    如果您在理解如何操作时遇到问题,请发表评论。

    【讨论】:

    • 耐心点!只要您不添加不是答案的内容,就好像它们是答案一样,您很快就会获得创建 cmets 的权限。这是一个主要评论——它不是一个有用的答案。
    • 感谢您的评论。但是, scanf 功能看起来像是一个合格的答案,不是吗?在 OP 澄清他的意图、努力和主要问题之前,也许我不应该急于解决这个问题。再次感谢。
    • sscanf()(带有一个或两个 s)很有可能是解决方案的一部分,但细节会很复杂(请注意,一个字段是 ,5)并且一般参考最难使用、最难使用的函数族之一确实没有多大帮助。是的,在我看来,你应该等待澄清。您很快就可以自己申请了。
    • 我考虑过并尝试过使用 sscan,但数字也可以算作 ASCII 字符,因此仅执行 sscanf 来获取 %c 然后 %d 似乎效果不佳。
    猜你喜欢
    • 2015-05-24
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 2019-12-04
    • 2021-11-01
    相关资源
    最近更新 更多