打开没有 windows 行结尾的非文本文件

Open non text file without windows line ending

我接手了一个项目,使用下面的函数来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] = '[=11=]';
    } else {
        fprintf(stderr, "Error reading file %s\n", filename);
        exit(1);
    }
    return buffer;
}

使用 rb 模式是因为有时文件可以是电子表格,因此我想要文本文件中的信息。

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

我不确定哪种方法更好 不会让 windows 行结尾混乱我的代码。

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

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

  1. dos2unix
  2. r 对比 rb 模式,
  3. 或任何其他适合的解决方案 更好的问题。

注意:我相信我理解 rrb 模式,但如果你能解释为什么对于这种特定情况它是一个坏的或好的解决方案(我认为它不会是很好,因为有时它会打开电子表格,但我不确定)。

代码只是将文件的内容复制到分配的缓冲区。 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;
} 

按照这些思路行事。如果您也想写入文件,请调整设置。

If my understanding is correct the rb mode is used because sometimes the file can be a spreadsheet and therefore the programs just want the information as in a text file.

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

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

A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined. Characters may have to be added, altered, or deleted on input and output to conform to differing conventions for representing text in the host environment. Thus, there need not be a one- to-one correspondence between the characters in a stream and those in the external representation. [...]

(C2011 7.21.2/2)

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

您的 fopen() 模式中的 b 指定文件应作为二进制流打开——也就是说,不会对从文件中读取的字节执行任何转换。这是否正确取决于您的环境和应用程序的要求。然而,这在 Linux 或其他 Unix 上没有实际意义,因为在此类系统上文本流和二进制流之间没有明显的区别。

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

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

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