[英]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;
}
我對上述代碼有以下疑問。
char*
字符串中獲取最后一個元素的正確方法是什么。malloc
分配一個新字符串。 我不確定這是正確的方法。 調用者是否希望釋放結果? 我如何指示調用者他必須釋放結果? 有沒有更不容易出錯的方法?任何幫助都會很棒。
編輯
修復了所有討論的問題並實施了建議的更改。 這是更新的代碼。
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
方法。 這避免了使用malloc
和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;
}
有沒有更多改進的建議?
並且存在內存泄漏:
const char *one = combine("foo", "file");
const char *two = combine("bar", "");
//...
free(one); // needed
free(two); // disaster!
編輯:您的新代碼看起來更好。 一些小的風格變化:
;;
在第 4 行。strlen(path2) == 0
替換為path2[0] == '\\0''
或僅替換!path2[0]
。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()
之類的東西有非常強烈的看法,認為它是不安全的。 但是,我認為您在這里的使用非常好。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;
}
不太容易出錯的方法是讓用戶為您提供目標緩沖區及其長度,這與strcpy
工作方式非常相似。 這清楚地表明他們必須管理分配和釋放內存。
這個過程看起來還不錯。 我認為只有一些細節可以解決,主要是用笨拙的方式做事。 但是你做得很好,因為你已經可以意識到正在發生的事情並尋求幫助。
這是我使用的:
#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 );
編寫多平台項目時的一個想法可能是在任何輸入路徑(來自用戶輸入、加載的文件...)中將'\\\\'
轉換為'/'
,然后您只需處理'/'
字符。
問候。
快速瀏覽顯示:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.