【问题标题】:Parsing and dynamically allocating substrings with inconsistant sizes using sscanf使用 sscanf 解析和动态分配大小不一致的子字符串
【发布时间】:2021-10-13 14:49:57
【问题描述】:

我实现了一个 read_line 函数:

#include<stdlib.h>
#include<stdio.h>
#include<stdbool.h>


char* read_line(){
    const char UNIX_LINEBREAK = '\n';
    const char WINDOWS_LINEBREAK = '\r';
    const char C_STRING_TERMINATOR = '\0';
    
    char extra_linebreak;
    char current_letter;

    char* line = NULL;
    int position = 0;
    bool reading_line = true;
    

    while(reading_line){
        scanf("%c", &current_letter);
        if(current_letter == UNIX_LINEBREAK || current_letter == EOF){
            reading_line = false;
        }
        else if(current_letter == WINDOWS_LINEBREAK) {
            reading_line = false;
            extra_linebreak = (char)getchar();
        }
        else {
            line = (char*) realloc(line, sizeof(char) * (position +  1));
            line[position] = current_letter;
            position ++;
        }
    }

    line = (char*) realloc(line, sizeof(char) * (position +  1));
    line[position] = C_STRING_TERMINATOR;

    return line; 
}

我用它来读取格式的字符串:

operation number number

例如:

sum 13 13

但是,我正在使用可能(并且将会)溢出最大 int 大小的数字来执行操作。例如:

sum 23879238932898239832983298329839229383928329 239823983298392893289238932883290312803291832109230189

这迫使我以字符串格式读取它们,解析它们并最终通过链表使用它们(可能有更好的方法,但这还不是重点)。到目前为止,我正在尝试使用辅助缓冲区(operationfirst_number_buffersecond_number_buffer)和sscanfread_line 读取的行拆分为三个子字符串。

#include <stdio.h>
#include <readline.h>
#include <stdio.h>
#include <readline.h>

int main (){
    char* line = read_line();
    char operation[4];
    char* first_number_buffer;
    char* second_number_buffer;

    sscanf(line, "%s %s %s", operation, first_number_buffer, second_number_buffer);

    printf("%s\n%s\n%s\n", line,first_number_buffer,second_number_buffer);

}

上面的代码不能很好地工作,因为我还没有真正分配first_number_buffersecond_number_buffer。我想知道在这种情况下是否有使用 sscanf 的有效方法。我没能在 google 中找到好的结果,因为 scanf 与 sscanf 结果重叠。

问题似乎是:通常,要动态分配字符串,使用realloc 将其大小一一增大。但是 sscanf 尝试一次“抛出”已解析子字符串的所有内容。由于字符串的大小不一致,我不能像使用 operation 那样简单地将它们设为静态。

是的,我可以使用一个大的静态缓冲区,但这似乎是一项重要的任务,而且由于我是一名本科生,我想知道正确的方法。提前致谢!

【问题讨论】:

  • scanf("%c", &amp;current_letter); -> current_letter = getchar();current_letter 的类型更改为int
  • 您对使用链表处理“大数字”的想法很好。这个网站上有一些例子。您可能希望以 1024 块为单位重新分配并跟踪空间 avaialbleused,并且仅在 available == used 时重新分配。 realloc() 每个字符都非常低效。
  • 谢谢大卫!这是我前段时间制作的通用 read_line() 函数。问题不在于该功能,肯定有改进的方法(例如,我还没有将它划分为更小的功能),但它确实有效!我已经测试了我必须阅读的所有输入,问题肯定是分配缓冲区
  • 你还没有为first_number_buffersecond_number_buffer 分配空间,所以当你执行sscanf(line, "%s %s %s", operation, first_number_buffer, second_number_buffer); 时一切都乱套了。如果你有一个足够兼容 POSIX 的操作系统(运行时 C 库),你可以使用%ms 作为这两个数字并传递&amp;first_number_buffer&amp;second_number_buffer。否则,您必须对数字的长度施加限制(也许是 1024?)并从那里开始工作。
  • 是的,我知道这不是你的问题,这就是为什么我用评论而不是答案来注明。

标签: c


【解决方案1】:

我相信我能做到我最初的打算。

很难将sscanf 用于该任务,因为必须事先分配sscanf 使用所需的内存。在问题的上下文中,这是未知的。

不过,@Cheatah 建议使用strtok,效果很好。这是代码的最终版本,使用它:

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

#include<stdlib.h>
#include<stdio.h>



char* read_line(){
    const char UNIX_LINEBREAK = '\n';
    const char WINDOWS_LINEBREAK = '\r';
    const char C_STRING_TERMINATOR = '\0';
    
    char extra_linebreak;
    char current_letter;

    char* line = NULL;
    int position = 0;
    bool reading_line = true;
    

    while(reading_line){
        scanf("%c", &current_letter);
        if(current_letter == UNIX_LINEBREAK || current_letter == EOF){
            reading_line = false;
        }
        else if(current_letter == WINDOWS_LINEBREAK) {
            reading_line = false;
            extra_linebreak = (char)getchar();
        }
        else {
            line = (char*) realloc(line, sizeof(char) * (position +  1));
            line[position] = current_letter;
            position ++;
        }
    }

    line = (char*) realloc(line, sizeof(char) * (position +  1));
    line[position] = C_STRING_TERMINATOR;

    return line; 
}

int main (){
    char* line = read_line();
    char* operation;
    char* first_number_buffer;
    char* second_number_buffer;

    char *line_split = strtok(line,  " ");
    operation = (char *) malloc(strlen(line_split) * sizeof(char));
    strcpy(operation, line_split);

    line_split = strtok(NULL, " ");
    first_number_buffer = (char *) malloc(strlen(line_split) * sizeof(char));
    strcpy(first_number_buffer, line_split);

    line_split = strtok(NULL, " ");
    second_number_buffer = (char *) malloc(strlen(line_split) * sizeof(char));
    strcpy(second_number_buffer, line_split);

    printf("%s\n%s\n%s\n", line,first_number_buffer,second_number_buffer);
}

输入:

sum 23879238932898239832983298329839229383928329 239823983298392893289238932883290312803291832109230189

输出:

sum
23879238932898239832983298329839229383928329
239823983298392893289238932883290312803291832109230189

可以通过多种方式改进代码。有人指出 read_line() 中没有正确检查 EOF,而 main 肯定可以在更小的函数中重构。

但是,在这种情况下,使用 strtok() 代替 sscanf() 的想法即使使用不定数量的令牌也是可行的。一会儿看一个strtok的例子:https://www.cplusplus.com/reference/cstring/strtok/

【讨论】:

  • scanf("%c", &amp;current_letter); if(current_letter == UNIX_LINEBREAK || current_letter == EOF){ 未能检测到文件结尾或输入错误。检查scanf() 或更好的返回值:使用fgetc()
  • extra_linebreak = (char)getchar(); 假定 '\n' 必须跟在 '\r' 后面。由于这不是文本文件的规范,如果假设不成立,最好ungetc()
  • int position = 0;size_t position = 0; 更好地处理大行。
  • @chux-ReinstateMonica 在该应用程序的上下文中,就是这样! \r 将跟随 \n。我们有 .in 文件,这些文件有时会被 Windows 用户打开。当这种情况发生时,windows 会更改行的终止,将\n 替换为\r\n。这部分逻辑适用于我们的输入,我已经测试过了
  • 不过,您对 EOF 问题的看法是 100% 正确的,我必须解决它,非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-28
  • 1970-01-01
  • 2020-08-14
  • 1970-01-01
  • 2021-01-11
  • 1970-01-01
相关资源
最近更新 更多