Linux API/POSIX应该有一些优雅的方法从完整路径中提取基本文件名
使用 basename
(其具有奇怪的边缘情况语义),或通过调用 strrchr(pathname, '/')
并将整个字符串视为基本名称(basename),如果它不包含 '/'
字符,您可以自己完成此操作。
以下是一个单行示例(假设已给定 char * whoami
),它说明了基本算法:
(whoami = strrchr(argv[0], '/')) ? ++whoami : (whoami = argv[0]);
如果有可能为 NULL,则需要进行额外的检查。还请注意,这只是指向原始字符串 - 可能需要使用 "strdup()
"。
如果你也对目录名感兴趣,可以使用strstr
:
char *path ="ab/cde/fg.out";
char *ssc;
int l = 0;
ssc = strstr(path, "/");
do{
l = strlen(ssc) + 1;
path = &path[strlen(path)-l+2];
ssc = strstr(path, "/");
}while(ssc);
printf("%s\n", path);
basename()
函数返回路径的最后一个组成部分,可能是文件夹名称而不是文件名。有两个版本的basename()
函数:GNU版本和POSIX版本。#define _GNU_SOURCE
之后,可以在string.h
中找到GNU版本:
#define _GNU_SOURCEGNU版本使用
#include <string.h>
const
并且不修改参数。
char * basename (const char *path)如果包含了
libgen.h
,则XPG(POSIX)版本将覆盖此函数。
char * basename (char *path)此函数可能通过删除尾随的'/'字节来修改参数。在这种情况下,结果可能与GNU版本不同:
basename("foo/bar/")如果您使用XPG版本,则将返回字符串"bar",如果您使用GNU版本,则返回空字符串。
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *fn;
char *input;
if (argc > 1)
input = argv[1];
else
input = argv[0];
/* handle trailing '/' e.g.
input == "/home/me/myprogram/" */
if (input[(strlen(input) - 1)] == '/')
input[(strlen(input) - 1)] = '\0';
(fn = strrchr(input, '/')) ? ++fn : (fn = input);
printf("%s\n", fn);
return 0;
}
template<typename charType>
charType* getFileNameFromPath( charType* path )
{
if( path == NULL )
return NULL;
charType * pFileName = path;
for( charType * pCur = path; *pCur != '\0'; pCur++)
{
if( *pCur == '/' || *pCur == '\\' )
pFileName = pCur+1;
}
return pFileName;
}
调用:
wchar_t * fileName = getFileNameFromPath < wchar_t > ( filePath );
(这是 c++ 代码)
您可以通过反斜杠来转义斜杠并使用以下代码:
#include <stdio.h>
#include <string.h>
int main(void)
{
char path[] = "C:\\etc\\passwd.c"; //string with escaped slashes
char temp[256]; //result here
char *ch; //define this
ch = strtok(path, "\\"); //first split
while (ch != NULL) {
strcpy(temp, ch);//copy result
printf("%s\n", ch);
ch = strtok(NULL, "\\");//next split
}
printf("last filename: %s", temp);//result filename
return 0;
}
char * extract_file_name(char *path)
{
int len = strlen(path);
int flag=0;
printf("\nlength of %s : %d",path, len);
for(int i=len-1; i>0; i--)
{
if(path[i]=='\\' || path[i]=='//' || path[i]=='/' )
{
flag=1;
path = path+i+1;
break;
}
}
return path;
}
Input path = "C:/Users/me/Documents/somefile.txt"
Output = "somefile.txt"
@Nikolay Khilyuk提供了最好的解决方案,除此之外。
1)回到使用char *,没有使用const的好理由。
2)这段代码不可移植,并且可能在非POSIX系统上失败,在这些系统中,“/”不是文件系统分隔符,具体取决于编译器实现。对于某些Windows编译器,您可能需要测试“\”而不是“/”。您甚至可以测试系统并根据结果设置分隔符。
函数名很长但描述性强,没有问题。永远无法确定函数是否会返回文件名,只有在函数编写正确的情况下才能确定它是否可以。虽然如果有人在不是路径的字符串上使用它,显然它会失败。我可能会将其命名为basename,因为它会向许多程序员传达其目的。这只是基于我的偏见,您的名称也很好。至于此函数将处理的字符串长度以及为什么有人认为这将是一个要点?您不太可能在ANSI C编译器上处理比此函数可以处理的路径名更长的路径名。因为size_t被定义为无符号长整型,其范围为0到4,294,967,295。
我使用以下内容证明了您的函数。
#include <stdio.h>
#include <string.h>
char* getFileNameFromPath(char* path);
int main(int argc, char *argv[])
{
char *fn;
fn = getFileNameFromPath(argv[0]);
printf("%s\n", fn);
return 0;
}
char* getFileNameFromPath(char* path)
{
for(size_t i = strlen(path) - 1; i; i--)
{
if (path[i] == '/')
{
return &path[i+1];
}
}
return path;
}
工作得很好,尽管Daniel Kamil Kozar发现了一个我已经纠正的1个错误。该错误只会在格式不正确的绝对路径下显示,但是该函数仍应能够处理虚假输入。不要听取每个批评你的人的意见。有些人只是喜欢发表自己的看法,即使这些看法毫无价值。
我不喜欢strstr()的解决方案,因为如果文件名与路径中的目录名相同,则会失败,而且这种情况确实会发生,特别是在POSIX系统上,可执行文件通常没有扩展名,至少第一次是这样的,这意味着您必须进行多次测试和搜索定界符,使用strstr()更加麻烦,因为无法知道可能有多少个定界符。如果您想知道为什么一个人想要可执行文件的基本名称,请考虑busybox、egrep、fgrep等...
strrchar()的实现将会很麻烦,因为它搜索字符而不是字符串,所以我认为它不如这个解决方案那么可行或简洁。 我被Rad Lexus纠正了,这并不像我想象的那么麻烦,因为strrchar()具有返回找到的字符之后字符串的索引的副作用。
保重
i
的类型是 size_t
,根据定义它是无符号的。因此,如果字符串不包含 /
,该函数将永远不会返回。而且,你可以将 char*
传递给一个需要 const char*
的函数,而不需要在中间进行任何复制。 - Daniel Kamil Kozar.L5
:rdx
在没有检查为零的情况下被减少,这意味着如果在i
到达零之前没有/
,它将被“减少”到SIZE_MAX
(在这种特殊情况下为2^64-1)。这意味着您将使用负索引访问path
,这是UB。请参考godbolt。 - Daniel Kamil Kozar