C语言中可用的标准预定义宏__FILE__显示文件的完整路径。有办法缩短这条路吗?我的意思是

/full/path/to/file.c

我看到

to/file.c

or

file.c

当前回答

纯粹的编译时解决方案。它基于这样一个事实:字符串字面量的sizeof()返回其长度+1。

#define STRIPPATH(s)\
    (sizeof(s) > 2 && (s)[sizeof(s)-2] == '/' ? (s) + sizeof(s) - 1 : \
    sizeof(s) > 3 && (s)[sizeof(s)-3] == '/' ? (s) + sizeof(s) - 2 : \
    sizeof(s) > 4 && (s)[sizeof(s)-4] == '/' ? (s) + sizeof(s) - 3 : \
    sizeof(s) > 5 && (s)[sizeof(s)-5] == '/' ? (s) + sizeof(s) - 4 : \
    sizeof(s) > 6 && (s)[sizeof(s)-6] == '/' ? (s) + sizeof(s) - 5 : \
    sizeof(s) > 7 && (s)[sizeof(s)-7] == '/' ? (s) + sizeof(s) - 6 : \
    sizeof(s) > 8 && (s)[sizeof(s)-8] == '/' ? (s) + sizeof(s) - 7 : \
    sizeof(s) > 9 && (s)[sizeof(s)-9] == '/' ? (s) + sizeof(s) - 8 : \
    sizeof(s) > 10 && (s)[sizeof(s)-10] == '/' ? (s) + sizeof(s) - 9 : \
    sizeof(s) > 11 && (s)[sizeof(s)-11] == '/' ? (s) + sizeof(s) - 10 : (s))

#define __JUSTFILE__ STRIPPATH(__FILE__)

请随意将条件操作符级联扩展到项目中最大的合理文件名。路径长度并不重要,只要检查距离字符串的末尾足够远。

我将看看我是否可以得到一个类似的宏,没有硬编码的长度与宏递归…

其他回答

上面的答案不够好,因为它不是一个编译时const表达式 这里有一个非常简单的解决方案:

 #define FILESTEM(x)                                                        \
  std::string_view(x).substr(std::string_view(x).rfind(OS_PATH_SLASH) + 1, \
                             std::string_view(x).rfind('.') -              \
                                 std::string_view(x).rfind(OS_PATH_SLASH) - 1)

它是一个constexpr,可以在头文件中使用。

因为使用的是GCC,所以可以利用

__BASE_FILE__这个宏扩展为主输入文件的名称,以C字符串常量的形式。这是源文件 在预处理器或C编译器的命令行中指定

然后在编译时通过更改源文件表示(完整路径/相对路径/basename)来控制显示文件名的方式。

下面是一个适用于Linux(路径'/')和Windows('\'和'/'混合)的可移植函数。使用gcc, clang和vs编译。

#include <string.h>
#include <stdio.h>

const char* GetFileName(const char *path)
{
    const char *name = NULL, *tmp = NULL;
    if (path && *path) {
        name = strrchr(path, '/');
        tmp = strrchr(path, '\\');
        if (tmp) {
             return name && name > tmp ? name + 1 : tmp + 1;
        }
    }
    return name ? name + 1 : path;
}

int main() {
    const char *name = NULL, *path = NULL;

    path = __FILE__;
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path ="/tmp/device.log";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\\Downloads\\crisis.avi";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\\Downloads/nda.pdf";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:/Downloads\\word.doc";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = NULL;
    name = GetFileName(NULL);
    printf("path: %s, filename: %s\n", path, name);

    path = "";
    name = GetFileName("");
    printf("path: %s, filename: %s\n", path, name);

    return 0;
}

标准输出:

path: test.c, filename: test.c
path: /tmp/device.log, filename: device.log
path: C:\Downloads\crisis.avi, filename: crisis.avi
path: C:\Downloads/nda.pdf, filename: nda.pdf
path: C:/Downloads\word.doc, filename: word.doc
path: (null), filename: (null)
path: , filename: 

GCC 12将提供GNU C扩展宏__FILE_NAME__来获取编译文件的基名。

请参阅GCC文档,其中已经包含了这个宏:GCC -common_predefined_macros

GCC线程:Bug 42579 - [PATCH]支持获取文件basename

对@red1ynx提议的轻微变化将创建以下宏:

#define SET_THIS_FILE_NAME() \
    static const char* const THIS_FILE_NAME = \
        strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__;

在每一个。c(pp)文件中添加:

SET_THIS_FILE_NAME();

然后你可以引用THIS_FILE_NAME而不是__FILE__:

printf("%s\n", THIS_FILE_NAME);

这意味着每个.c(pp)文件执行一次构造,而不是每次引用宏时都执行。

它仅限于从.c(pp)文件中使用,不能从头文件中使用。