C中可用的标准预定义MACRO __FILE__显示文件的完整路径。有没有办法缩短路径?我的意思是代替
/full/path/to/file.c
我看到了
to/file.c
或
file.c
答案 0 :(得分:143)
尝试
#include <string.h>
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
对于Windows,请使用'\\'而不是'/'。
答案 1 :(得分:50)
如果你正在使用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__
。
答案 2 :(得分:11)
至少对于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__
的说法是它扩展为&#34;当前源文件的假定名称(字符串文字)&#34;。
另一种方法是使用#line
指令。它会覆盖当前行号,也可以覆盖源文件名。如果要覆盖文件名但仅保留行号,请使用__LINE__
宏。
例如,您可以将其添加到file.c
顶部附近:
#line __LINE__ "file.c"
唯一的问题是它将指定的行号分配给跟随行,而#line
的第一个参数必须是数字序列所以你不能做像
#line (__LINE__-1) "file.c" // This is invalid
确保#line
指令中的文件名与文件的实际名称相匹配,作为练习。
至少对于gcc,这也会影响诊断消息中报告的文件名。
答案 3 :(得分:11)
这里纯粹编译时间解决方案。它基于以下事实:字符串文字的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__)
随意将条件运算符级联扩展为项目中的最大敏感文件名。路径长度并不重要,只要您从字符串末尾检查足够远。
我会看看是否可以获得一个类似的宏,没有带有宏递归的硬编码长度...
答案 4 :(得分:4)
没有编译时间方法来做到这一点。显然,您可以在运行时使用C运行时执行此操作,正如其他一些答案所示,但在编译时,当预处理器启动时,您运气不佳。
答案 5 :(得分:4)
在vs中,当使用/ FC时, FILE 等于完整路径,没有/ FC FILE 等于文件名。 ref here
答案 6 :(得分:4)
如果您使用CMAKE
与GNU编译器,global
定义可以正常工作:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")
答案 7 :(得分:4)
使用basename()功能,或者,如果您使用的话,请使用_splitpath()。
#include <libgen.h>
#define PRINTFILE() { char buf[] = __FILE__; printf("Filename: %s\n", basename(buf)); }
还要在shell中尝试man 3 basename
。
答案 8 :(得分: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)文件,并且无法从头文件中使用。
答案 9 :(得分:3)
我做了一个宏__FILENAME__
,每次都避免削减完整路径。问题是将结果文件名保存在cpp-local变量中。
可以通过在 .h 文件中定义静态全局变量轻松完成。 此定义在每个 .cpp 文件中提供单独且独立的变量,其中包含 .h 。 为了成为多线程证明,值得使变量也是本地线程(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;
}
答案 10 :(得分:3)
msvc2015u3,gcc5.4,clang3.8.0
wget -P ~/.local/lib https://bootstrap.pypa.io/get-pip.py
python2.7 ~/.local/lib/get-pip.py --user
#if using bash
printf "\nPATH=\$PATH:~/.local/bin/" >> ~/.bashrc
source ~/.bashrc
'
template <size_t S>
inline constexpr size_t get_file_name_offset(const char (& 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 <size_t S>
inline constexpr size_t get_file_name_offset(const wchar_t (& str)[S], size_t i = S - 1)
{
return (str[i] == L'/' || str[i] == L'\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
}
template <size_t S>
inline constexpr size_t get_file_name_offset(const char16_t (& str)[S], size_t i = S - 1)
{
return (str[i] == u'/' || str[i] == u'\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
}
template <size_t S>
inline constexpr size_t get_file_name_offset(const char32_t (& str)[S], size_t i = S - 1)
{
return (str[i] == U'/' || str[i] == U'\\') ? 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__)]);
}
:至少gcc6.1 + gcc
-O1
:将结果放入constexpr变量:
msvc
constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
printf("%s\n", file);
:坚持不进行编译时间评估
有一个技巧可以强制所有3个编译器即使在禁用优化的调试配置中也可以进行编译时间评估:
clang
答案 11 :(得分:3)
聚会晚了一点,但是对于 GCC ,请查看-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
也是{em>我认为执行{{ 1}}部分。例如,对于GCC 5,我们只有__FILE__
,它不会(似乎)影响-fdebug-prefix-map
。
答案 12 :(得分:2)
GCC 12 将提供 GNU C 扩展宏 __FILE_NAME__
以获取编译文件的基本名称。
请参阅已包含此宏的 GCC 文档:gcc-common_predefined_macros
GCC 线程:Bug 42579 - [PATCH] support for obtaining file basename
答案 13 :(得分:2)
希望稍微改进一下FILE宏:
#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
这就像Czarek Tomczak所要求的那样捕获/和\,这在我的混合环境中效果很好。
答案 14 :(得分:2)
尝试
#pragma push_macro("__FILE__")
#define __FILE__ "foobar.c"
在源文件中的include语句之后添加
#pragma pop_macro("__FILE__")
在源文件的末尾。
答案 15 :(得分:2)
如果您最终在此页面上找到了一种方法,该方法可以从要运送的二进制文件中删除指向丑陋构建位置的绝对源路径,则下面的内容可能会满足您的需求。
尽管这并不能完全给出作者表示希望的答案,因为它假定使用了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 3.13 + gcc 4.5
答案 16 :(得分: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:
答案 17 :(得分:0)
以下是使用编译时计算的解决方案:
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__);
答案 18 :(得分:0)
这是一个适用于没有字符串库(Linux内核,嵌入式系统等)的环境的解决方案:
duplicate_rows = df.duplicated(keep = False)
print(duplicate_rows)
#count the number of duplicates (i.e. count the number of 'True' values in
#the duplicate_rows boolean series.
number_of_duplicates = sum(duplicate_rows)
print("Number of duplicate rows:")
print(number_of_duplicates)
#Output
#The duplicate_rows pandas series from df.duplicated(keep = False)
0 True
1 True
2 False
3 True
4 True
5 False
6 True
dtype: bool
#The number of rows from sum(df.duplicated(keep = False))
Number of duplicate rows:
5
现在只需使用#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
。是的,它仍然是一个运行时的东西,但它的工作原理。
答案 19 :(得分: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
答案 20 :(得分:0)
多年来,我一直在@Patrick的answer使用相同的解决方案。
当完整路径包含符号链接时,这是一个小问题。
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__
宏。
对于那些不支持此功能的编译器,请参考下面的 Robust方法。
从文件路径中剥离项目路径是您的 real 要求。您不会想浪费时间来找出header.h
或src/foo/header.h
是src/bar/header.h
文件的位置。
我们应该删除__FILE__
配置文件中的cmake
宏。
大多数现有代码中都使用此宏。只需重新定义它即可让您自由。
像gcc
这样的编译器会从命令行参数中预定义此宏。完整路径写在makefile
生成的cmake
s中。
CMAKE_*_FLAGS
中的硬密码是必需的。
有些命令可以添加编译器选项或定义的最新版本,例如add_definitions()
和add_compile_definitions()
。这些命令将在应用于源文件之前解析诸如subst
之类的make函数。那不是我们想要的。
-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__=...)
行中删除此编译器选项。
答案 21 :(得分:0)
适用于Windows和* nix的简短有效答案:
#define __FILENAME__ std::max<const char*>(__FILE__,\
std::max(strrchr(__FILE__, '\\')+1, strrchr(__FILE__, '/')+1))
答案 22 :(得分:0)
最近的Clang编译器有一个__FILE_NAME__
宏(请参见here)。
答案 23 :(得分:0)
red1ynx 答案的经过调整的、更加“臃肿”的版本:
#define __FILENAME__ \
(strchr(__FILE__, '\\') \
? ((strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)) \
: ((strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)))
如果我们找到反斜杠,我们就拆分反斜杠。否则,在正斜杠上拆分。很简单。
几乎任何替代方案都会更干净(在我看来,C++ constexpr 确实是这里的黄金标准)。但是,如果您使用的编译器 __BASE_FILE__
不可用,这可能会有所帮助。
答案 24 :(得分:0)
我认为这比使用 strrchr 函数更好。
strfnchr 将搜索最后一个 delemeter '/' 并从 __FILE__
获取文件名
并且您可以使用 __FILE__NAME__
代替 __FILE__
来获取没有完整文件路径的文件名。
strrchr 解决方案每次使用搜索文件名两次。但此代码只是 1 次搜索。
即使 __FILE__
中没有分隔符“/”,它也能有效工作。
您可以根据需要将其替换为 \ 来使用它。
使用下面的strrchr源代码改进了strfnchr的源代码。我认为它会比 strrchr 更有效地工作。
https://code.woboq.org/userspace/glibc/string/strrchr.c.html
inline const char* strfnchr(const char* s, int c) {
const char* found = s;
while (*(s++)) {
if (*s == c)
found = s;
}
if (found != s)
return found + 1;
return s;
}
#define __FILE_NAME__ strfnchr(__FILE__, '/')
答案 25 :(得分:0)
由于您使用的是GCC,因此可以{/ 3}}
__BASE_FILE__
此宏以C字符串常量的形式扩展为主输入文件的名称。这是源文件 在预处理器或C编译器的命令行上指定
然后通过在编译时更改源文件表示(完整路径/相对路径/ basename)来控制如何显示文件名。
答案 26 :(得分:0)
此解决方案基于@RenatoUtsch 答案:
CMake 列表:
string(LENGTH "${PROJECT_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")
add_definitions("-DSOURCE_PATH=\"${PROJECT_SOURCE_DIR}\"")
C/C++ 头文件
#define __FILENAME__ strstr(__FILE__, SOURCE_PATH) + SOURCE_PATH_SIZE
答案 27 :(得分:0)