【问题标题】:Reading a file character by character in C在C中逐字符读取文件
【发布时间】:2011-06-16 22:16:51
【问题描述】:

我正在用 C 编写一个 BF 解释器,但在读取文件时遇到了问题。我曾经使用scanf 来读取第一个字符串,但是你的BF 代码中不能有空格或cmets。

现在这就是我所拥有的。

char *readFile(char *fileName)
{
  FILE *file;
  char *code = malloc(1000 * sizeof(char));
  file = fopen(fileName, "r");
  do 
  {
    *code++ = (char)fgetc(file);

  } while(*code != EOF);
  return code;
}

我知道问题出在我如何将文件中的下一个字符分配给代码指针,但我只是不确定那是什么。
我缺乏指针知识,这是本练习的重点。 解释器工作正常,全部使用指针,我只是在读取文件时遇到问题。

(稍后我将实现仅将+-><[]., 读取到文件中,尽管如果有人有好的方法可以做到这一点,如果你能告诉我会很棒!)

【问题讨论】:

    标签: c file-io io iostream


    【解决方案1】:

    您的代码有很多问题:

    char *readFile(char *fileName)
    {
        FILE *file;
        char *code = malloc(1000 * sizeof(char));
        file = fopen(fileName, "r");
        do 
        {
          *code++ = (char)fgetc(file);
    
        } while(*code != EOF);
        return code;
    }
    
    1. 如果文件大于 1000 字节怎么办?
    2. 每次读取字符时都会增加code,并且将code返回给调用者(即使它不再指向内存块的第一个字节,因为它是由malloc返回的)。
    3. 您将fgetc(file) 的结果转换为char。在将结果转换为 char 之前,您需要检查 EOF

    维护malloc返回的原始指针很重要,以便以后可以释放它。如果我们忽略文件大小,我们仍然可以通过以下方式实现:

    char *readFile(char *fileName)
    {
        FILE *file = fopen(fileName, "r");
        char *code;
        size_t n = 0;
        int c;
    
        if (file == NULL)
            return NULL; //could not open file
    
        code = malloc(1000);
    
        while ((c = fgetc(file)) != EOF)
        {
            code[n++] = (char) c;
        }
    
        // don't forget to terminate with the null character
        code[n] = '\0';        
    
        return code;
    }
    

    有多种系统调用可以为您提供文件的大小;一个常见的是stat

    【讨论】:

    • 什么是计算文件中字符数的最简单方法,以便我可以将“1000”设置为?另外,我不确定您所说的数字 2 是什么意思,我知道我这样做是错误的,但是我将如何修改呢?
    • @pwnmonkey:我的意思是当它指向文件的 end 时,您正在返回 code,而不是在它指向开头时。
    • @deamlax 您的示例似乎有一个小错字。 fgets 需要多个参数。你的意思是fgetc 或许?
    • 我不得不将 malloc 转换为 code = (char*)malloc(1000);,因为在 c++ 中将 char* 转换为 void 时出错
    • @A.k.如果您使用 C++,请不要使用 malloc。使用std::istream 读取文件,并使用std::vector<char> 或其他结构来处理缓冲区。使用new char[] 作为最后的手段,但在编写 C++ 代码时避免使用malloc,因为有更好的(类型安全)替代方案:)
    【解决方案2】:

    从@dreamlax 扩展上述代码

    char *readFile(char *fileName) {
        FILE *file = fopen(fileName, "r");
        char *code;
        size_t n = 0;
        int c;
    
        if (file == NULL) return NULL; //could not open file
        fseek(file, 0, SEEK_END);
        long f_size = ftell(file);
        fseek(file, 0, SEEK_SET);
        code = malloc(f_size);
    
        while ((c = fgetc(file)) != EOF) {
            code[n++] = (char)c;
        }
    
        code[n] = '\0';        
    
        return code;
    }
    

    这会给你文件的长度,然后逐个字符地读取它。

    【讨论】:

    • 你能解释一下 fseek 和 ftell 是如何工作的吗?我认为您需要某种循环来计算文件的字符数。
    • fseek 视为重新定位光标的一种方式。 fseek(文件,0,SEEK_END);将光标放在文件末尾,然后ftell 告诉您光标在哪里。这给了你文件的大小。 fseek(file, 0, SEEK_SET); 将光标放回文件的开头以便可以读取。如果不这样做,则会从头开始读取文件,这会导致错误并破坏整个操作。
    【解决方案3】:

    这是一种简单的方法,可以忽略除有效的脑残字符之外的所有内容:

    #define BF_VALID "+-><[].,"
    
    if (strchr(BF_VALID, c))
        code[n++] = c;
    

    【讨论】:

    • 是的,我以前使用过这个,但问题是如果文件中有换行符,它会将这些换行符放入字符串中。如果我想在一行中将纯 BF 代码写入文件,甚至将其打印到控制台,这很糟糕。
    • @pwnmonkey:不,这不会将任何换行符存储到目标字符串中(如果您将\n 添加到BF_VALID,它会)。
    【解决方案4】:

    每次调用函数时,文件都被打开而不是关闭

    【讨论】:

    • 是的,我知道,我已经更改了,但感谢您告诉我。
    【解决方案5】:

    我认为最重要的问题是你在读入内容时递增code,然后返回code 的最终值,即你将返回一个指向end 的字符串。您可能想在循环之前复制code,然后将其返回。

    此外,C 字符串需要以空值结尾。您需要确保将'\0' 直接放在您读入的最后一个字符之后。

    注意:您可以只使用fgets() 一次性获得整行。

    【讨论】:

    • 对,但我将排除任何不是 BF 代码的内容,因此一次运行更容易。
    【解决方案6】:

    两者中的任何一个都可以解决问题 -

    char *readFile(char *fileName)
    {
      FILE *file;
      char *code = malloc(1000 * sizeof(char));
      char *p = code;
      file = fopen(fileName, "r");
      do 
      {
        *p++ = (char)fgetc(file);
      } while(*p != EOF);
      *p = '\0';
      return code;
    }
    
    char *readFile(char *fileName)
    {
      FILE *file;
      int i = 0;
      char *code = malloc(1000 * sizeof(char));
      file = fopen(fileName, "r");
      do 
      {
        code[i++] = (char)fgetc(file);
      } while(code[i-1] != EOF);
      code[i] = '\0'
      return code;
    }
    

    就像其他发帖人指出的那样,您需要确保文件大小不超过 1000 个字符。另外,请记住在使用完毕后释放内存。

    【讨论】:

      【解决方案7】:

      这里的问题是双重的

      • a) 在检查读入的值之前增加指针,并且
      • b) 你忽略了 fgetc() 返回一个 int 而不是 char 的事实。

      第一个很容易解决:

      char *orig = code; // the beginning of the array
      // ...
      do {
        *code = fgetc(file);
      } while(*code++ != EOF);
      *code = '\0'; // nul-terminate the string
      return orig; // don't return a pointer to the end
      

      第二个问题更微妙 -fgetc 返回一个 int 以便可以将 EOF 值与任何可能的 char 值区分开来。解决此问题时使用临时 int 进行 EOF 检查,并且可能使用常规 while 循环而不是 do / while。

      【讨论】:

      • 如果 EOF 介于 -1 和 -128 之间,那么 可表示为 char 值(假设实现的普通 char 类型已签名) .在这种情况下,从intchar 的转换完全定义为当值可以由char 表示时,EOF 可能就是这种情况。
      猜你喜欢
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多