簡體   English   中英

組合目錄和文件路徑 - C

[英]Combine directory and file path - C

作為學習 C 的一部分,我編寫了以下代碼來將目錄名和文件名結合起來。 例如: combine("/home/user", "filename")將導致/home/user/filename 此功能預計可跨平台工作(至少在所有流行的 linux 發行版和 windows 32 和 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 的行。 所有這 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方法。 這避免了使用mallocfree問題。 這是用法

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;
}

有沒有更多改進的建議?

並且存在內存泄漏:

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

編輯:你的last_char循環有一個錯誤:它總是返回path1的結尾,所以你的答案可能會以雙斜杠 // 結束。 (但 Unix 會將其視為單個斜杠,除非它位於開頭)。 無論如何,我的建議解決了這個問題——我認為這與 jdmichal 的回答非常相似。 而且我看到您在原始代碼中的這一點是正確的(我承認我只是看了一眼——這對我的口味來說太復雜了;您的新代碼要好得多)。

還有兩個稍微主觀一點的意見:

  • 我會使用stpcpy() ,以避免strcat()的低效率。 (如果需要,可以輕松編寫自己的代碼。)
  • 有些人對strcat()之類的東西有非常強烈的看法,認為它是不安全的 但是,我認為您在這里的使用非常好。
  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(directory_separator) 而不是 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. 這個過程看起來還不錯。 我認為只有一些細節可以解決,主要是用笨拙的方式做事。 但是你做得很好,因為你已經可以意識到正在發生的事情並尋求幫助。

這是我使用的:

#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';
}

也許我對此有點晚了,但我以某種方式改進了更新后的代碼,它也適用於諸如“/../”之類的東西。

/*
 * Combine two paths into one. Note that the function
 * will write to the specified buffer, which has to
 * be allocated beforehand.
 *
 * @dst: The buffer to write to
 * @pth1: Part one of the path
 * @pth2: Part two of the path
*/
void joinpath(char *dst, const char *pth1, const char *pth2)
{
    if(pth1 == NULL && pth2 == NULL) {
        strcpy(dst, "");
    }
    else if(pth2 == NULL || strlen(pth2) == 0) {
        strcpy(dst, pth1);
    }
    else if(pth1 == NULL || strlen(pth1) == 0) {
        strcpy(dst, pth2);
    } 
    else {
        char directory_separator[] = "/";
#ifdef WIN32
        directory_separator[0] = '\\';
#endif
        const char *last_char = pth1;
        while(*last_char != '\0')
            last_char++;        
        int append_directory_separator = 0;
        if(strcmp(last_char, directory_separator) != 0) {
            append_directory_separator = 1;
        }
        strcpy(dst, pth1);
        if(append_directory_separator)
            strcat(dst, directory_separator);
        strcat(dst, pth2);
    }

    char *rm, *fn;
    int l;
    while((rm = strstr (dst, "/../")) != NULL) {
        for(fn = (rm - 1); fn >= dst; fn--) {
            if(*fn == '/') {
                l = strlen(rm + 4);
                memcpy(fn + 1, rm + 4, l);
                *(fn + len + 1) = 0;
                break;
            }
        }
    }
}

只是為了改進您的功能的一點評論:

Windows 確實支持路徑中的'/''\\\\'分隔符。 所以我應該能夠執行以下調用:

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

編寫多平台項目時的一個想法可能是在任何輸入路徑(來自用戶輸入、加載的文件...)中將'\\\\'轉換為'/' ,然后您只需處理'/'字符。

問候。

快速瀏覽顯示:

  1. 您使用的不是標准 C 的 C++ 注釋 (//)
  2. 您在代碼的一部分聲明變量 - 也不是 C。它們應該在函數的開頭定義。
  3. #1 處的字符串 p1 在 #2 處寫入了 1 個太多字節,因為 strlen 返回字符串的長度,而空終止符還需要 1 個字節。
  4. malloc 沒有分配足夠的內存 - 您需要路徑 1 的長度 + 路徑 2 的長度 + 分隔符的長度 + 空終止符。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM