打开非文本文件而不使用Windows行结束

时间:2017-07-05 13:59:39

标签: c text-files fopen

我接管了一个使用以下函数的项目read文件:

char *fetchFile(char *filename) {
    char *buffer;
    int len;
    FILE *f = fopen(filename, "rb");
    if(f) {
        if(verbose) {
            fprintf(stdout, "Opened file %s successfully\n", filename);
        }
        fseek(f, 0, SEEK_END);
        len = ftell(f);
        fseek(f, 0, SEEK_SET);
        if(verbose) {
            fprintf(stdout, "Allocating memory for buffer for %s\n", filename);
        }
        buffer = malloc(len + 1);
        if(buffer) fread (buffer, 1, len, f);
        fclose (f);
        buffer[len] = '\0';
    } else {
        fprintf(stderr, "Error reading file %s\n", filename);
        exit(1);
    }
    return buffer;
}

使用rb模式是因为有时文件可能是电子表格,因此我希望信息与文本文件一样。

程序在linux机器上运行,但要读取的文件来自linux和windows。

我不确定哪种方法更好没有Windows行结束我的代码。

我在考虑在此函数的开头使用dos2unix。 我还考虑过以r模式打开,但我相信在打开非文本文件时可能会搞砸。

我想更好地理解使用之间的区别:

  1. dos2unix
  2. r vs rb模式,
  3. 或任何其他适合的解决方案 更好的问题。
  4. 注意:我相信我理解r vs rb模式,但是如果你能解释为什么它对于这种特定情况来说是一个坏的或好的解决方案(我认为这不会很好,因为有时它打开电子表格,但我不确定那个)。

2 个答案:

答案 0 :(得分:1)

  

如果我的理解是正确的,则使用rb模式,因为有时文件可能是电子表格,因此程序只需要文本文件中的信息。

你似乎不确定,虽然也许你确实理解正确,但你的解释并没有给我任何信心。

C知道两种不同的流:二进制流和文本流。二进制流只是一个有序的字节序列,按原样写入和/或读取,没有任何转换。另一方面,

  

文本流是由字符组成的有序字符序列   行,每行由零个或多个字符加上a组成   终止换行符。最后一行是否需要   终止换行符是实现定义的。的字符   可能必须在输入和输出上添加,更改或删除   符合在主机中表示文本的不同约定   环境。因此,不需要一对一的对应关系   流中的字符与外部字符之间   代表。 [...]

C2011 7.21.2/2

对于某些实现,例如POSIX兼容的实现,这是一个没有区别的区别。对于其他实现,例如针对Windows的实现,差异很重要。特别是,在Windows上,文本流在外部表示中的回车/换行对与内部表示中的换行(仅)之间即时转换。

b模式下的fopen()指定文件应作为二进制流打开 - 也就是说,不会对从文件读取的字节执行转换。这是否正确,取决于您的环境和应用程序的要求。然而,这在Linux或其他Unix上没有实际意义,因为在这样的系统上文本和二进制流之间没有可观察到的差异。

dos2unix将输入文件中的回车符/换行符对转换为单换行符(换行符)。这将把Windows风格的文本文件或具有混合Windows / Unix行终止符的文件转换为Unix文本文件约定。如果文件中同时存在Windows样式和Unix样式的行终止符,则这是不可逆转的,如果文件首先不是文本文件,则可能会损坏您的文件。

如果您的输入有时是二进制文件,那么以二进制模式打开是合适的,而dos2unix的转换可能不是。如果是这种情况并且您还需要翻译文本文件行终止符,那么您首先需要一种方法来区分哪种情况适用于任何特定文件 - 例如,通过命令行参数或预先分析文件通过libmagic。然后,您必须为文本文件提供不同的处理;你的主要选择是

  1. 在您自己的代码中执行行终止符转换。
  2. 为文本和二进制文件提供fetchFile()函数的单独版本。

答案 1 :(得分:0)

代码只是将文件的内容复制到分配的缓冲区。 UNIX方式(YMMV)只是内存映射文件而不是读取它。快得多。

// untested code
void* mapfile(const char *name)
{
    int fd;
    struct stat st;

    if ((fd = open(name, O_RDONLY)) == -1)
        return NULL;
    if (fstat(fd, &st)) {
        close(fd);
        return NULL;
    }

    void *p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, 0, fd);
    close(fd);
    if (p == (void *)MAP_FAILED)
        p = NULL;

    return p;
} 

这些方面的东西都可行。如果要同时写入文件,请调整设置。