作为学习C的一部分,我编写了以下代码来将目录名与文件名组合在一起。例如:combine("/home/user", "filename")
将导致/home/user/filename
。这个功能可以跨平台工作(至少在所有流行的Linux发行版和Windows 32和64bit上)。
这是代码。
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;
}
有关更多改进的建议吗?
答案 0 :(得分:4)
内存泄漏:
const char *one = combine("foo", "file");
const char *two = combine("bar", "");
//...
free(one); // needed
free(two); // disaster!
修改:您的新代码看起来更好。一些小的风格变化:
;;
。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()
之类的观点非常强烈,认为不安全。但是,我认为你在这里的用法非常好。答案 1 :(得分:2)
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
的工作方式一样。这清楚表明他们必须管理分配和释放内存。
这个过程看起来不错。我认为只有一些细节可以解决,主要是以笨重的方式做事。但你表现得很好,因为你已经认识到这种情况并寻求帮助。
答案 2 :(得分:1)
也许我有点迟了,但是我以某种方式改进了更新的代码,使其也可以与“ /../”之类的东西一起使用。
/*
* 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;
}
}
}
}
答案 3 :(得分:0)
这就是我使用的:
#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)
为了改善你的功能,请注意一点:
Windows确实支持路径中的'/'
和'\\'
分隔符。所以我应该能够执行以下调用:
const char* path1 = "C:\\foo/bar";
const char* path2 = "here/is\\my/file.txt";
char destination [ MAX_PATH ];
combine ( destination, path1, path2 );
编写多平台项目时的想法可能是在任何输入路径(从用户输入,加载的文件......)中将'\\'
转换为'/'
,然后您只需要处理{ {1}}字符。
问候。
答案 5 :(得分:-1)
快速浏览一下: