努力构建可移植代码以使用 C 在任何操作系统中生成文件

Struggling to build a portable code to generate a file in any operating system using C

我正在尝试构建一个 C 可移植代码(适用于 Windows、MacOS 和 Linux),它创建一个输出 .txt 文件来接收数值模拟的结果。

总而言之,该代码采用文件名和扩展名,并检查该文件是否已存在于目录中。如果是这样,它会创建另一个同名文件,但在末尾的括号 (#) 之间加上一个数字以区分旧文件和新文件。

问题是:它在mac环境下正常工作,但是当我在windows上编译并运行它时,执行结束时没有创建文件.我找不到我做错了什么。

此外,我正在使用 Intel C/C++ Classic 编译器。如果我使用另一个编译器,例如 Intel® oneAPI DPC++/C++ Compiler for windows,当我调用 sizeof(src) 时,它会抱怨使用 sizeof(src) =13=]函数。

到目前为止,这是代码的版本:

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

bool file_exists(char *filename);
FILE *create_output_file(char *name, char *ext, bool (*file_exists)(char *));

#define funcname "test"

int main(void) {
    
    // Create output files to store results
    char *output_filename = malloc(200 * sizeof(*output_filename));
    sprintf(output_filename, "%s_out", funcname);
    printf("output_filename = %s\n", output_filename);
    char *ext = ".txt";
    FILE *output_file = create_output_file(output_filename, ext, file_exists);
}

bool file_exists(char *filename) {
    // Try to open file with same name as filename
    FILE *testfile = fopen(filename, "r");
    // Bool variable to check the existence of file
    bool exists = false; 
    // Check the existence of a file called filename
    if (testfile != NULL) {
        // Returns true if the file exists
        exists = true; 
    }
    // Close the file
    fclose(testfile); 
    // Returns the existence of a file (1) = does exist, (0) = does not exist
    return exists;
}

FILE *create_output_file(char *name, char *ext, bool (*file_exists)(char *)) {
    // Form the full filename
    name = strncat(name, ext, sizeof(ext));
    printf("fullfilename = %s\n", name);
    // Check if a file with the filename exists in the directory
    if (file_exists(name)) {
        // If exists, assign the same name with "(number)" to differentiate the new version
        int j = 1;
        char *numb = malloc(10 * sizeof(*numb));
        sprintf(numb, "(%i)", j);
        // Remove the extension from the name string
        name[strlen(name) - strlen(ext)] = '[=10=]';
        // Add (number) to the name and then add the file extension again
        name = strncat(name, numb, sizeof(numb));
        name = strncat(name, ext, sizeof(ext));
        // Check if the name with numbers exists until it doesn't
        int limit = 1e1;
        while (file_exists(name)) {
            j++;
            sprintf(numb, "(%i)", j);            
            if (j == limit) {
                name[strlen(name) - strlen(numb) + 1 - strlen(ext)] = '[=10=]';
                limit = limit*10;    
            } else {
                name[strlen(name) - strlen(numb) - strlen(ext)] = '[=10=]';
            }
            name = strncat(name, numb, sizeof(numb));
            name = strncat(name, ext, sizeof(ext));
        }
        // Free allocated memory
        free(numb); 
    }
    // After assign the proper name, create the output file
    FILE *output_file = fopen(name, "w");
    // Returns the file
    return output_file;
}

我在这里错过了什么?

存在多个问题:

  • file_exists 中你调用 fclose(testfile) 即使 fopen 失败了这有未定义的行为。

  • create_output_file 中,您对 sizeof 的用法不正确:在 strncat(name, ext, sizeof(ext)); 中,sizeof 应用于指针,因此它的计算结果为指针的大小,而不是它指向的字符串的长度。你可以写

    strncat(name, ext, strlen(ext));
    

    但它完全等同于 strcat(name, ext);

    函数strncat定义为

      char *strncat(char *dest, const char *src, size_t n);
    

    它从src指向的字符串到dest指向的字符串的末尾最多复制n个字符加上一个空终止符。

  • 代码太复杂,你有多次内存泄漏,而且你在编写文件名时没有检查缓冲区溢出。

这是一个简化版本:

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

#define funcname "test"

bool file_exists(const char *filename) {
    // Try to open file with same name as filename
    FILE *testfile = fopen(filename, "r");
    if (testfile != NULL) {
        fclose(testfile);
        return true;
    } else {
        return false;
    }
}

#define MAX_FILE_NUM  10000

FILE *create_output_file(char *name, size_t size,
                         const char *ext,
                         bool (*file_exists)(const char *))
{
    size_t len = strlen(name);
    int i = 0;

    snprintf(name + len, size - len, "%s", ext);
    while (file_exists(name)) {
        if (++i > MAX_FILE_NUM) {
            // all file versions tried.
            return NULL;
        }
        snprintf(name + len, size - len, "(%d)%s", i, ext);
    }
    return fopen(name, "w");
}

int main() {
    // Create output files to store results
    char output_filename[200];
    snprintf(output_filename, sizeof output_filename, "%s_out", funcname);
    printf("output_filename = %s\n", output_filename);
    const char *ext = ".txt";
    FILE *output_file = create_output_file(output_filename,
                            sizeof output_filename, ext, file_exists);
    if (output_file == NULL) {
        printf("could not open output file, last try: %s\n", output_filename);
    } else {
        printf("actual output_filename = %s\n", output_filename);
        fclose(output_file);
    }
    return 0;
}