代码之家  ›  专栏  ›  技术社区  ›  Navaneeth K N

合并目录和文件路径-c

  •  7
  • Navaneeth K N  · 技术社区  · 14 年前

    作为学习C的一部分,我编写了以下代码,将目录名和文件名结合起来。如: combine("/home/user", "filename") 将导致 /home/user/filename . 这个功能可以跨平台工作(至少在所有流行的Linux发行版和Windows32和64位上都可以)。

    这是密码。

    const char* combine(const char* path1, const char* path2)
    {
        if(path1 == NULL && path2 == NULL) {
            return NULL;
        }
    
        if(path2 == NULL || strlen(path2) == 0) return path1;
        if(path1 == NULL || strlen(path1) == 0) return path2;
    
        char* directory_separator = "";
    #ifdef WIN32
        directory_separator = "\\";
    #else 
        directory_separator = "/";
    #endif
    
        char p1[strlen(path1)];                    // (1)
        strcpy(p1, path1);                         // (2) 
        char *last_char = &p1[strlen(path1) - 1];  // (3)
    
        char *combined = malloc(strlen(path1) + 1 + strlen(path2));
        int append_directory_separator = 0;
        if(strcmp(last_char, directory_separator) != 0) {
            append_directory_separator = 1;
        }
        strcpy(combined, path1);
        if(append_directory_separator)
            strcat(combined, directory_separator);
        strcat(combined, path2);
        return combined;
    }
    

    关于上述代码,我有以下问题。

    1. 考虑编号为1、2、3的行。这三行用于从字符串中获取最后一个元素。看起来我在为这么小的事情写更多的代码。从中获取最后一个元素的正确方法是什么 char* 字符串。
    2. 要返回结果,我将使用 malloc . 我不确定这是正确的方法。呼叫方是否希望释放结果?我怎样才能告诉打电话的人他必须释放结果?有没有一种不易出错的方法?
    3. 您如何评价代码(差、平均、好)?哪些领域可以被整合?

    任何帮助都会很好。

    编辑

    修复了所有讨论的问题,并实施了建议的更改。这是更新后的代码。

    void combine(char* destination, const char* path1, const char* path2)
    {
        if(path1 == NULL && path2 == NULL) {
            strcpy(destination, "");;
        }
        else if(path2 == NULL || strlen(path2) == 0) {
            strcpy(destination, path1);
        }
        else if(path1 == NULL || strlen(path1) == 0) {
            strcpy(destination, path2);
        } 
        else {
            char directory_separator[] = "/";
    #ifdef WIN32
            directory_separator[0] = '\\';
    #endif
            const char *last_char = path1;
            while(*last_char != '\0')
                last_char++;        
            int append_directory_separator = 0;
            if(strcmp(last_char, directory_separator) != 0) {
                append_directory_separator = 1;
            }
            strcpy(destination, path1);
            if(append_directory_separator)
                strcat(destination, directory_separator);
            strcat(destination, path2);
        }
    }
    

    在新版本中,调用者必须分配足够的缓冲区并发送到 combine 方法。这样就避免了使用 马洛克 free 问题。这是用法

    int main(int argc, char **argv)
    {
        const char *d = "/usr/bin";
        const char* f = "filename.txt";
        char result[strlen(d) + strlen(f) + 2];
        combine(result, d, f);
        printf("%s\n", result);
        return 0;
    }
    

    有什么改进的建议吗?

    5 回复  |  直到 9 年前
        1
  •  4
  •   Joseph Quinsey Taseen    14 年前

    内存泄漏:

    const char *one = combine("foo", "file");
    const char *two = combine("bar", "");
    //...
    free(one);   // needed
    free(two);   // disaster!
    

    编辑: 您的新代码看起来更好。一些细微的风格变化:

    • 双半结肠 ;; 在第4行。
    • 在第6行中,更换 strlen(path2) == 0 具有 path2[0] == '\0'' 或者只是 !path2[0] .
    • 与第9行类似。
    • 删除循环确定 last_char 及使用 const char last_char = path1[strlen(path1) - 1];
    • 变化 if(append_directory_separator) if(last_char != directory_separator[0]) . 所以你不需要变量 append_directory_separator 再。
    • 让你的功能也恢复 destination ,类似于 strcpy(dst, src) 回报 dst .

    编辑 :和您的循环 拉斯特查尔 有一个 缺陷 :它总是返回 path1 所以你可以在答案中加上一个斜线。(但是,除非在开始处,否则Unix将把它当作一个斜杠)。不管怎样,我的建议解决了这个问题——我认为这与JDmichal的答案非常相似。 我看到你在你的 起初的 代码(我承认我只看了一眼——这对我来说太复杂了;你的新代码好多了)。

    还有两种更主观的观点:

    • 我会用 stpcpy() ,以避免 strcat() . (如果需要的话,你可以自己写。)
    • 有些人对 斯特劳() 像是 不安全的 . 不过,我认为你在这里的用法非常好。
        2
  •  2
  •   jdmichal    14 年前
    1. 你唯一一次使用 last_char 在比较中,以检查最后一个字符是否为分隔符。

    为什么不换成这个呢?

    /* Retrieve the last character, and compare it to the directory separator character. */
    char directory_separator = '\\';
    if (path1[strlen(path1) - 1] == directory_separator)
    {
        append_directory_separator = 1;
    }
    

    如果要考虑多个字符分隔符的可能性,可以使用以下内容。但在分配组合字符串以添加strlen(目录分隔符)而不是仅添加1时,请确保。

    /* First part is retrieving the address of the character which is
       strlen(directory_separator) characters back from the end of the path1 string.
       This can then be directly compared with the directory_separator string. */
    char* directory_separator = "\\";
    if (strcmp(&(path1[strlen(path1) - strlen(directory_separator)]), directory_separator))
    {
        append_directory_separator = 1;
    }
    
    1. 更不容易出错的方法是让用户向您提供目标缓冲区及其长度,主要是 strcpy 作品。这表明他们必须管理分配和释放内存。

    2. 这一过程似乎相当体面。我认为只有一些细节可以解决,主要是以笨拙的方式做事。但是你做得很好,因为你已经意识到这一点并寻求帮助。

        3
  •  0
  •   Andrei Sedoi    12 年前

    这是我使用的:

    #if defined(WIN32)
    #  define DIR_SEPARATOR '\\'
    #else
    #  define DIR_SEPARATOR '/'
    #endif
    
    void combine(char *destination, const char *path1, const char *path2) {
      if (path1 && *path1) {
        auto len = strlen(path1);
        strcpy(destination, path1);
    
        if (destination[len - 1] == DIR_SEPARATOR) {
          if (path2 && *path2) {
            strcpy(destination + len, (*path2 == DIR_SEPARATOR) ? (path2 + 1) : path2);
          }
        }
        else {
          if (path2 && *path2) {
            if (*path2 == DIR_SEPARATOR)
              strcpy(destination + len, path2);
            else {
              destination[len] = DIR_SEPARATOR;
              strcpy(destination + len + 1, path2);
            }
          }
        }
      }
      else if (path2 && *path2)
        strcpy(destination, path2);
      else
        destination[0] = '\0';
    }
    
        4
  •  0
  •   S.Clem    9 年前

    为了提高您的功能,您只需说一句话:

    Windows同时支持 '/' '\\' 路径中的分隔符。因此,我应该能够执行以下调用:

    const char* path1 = "C:\\foo/bar";
    const char* path2 = "here/is\\my/file.txt";
    char destination [ MAX_PATH ];
    combine ( destination, path1, path2 );
    

    编写多平台项目时的一个想法是 '' “/” 在任何输入路径中(从用户输入,加载的文件…),那么您只需要处理 “/” 字符。

    当做。

        5
  •  -1
  •   Brian    14 年前

    快速浏览显示:

    1. 您使用的是C++注释(//),而不是标准C。
    2. 您声明的变量是代码的一部分,而不是C。它们应该在函数开始时定义。
    3. 您在1处的字符串p1在2处写入了太多的字节,因为strlen返回字符串的长度,而您需要为空终止符多输入一个字节。
    4. malloc没有分配足够的内存-您需要path1的长度+path2的长度+分隔符的长度+空终止符。