__FILE__宏显示完整路径


164

__FILE__C中可用的标准预定义宏显示了文件的完整路径。有什么办法可以缩短这条路吗?我的意思不是

/full/path/to/file.c

我懂了

to/file.c

要么

file.c

18
找到仅限预处理程序的解决方案真的很棒。恐怕基于字符串操作的建议将在运行时执行。
cdleonard

9
由于您使用的是gcc,我认为您可以__FILE__通过更改命令行中传递的文件名来更改包含的内容。因此gcc /full/path/to/file.c,请尝试cd /full/path/to; gcc file.c; cd -;。当然,如果您依赖于gcc的当前目录作为包含路径或输出文件位置,那么还有更多的事情要做。编辑:gcc文档建议它是完整路径,而不是输入文件名参数,但这不是我在Cygwin上看到的gcc 4.5.3的内容。因此,您不妨在Linux上尝试一下看看。
史蒂夫·杰索普

4
GCC 4.5.1(专门为arm-none-eabi构建)在其命令行上使用文件名的确切文本。就我而言,这是IDE的错误,即使用完全限定的所有文件名来调用GCC,而不是将当前目录放置在明智的位置(可能是项目文件的位置?)或可配置的位置,并使用从此处开始的相对路径。我怀疑很多IDE都会这样做(特别是在Windows上),这是出于与解释“当前”目录真正用于GUI应用程序的位置有关的某种不适感。
RBerteig 2012年

3
@SteveJessop-希望您阅读此评论。我遇到这样的情况,我希望将其__FILE__打印为,../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c并且我想知道gcc在哪里编译了该文件,使得该文件相对地位于如图所示的位置。
灿金

1
这个问题不是链接的问题的欺骗。首先,链接的内容是关于C ++的,因此答案就探究到C ++宏esoterica。第二,OP的问题中没有什么可以强制执行宏观解决方案。它仅郑重地指出一个问题,并提出一个开放性问题。
Falken教授2015年

Answers:


166

尝试

#include <string.h>

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

对于Windows,使用“ \\”而不是“ /”。


13
/在Windows中是有效的路径分隔符。
汉斯·帕桑

11
/是传递给CreateFile()等的文件名中的有效路径分隔符。但是,这并不总是意味着您可以/在Windows中的任何位置使用,因为有一种传统(从CPM继承),它/用作命令提示符下的引数。但一个优质工具会小心在分割文件名斜杠和反斜杠字符以避免对那些管理使用人的任何问题/
RBerteig 2012年

5
@AmigableClarkKant,不,您可以在同一文件名中混合使用两个分隔符。
RBerteig 2012年

2
如果您的平台支持它char* fileName = basename(__FILE__); ,那么Linux和OS X肯定存在它,但是对Windows却一无所知。
JeremyP

14
可以将其缩短为strrchr("/" __FILE__, '/') + 1。通过在“ /”前添加__FILE__,可以确保strrchr找到某些内容,因此不再需要条件?:。
ɲeuroburɳ

54

如果您使用cmake,这里有个提示。来自:http : //public.kitware.com/pipermail/cmake/2013-January/053117.html

我正在复制提示,因此所有内容都在此页面上:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst
  ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")

如果您使用的是GNU make,我认为没有理由不能将其扩展到您自己的makefile中。例如,您可能会有这样的一行:

CXX_FLAGS+=-D__FILENAME__='\"$(subst $(SOURCE_PREFIX)/,,$(abspath $<))\"'"

$(SOURCE_PREFIX)您要删除的前缀在哪里。

然后使用__FILENAME__代替__FILE__


11
恐怕这不适用于头文件中引用的FILE
Baiyan Huang

2
同意@BaiyanHuang,但不确定注释是否清晰。 __FILE__不是简单的预处理器符号,它更改为当前文件通常用于发出当前文件的名称(标头或源模块)。这__FILENAME__将只有最外面的来源
nhed 2014年

3
该答案的解决方案不可移植,因为它使用的是Bourne外壳转义符。最好使用CMake以干净,便携式的方式实现它。请参阅此处的define_file_basename_for_sources宏答案
Colin D Bennett

3
GNU Make的变体是CFLAGS + = -D__FILENAME __ = \“ $(notdir $ <)\”
ctuffli

4
@firegurafiku在嵌入式平台上,代码大小通常更多地是约束而不是速度。如果每个文件中都有调试语句,则可以使用全路径字符串迅速增加文件大小。我现在正在使用这样的平台,并且__FILE__到处引用都超出了代码空间。
mrtumnus

26

我刚刚想到了一个很好的解决方案,它可以与源文件和头文件一起使用,非常有效,并且可以在所有平台上的编译时工作,而无需编译器特定的扩展。此解决方案还保留了项目的相对目录结构,因此您知道文件位于哪个文件夹中,并且仅相对于项目的根目录。

想法是使用构建工具获取源目录的大小,然后将其添加到__FILE__宏中,完全删除目录,仅显示从源目录开始的文件名。

以下示例是使用CMake实现的,但没有理由不能与其他任何构建工具一起使用,因为技巧很简单。

在CMakeLists.txt文件上,定义一个宏,该宏的长度与您在CMake上的项目的路径相同:

# The additional / is important to remove the last character from the path.
# Note that it does not matter if the OS uses / or \, because we are only
# saving the path size.
string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")

在源代码上,定义一个__FILENAME__仅将源路径大小添加到__FILE__宏的宏:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

然后,只需使用此新宏而不是__FILE__宏即可。这是可行的,因为__FILE__路径将始终以CMake源目录的路径开头。通过从__FILE__字符串中删除它,预处理器将负责指定正确的文件名,并且它们都相对于CMake项目的根目录。

如果您在乎性能,那么这和使用一样有效__FILE__,因为__FILE__SOURCE_PATH_SIZE都是已知的编译时间常数,因此编译器可以对其进行优化。

唯一失败的地方是如果您在生成的文件上使用此文件,并且它们在源构建文件夹中。然后,您可能必须使用CMAKE_BUILD_DIR变量而不是来创建另一个宏CMAKE_SOURCE_DIR


4
起初我不明白这一点。我编写了示例,然后将它们针对gcc和clang进行了运行,并且它们可以工作。我还尝试仅附加各种文字数字值,并且其行为符合我的预期。然后它终于降临在我身上。__FILE__是指向字节数组的指针。因此,添加数字文字只是指针加法。非常聪明的@RenatoUtsch
Daniel

仅当源文件在cmake列表目录下时,该文件才有效。如果源文件在外部,则它将中断,并且可能会在文字字符串之外进行访问。所以要小心。
安德里(Andry)

实际上,它不会减少代码大小。我猜整个路径仍然编译成二进制文件,只是指针被修改了。
Tarion

无论是__FILE__宏观和SOURCE_PATH_SIZE宏是在编译时已知常数。我希望现代的优化编译器能够检测到未使用该字符串的一部分,并将其从二进制文件中删除。无论如何,我认为这几个字节不会对二进制大小产生重大影响,因此我并不在乎。
RenatoUtsch

@RenatoUtsch我正在处理的项目进行了更改,该更改仅指定了文件名,但是也具有将C文件名也赋予标头的缺点。进行更改是为了获得可复制的版本。因此,对于带有-O2的gcc,字符串是否确实可以进行优化并且构建可复制?
Paul Stelian

18

这里纯编译时间解决方案。它基于以下事实: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__)

随意将条件运算符级联扩展为项目中的最大明智文件名。路径长度无关紧要,只要您从字符串末尾检查足够远即可。

我看看是否可以通过宏递归获得一个没有硬编码长度的类似宏...


3
很酷的答案。您至少-O1需要使用它才能在编译时使用。
Digital Trauma'3

1
这不是倒退吗?您要查找最后一次出现的“ /”,这意味着您应该首先sizeof(s) > 2检查一下。另外,对于我来说,这在-Os上不起作用。全路径字符串存在于输出二进制文件中。
mrtumnus

15

至少对于gcc而言,的值__FILE__编译器命令行上指定的文件路径。如果您这样编译file.c

gcc -c /full/path/to/file.c

__FILE__将扩大到"/full/path/to/file.c"。如果您改为这样做:

cd /full/path/to
gcc -c file.c

然后__FILE__会扩展到正义"file.c"

这可能可行,也可能不可行。

C标准不需要这种行为。它__FILE__只是说它扩展为“当前源文件的假定名称(字符串文字)”。

另一种方法是使用#line指令。它会覆盖当前行号以及源文件名(可选)。如果要覆盖文件名,但不保留行号,请使用__LINE__宏。

例如,您可以将其添加到附近file.c

#line __LINE__ "file.c"

唯一的问题是它将指定的行号分配给下一行,并且第一个参数#line必须是数字序列,因此您不能执行类似的操作

#line (__LINE__-1) "file.c"  // This is invalid

#line练习时要确保指令中的文件名与文件的实际名称匹配。

至少对于gcc,这还将影响诊断消息中报告的文件名。


1
Keith Thompson,这是一个很好的解决方案,谢谢。唯一的问题是,该宏似乎将__LINE__值减一。因此__LINE__xgest中写成行来评估x-1。至少使用gcc 5.4。
elklepo

1
@Klepak:你是对的,这是标准行为。指令“使实现的行为类似于以下源代码行序列始于具有由数字序列指定的行号的源代码行”。而且它必须是数字序列,所以您不能使用#line __LINE__-1, "file.c"。我将更新我的答案。
基思·汤普森

1
对于其他编译器(如Clang,MSVC或Intel C编译器),效果如何?
富兰克林于

7

参加聚会有点晚了,但是对于海湾合作委员会,请看看以下-ffile-prefix-map=old=new选项:

编译位于old目录中的文件时,请在编译结果中记录对它们的所有引用,就像这些文件位于new目录中一样。指定此选项等效于指定所有单个-f*-prefix-map选项。这可用于制作与位置无关的可复制构建。另请参阅 -fmacro-prefix-map-fdebug-prefix-map

因此,对于我的Jenkins构建,我将添加-ffile-prefix-map=${WORKSPACE}/=/,另一个将删除本地dev软件包的安装前缀。

注意不幸的是,该-ffile-prefix-map选项仅在GCC 8及更高版本中可用-fmacro-prefix-map我认为正是这一点__FILE__。例如,对于GCC 5,我们只有-fdebug-prefix-map(不会)影响__FILE__


1
期权-ffile-prefix-map确实意味着-fdebug-prefix-map-fmacro-prefix-map期权。另请参见reproducible-builds.org/docs/build-path上的参考信息跟踪-fmacro-prefix-map-ffile-prefix-mapgcc.gnu.org/bugzilla/show_bug.cgi?id=70268
-Lekensteyn,

6
  • C ++ 11
  • msvc2015u3,gcc5.4,clang3.8.0

    template <typename T, size_t S>
    inline constexpr size_t get_file_name_offset(const T (& str)[S], size_t i = S - 1)
    {
        return (str[i] == '/' || str[i] == '\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <typename T>
    inline constexpr size_t get_file_name_offset(T (& str)[1])
    {
        return 0;
    }

    '

    int main()
    {
         printf("%s\n", &__FILE__[get_file_name_offset(__FILE__)]);
    }

在以下情况下,代码将生成编译时间偏移:

  • gcc:至少gcc6.1 + -O1
  • msvc:将结果放入constexpr变量中:

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", file);
  • clang:坚持不编译时间评估

有一个技巧可以强制所有3个编译器即使在禁用优化的调试配置中也可以进行编译时间评估:

    namespace utility {

        template <typename T, T v>
        struct const_expr_value
        {
            static constexpr const T value = v;
        };

    }

    #define UTILITY_CONST_EXPR_VALUE(exp) ::utility::const_expr_value<decltype(exp), exp>::value

    int main()
    {
         printf("%s\n", &__FILE__[UTILITY_CONST_EXPR_VALUE(get_file_name_offset(__FILE__))]);
    }

https://godbolt.org/z/u6s8j3


伙计,那很漂亮,很有效,谢谢!不知道为什么这个答案被低估了。
罗马

5

在VC中,当使用时/FC__FILE__将扩展为完整路径,而无需使用该/FC选项__FILE__将扩展文件名。参考:这里


4

没有编译时的方法可以做到这一点。显然,您可以使用C运行时在运行时执行此操作,如其他一些答案所示,但是在编译时,如果前处理器启动,您很不走运。


1
strrchr回答可以振振有词地在编译时不被计算预处理的过程中虽然依然。我不知道gcc是否确实做到了,我还没有检查过,但是我很确定它strlen在编译时确实可以计算字符串文字。
史蒂夫·杰索普

@Steve-也许,但这很大程度上取决于编译器的特定行为。
肖恩

我认为这不是很大的依赖性,因为我非常怀疑此代码对性能至关重要。如果是这样,请将其移出循环。在这很重要的情况下,因为您绝对需要只包含基名称的字符串文字,则可以在构建时通过在源上运行一些脚本来计算正确的字符串。
史蒂夫·杰索普

5
它可能不是性能关键,但可以很容易地视为隐私关键。没有真正好的理由以冻结在已发布EXE文件中的字符串形式显示我的每个客户的组织惯例。更糟糕的是,对于代表客户创建的应用程序,这些字符串可能会显示我的客户可能不希望看到的东西,例如不是自己产品的作者。由于__FILE__是由隐式调用的assert(),因此无需任何其他公开操作即可发生此泄漏。
RBerteig 2012年

@RBerteig __FILE__本身的基本名称也可能会显示客户可能不希望__FILE__看到的内容,因此,无论在任何地方使用它-包含完整的绝对路径名还是仅包含基本名称-都具有您指出的相同问题。在这种情况下,将需要检查所有输出,并应引入特殊的API以输出给客户。其余的输出应转储到/ dev / NULL或stdout并关闭stderr。:-)
tchen

4

使用basename()函数,或者,如果使用Windows,则使用_splitpath()

#include <libgen.h>

#define PRINTFILE() { char buf[] = __FILE__; printf("Filename:  %s\n", basename(buf)); }

也可以man 3 basename在外壳中尝试。


2
@mahmood: char file_copy[] = __FILE__; const char *filename = basename(__FILE__);。复制的原因是基本名称可以修改输入字符串。您还必须注意,直到basename再次调用结果指针才是好的。这意味着它不是线程安全的。
史蒂夫·杰索普

@SteveJessop,嗯,我忘了。真正。
法尔肯教授

1
@Amigable:公平地说,我怀疑basename实际上不会修改由产生的输入字符串__FILE__,因为输入字符串的结尾没有a /,因此不需要修改。因此,您可能会摆脱它,但我认为有人第一次看到basename,他们应该看到所有限制。
史蒂夫·杰索普

@SteveJessop basename()的BSd手册页中提到,basename()的旧版本采用const char *且不修改字符串。linux手册页没有提到const,但是提到它可以返回一部分参数字符串。因此,最好保守地对待basename()。
法尔肯教授

@SteveJessop,嘿嘿,我只是现在在您的评论仔细寻找后,四年后,REALIZE/在串手段基本名称的末尾可能有一个很好的理由来修改其参数。
Falken教授2015年

4

如果CMAKE与GNU编译器一起使用,则此global定义可以正常工作:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")

4

多年来,我一直使用与@Patrick的答案相同的解决方案。

当完整路径包含符号链接时,存在一个小问题。

更好的解决方案。

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")

为什么要使用这个?

  • -Wno-builtin-macro-redefined使编译器警告静音以重新定义__FILE__宏。

    对于那些不支持此功能的编译器,请参考下面的“ 健壮方法”

  • 从文件路径中删除项目路径是您的真正要求。您不会希望浪费时间来查找header.h文件src/foo/header.hsrc/bar/header.h

  • 我们应该删除配置文件中的__FILE__cmake

    大多数现有代码中都使用此宏。只需重新定义它即可让您自由。

    像编译器这样的gcc宏可以从命令行参数中预定义。完整路径将写入makefile由生成的cmake

  • 必须输入硬编码CMAKE_*_FLAGS

    有一些命令可以在较新的版本中添加编译器选项或定义,例如add_definitions()add_compile_definitions()。这些命令将解析make函数,就像subst应用于源文件之前一样。那不是我们想要的。

鲁棒的方法-Wno-builtin-macro-redefined

include(CheckCCompilerFlag)
check_c_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_C_WNO_BUILTIN_MACRO_REDEFINED)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-Wno-builtin-macro-redefined SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
if (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined")
endif (SUPPORT_CXX_WNO_BUILTIN_MACRO_REDEFINED)

请记住从set(*_FLAGS ... -D__FILE__=...)行中删除此编译器选项。


这不适用于包含文件中的内容。
chqrlie

你可以发布一些代码吗?一个常见的情况是在局部范围内设置变量,然后在另一个范围内使用它。
Levi.G

例如,如果您使用__FILE__定义在头文件中定义的内联函数中生成诊断,则运行时诊断将报告传递给编译器的文件名而不是包含文件的名,而行号将请参阅包含文件。
chqrlie

是的,它的设计意图是,最常见的用法是#define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args)。使用LOG()宏时,您实际上并不想看到log.h消息。毕竟,__FILE__宏是在每个C / Cpp文件(编译单元)中而不是包含的文件中扩展的。
Levi.G

3

@ 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)文件使用,并且无法从头文件使用。


3

我做了一个__FILENAME__避免每次剪切完整路径的宏。问题是将生成的文件名保存在cpp-local变量中。

通过在.h文件中定义静态全局变量,可以轻松完成此操作。此定义在每个包含.h的.cpp文件中提供独立的变量。为了成为多线程的证明,值得使变量也局部线程化(TLS)。

一个变量存储文件名(缩小)。另一个持有所给的非割__FILE__价。h文件:

static __declspec( thread ) const char* fileAndThreadLocal_strFilePath = NULL;
static __declspec( thread ) const char* fileAndThreadLocal_strFileName = NULL;

宏本身使用所有逻辑调用方法:

#define __FILENAME__ \
    GetSourceFileName(__FILE__, fileAndThreadLocal_strFilePath, fileAndThreadLocal_strFileName)

该功能是通过以下方式实现的:

const char* GetSourceFileName(const char* strFilePath, 
                              const char*& rstrFilePathHolder, 
                              const char*& rstrFileNameHolder)
{
    if(strFilePath != rstrFilePathHolder)
    {
        // 
        // This if works in 2 cases: 
        // - when first time called in the cpp (ordinary case) or
        // - when the macro __FILENAME__ is used in both h and cpp files 
        //   and so the method is consequentially called 
        //     once with strFilePath == "UserPath/HeaderFileThatUsesMyMACRO.h" and 
        //     once with strFilePath == "UserPath/CPPFileThatUsesMyMACRO.cpp"
        //
        rstrFileNameHolder = removePath(strFilePath);
        rstrFilePathHolder = strFilePath;
    }
    return rstrFileNameHolder;
}

removePath()可以通过不同的方式实现,但是快速和简单似乎是通过strrchr实现的:

const char* removePath(const char* path)
{
    const char* pDelimeter = strrchr (path, '\\');
    if (pDelimeter)
        path = pDelimeter+1;

    pDelimeter = strrchr (path, '/');
    if (pDelimeter)
        path = pDelimeter+1;

    return path;
}


2

只是希望可以改善FILE宏:

#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

就像Czarek Tomczak所要求的那样,这捕获了/和\,这在我的混合环境中非常有用。


6
FILE如果包含,则定义一个名为的宏是一个非常糟糕的主意<stdio.h>
基思·汤普森

很高兴知道。我只是想向Czarek显示我的\\ /解决方案,所以我不介意命名方案。
亚历山大·高克(Alexander Golks),2013年

2

尝试

#pragma push_macro("__FILE__")
#define __FILE__ "foobar.c"

在源文件中的include语句之后并添加

#pragma pop_macro("__FILE__")

在源文件末尾。


2
push_macro并且pop_macro是非标准的。(gcc支持它们以与Microsoft Windows编译器兼容。)在任何情况下,没有必要推送和弹出__FILE__; 的定义。恢复的值在源文件结束后将不再使用。更改的更干净的方法__FILE__#line __LINE__ "foobar.c"
Keith Thompson

1
这会在gcc的预处理器中导致内部错误。gcc.gnu.org/bugzilla/show_bug.cgi?id=69665
Keith Thompson

1

这是一个可移植的函数,适用于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: 

1

这是使用编译时计算的解决方案:

constexpr auto* getFileName(const char* const path)
{
    const auto* startPosition = path;
    for (const auto* currentCharacter = path;*currentCharacter != '\0'; ++currentCharacter)
    {
        if (*currentCharacter == '\\' || *currentCharacter == '/')
        {
            startPosition = currentCharacter;
        }
    }

    if (startPosition != path)
    {
        ++startPosition;
    }

    return startPosition;
}

std::cout << getFileName(__FILE__);

1

如果您最终在此页面上找到了一种方法,该方法可以从要运送的二进制文件中删除指向丑陋构建位置的绝对源路径,则下面的内容可能会满足您的需求。

尽管由于使用了CMake,因此并不能完全给出作者所希望表达的答案,但它却非常接近。遗憾的是没有人提起过,因为这可以节省我很多时间。

OPTION(CMAKE_USE_RELATIVE_PATHS "If true, cmake will use relative paths" ON)

设置上面的变量ON将生成格式为以下的构建命令:

cd /ugly/absolute/path/to/project/build/src && 
    gcc <.. other flags ..> -c ../../src/path/to/source.c

结果,__FILE__宏将解析为../../src/path/to/source.c

CMake文档

但是请注意文档页面上的警告:

使用相对路径(可能不起作用!)。

它不能保证在所有情况下都能正常工作,但可以在我的系统中工作-CMake 3.13 + gcc 4.5


CMake 3.4+文档指出:“此变量无效。在CMake 3.4中,该变量在早期版本中已部分实现。” 如果它适用于3.13,则还有另一个原因。
mike.dld19年

0

由于您使用GCC,你可以利用

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

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


6
没有区别。我用过__FILE____BASE_FILE__但是它们都显示了完整的文件路径
mahmood 2011年

您如何调用编译器?
ziu

2
然后我敢打赌,SCONS会像这样调用gcc gcc /absolute/path/to/file.c。如果您找到更改此行为的方法(在SO上打开另一个问题,大声笑?),则无需在运行时修改字符串
ziu

17
这个答案是100%错误的。__BASE_FILE__(如文档所说,尽管不清楚)会生成在命令行上指定的文件名,例如,test.c或者/tmp/test.c取决于您调用编译器的方式。__FILE__除了您位于头文件中之外,这与之完全相同,在这种情况下,将__FILE__产生当前文件的名称(例如foo.h),而__BASE_FILE__继续产生test.c
Quuxplusone 2015年

0

以下是适用于没有字符串库(Linux内核,嵌入式系统等)的环境的解决方案:

#define FILENAME ({ \
    const char* filename_start = __FILE__; \
    const char* filename = filename_start; \
    while(*filename != '\0') \
        filename++; \
    while((filename != filename_start) && (*(filename - 1) != '/')) \
        filename--; \
    filename; })

现在只需使用FILENAME代替__FILENAME__。是的,它仍然是运行时,但可以运行。


可能要同时检查'/'和'\',具体取决于平台。
Technophile

0
#include <algorithm>
#include <string>
using namespace std;
string f( __FILE__ );
f = string( (find(f.rbegin(), f.rend(), '/')+1).base() + 1, f.end() );

// searches for the '/' from the back, transfers the reverse iterator 
// into a forward iterator and constructs a new sting with both

0

Windows和* nix的简短有效答案:

#define __FILENAME__ std::max<const char*>(__FILE__,\
    std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))

为什么需要std::max<const char*>而不是仅仅std::max
chqrlie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.