【问题标题】:How to read in specific data from a text file (C)?如何从文本文件 (C) 中读取特定数据?
【发布时间】:2018-04-26 00:15:20
【问题描述】:

我对 C(以及这个网站)有点陌生,并且对我正在从事的项目有疑问。我坚持的部分是文件 I/O 的东西,因为我在上一堂课中从未学过这部分。因此,输入 (.txt) 文件如下所示:

REGISTERS
R5 11
R7 15
R21 4
MEMORY
4 12
12 92
[OTHER STUFF]

该文件为我们提供了任意寄存器编号,然后是其内容(R0 到 31,省略了值 0 的寄存器)。下一节给出了类似格式的内存位置,然后是内容。然后是我以后可以处理的更多部分。

我的问题是:如何读取寄存器/内存位置及其值?目前我有一个大小为 32 的数组来存储内容,还有一个用于 Reg 编号和值的变量(例如:Registers[regNumber] = regValue;),但我不知道如何在单词 REGISTERS 之后开始正确读取值。

我尝试过的:

我环顾四周,并设法读入了一个只有一行的测试输入文件:“R5 11”。为此我做了:

fscanf(file_ptr, "R%d %d", &regNumber, &regValue);

我打印了变量 regNumber、regValue,它们正确地给了我 5 和 11。但是对于单词 REGISTERS 之后的任意行数并在单词 MEMORY 处停止,我该如何执行此操作?

我假设无论解决方案是什么,我都可以做同样的事情来读取 MEMORY 位置和值。

【问题讨论】:

    标签: c input file-io


    【解决方案1】:

    您可以使用 fgets() 读取文本文件的整行,然后使用 strcmp 或 strncmp 读取“REGISTERS”和“MEMORY”字符串文字的行。

    您的 input.txt 的格式在“REGISTERS”之后的所有内容在“MEMORY”之前都具有相同的格式,而在“MEMORY”之后的所有内容都是相同的格式。尽管可能有更好的选择,但可能类似以下的方法会起作用:

    int main () {
       FILE *fp;
       char str[60];
       int input_type = 0; //you can enum this for the input types you have
       int line_num = 0;
    
       /* opening file for reading */
       fp = fopen("input.txt" , "r");
       if(fp == NULL) {
          perror("Error opening file");
          return(-1);
       }
       while( fgets (str, 60, fp)!=NULL ) { // dont really need the != NULL compare here.
          line_num++;
          /* Parse the next line in the file */
          if(strcmp(str,"MEMORY") == 0){
             //the next lines will be formatted like memory input lines
             input_type = 2; //This should be an enum or macro
             continue;
          }
          if(strcmp(str,"REGISTERS") == 0){
             //the next lines will be formatted like register input lines
             input_type = 1;//Also should be enum or macro
             continue;
          }
          switch(input_type){
             case 0:
                fprintf(stderr,"Failed to find REGISTER keyword%d\n",line_num);
                break;
             case 1:
                sscanf(str, "R%d %d", &regNumber, &regValue); //parse known string format
                do_something(&regNumber, &regValue);
                break;
             case 2:
                sscanf(str, "%d %d", &memNumber, &memValue); //parse known string format
                do_something_else(&memNumber, &memValue);
                break;
             default:
                fprintf(stderr,"Could not find input format at line %d\n",line_num);
                break;
          }
       }
       fclose(fp);
    
       return(0);
    }
    

    【讨论】:

      【解决方案2】:

      我不会在这里使用fscanf,因为你不能向前看,看看下一个 行是REGISTERSMEMORY 或其他。理论上你可以检查 返回值fscanf,但代码很快就会变得非常混乱。

      更好的解决方案是使用fgets 获取整行。如果线相等 到REGISTERS\n,那么你知道下一行是寄存器行,等等。 解析本身可以使用sscanf 完成,类似于fscanf,但不是 从FILE* 缓冲区读取,它从字符串读取。

      所以,我会这样做:

      enum mode_t {
          REGISTERS = 0,
          MEMORY,
          ...,
          INVALID
      };
      
      int parse_file(FILE *file_ptr, int *registers, int some_other_params)
      {
          if(file_ptr == NULL || registers == NULL)
              return 0;
      
          char line[1024];
      
          enum mode_t mode = INVALID;
      
          while(fgets(line, sizeof line, file_ptr))
          {
              if(strcmp(line, "REGISTERS\n") == 0)
              {
                  mode = REGISTERS;
                  continue;
              } else if(strcmp(line, "MEMORY\n") == 0) {
                  mode = MEMORY;
                  continue;
              } // keep adding if(strcmp.... as needed)
      
              if(mode == INVALID)
              {
                  fprintf(stderr, "Invalid file format\n");
                  return 0; // or a better error handling
              }
      
              int ret;
              switch(mode)
              {
                  case REGISTERS:
                      ret = parse_registers(line, registers);
                      break;
                  case MEMORY:
                      ret = parse_memory(line, some_other_args);
                      break;
                  ...
              }
      
              // evaluate ret and do error handling if necessary
          }
      
          return 1;
      }
      

      使用mode作为解析状态,可以判断你是哪个section 解析。然后你必须编写像parse_registers这样的函数和 parse_memory 对行进行真正的解析,例如:

      int parse_registers(const char *line, int *registers)
      {
          if(line == NULL || registers == NULL)
              return 0;
      
          int regNumber, regValue;
      
          if(sscanf("R%d %d", &regNumber, &regValue) != 2)
          {
              fprintf(stderr, "Invalid format for REGISTER section\n");
              return 0;
          }
      
          if(regNumber < 0 || regNumber > 31)
          {
              fprintf(stderr, "Invalid register number %d\n", regNumber);
              return 0;
          }
      
          registers[regNumber] = regValue;
      
          return 1;
      }
      

      现在您可以实现 parse_memory 和其他 parse_xxx 函数 文件的其他部分。

      【讨论】:

      • 这应该是公认的答案!它是完整的、结构良好的,而且你在解释你的方法方面做得很好。但是,如果我错了,请纠正我,但第一个代码块中的 strcmp(line, "MEMORY") 不应该检查 "MEMORY\n" 吗?
      • @ThunderStruct 但是,如果我错了,请纠正我,但第一个代码块中的 strcmp(line, "MEMORY") 不应该检查 "MEMORY\n" 是的,确实应该.感谢反馈,我已经解决了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多