Makro pliku pokazuje pełną ścieżkę

Standardowe, predefiniowane makro__ FILE _ _ dostępne w języku C pokazuje pełną ścieżkę do pliku. Czy jest jakiś sposób na skrócenie ścieżki? Znaczy zamiast

/full/path/to/file.c

Widzę

to/file.c

Lub

file.c
Author: Ajay, 2011-12-13

17 answers

Try

#include <string.h>

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

Dla Windows użyj '\ \ 'zamiast'/'.

 124
Author: red1ynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-22 20:00:51

Oto wskazówka, jeśli używasz cmake. Od: http://public.kitware.com/pipermail/cmake/2013-January/053117.html

Kopiuję końcówkę więc wszystko jest na tej stronie:

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

Jeśli używasz GNU make, nie widzę powodu, dla którego nie mógłbyś rozszerzyć tego na swoje własne pliki Makefile. Na przykład możesz mieć taką linię:

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

Gdzie $(SOURCE_PREFIX) jest przedrostkiem, który chcesz usunąć.

Następnie użyj __FILENAME__ zamiast __FILE__.

 46
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-20 21:40:20

Właśnie wymyśliłem świetne rozwiązanie, które działa zarówno z plikami źródłowymi, jak i nagłówkowymi, jest bardzo wydajne i działa w czasie kompilacji na wszystkich platformach bez rozszerzeń specyficznych dla kompilatora. To rozwiązanie zachowuje również względną strukturę katalogów projektu, dzięki czemu wiesz, w którym folderze znajduje się plik i tylko w stosunku do katalogu głównego projektu.

Chodzi o to, aby uzyskać rozmiar katalogu źródłowego za pomocą narzędzia do budowania i po prostu dodać go do makra __FILE__ , usunięcie katalogu całkowicie i pokazanie tylko nazwy pliku zaczynającej się w katalogu źródłowym.

Poniższy przykład jest zaimplementowany przy użyciu CMake, ale nie ma powodu, aby nie działał z innymi narzędziami do budowania, ponieważ sztuczka jest bardzo prosta.

Na Cmakelistach.plik txt, zdefiniuj makro, które ma długość ścieżki do twojego projektu na 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}")

W kodzie źródłowym zdefiniuj makro __FILENAME__, które dodaje rozmiar ścieżki źródłowej do __FILE__ makro:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

Następnie po prostu użyj tego nowego makra zamiast makra __FILE__. Działa to, ponieważ ścieżka __FILE__ będzie zawsze zaczynała się od ścieżki do katalogu źródłowego CMake. Usuwając go z łańcucha __FILE__, preprocesor zadba o podanie poprawnej nazwy pliku i wszystko będzie względem katalogu głównego Twojego projektu CMake.

Jeśli zależy ci na wydajności, jest to równie efektywne jak użycie __FILE__, ponieważ zarówno __FILE__, jak i SOURCE_PATH_SIZE są znanymi stałymi czasu kompilacji, więc może być zoptymalizowany przez kompilator.

Jedyne miejsce, gdzie to się nie powiedzie, to jeśli używasz tego na wygenerowanych plikach i są one w folderze kompilacji spoza źródła. Następnie prawdopodobnie będziesz musiał utworzyć kolejne makro używając zmiennej CMAKE_BUILD_DIR zamiast CMAKE_SOURCE_DIR.

 10
Author: RenatoUtsch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-20 20:06:06

Czysto skompilować rozwiązanie czasu tutaj. Opiera się na fakcie, że sizeof() ciągu literalnego zwraca jego długość+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__)

Możesz rozszerzyć operator warunkowy do maksymalnej sensownej nazwy pliku w projekcie. Długość ścieżki nie ma znaczenia, o ile sprawdzasz wystarczająco daleko od końca łańcucha.

Zobaczę, czy uda mi się uzyskać podobne makro bez zakodowanej długości z rekurencją makra...

 9
Author: Seva Alekseyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-02 17:24:33

Przynajmniej dla gcc, wartość __FILE__ jest ścieżką pliku określoną w wierszu poleceń kompilatora. Jeśli skompilujesz file.c w ten sposób:

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

__FILE__ rozszerzy się do "/full/path/to/file.c". Jeśli zamiast tego zrobisz to:

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

Następnie __FILE__ rozszerzy się do tylko "file.c".

To może, ale nie musi być praktyczne.

Standard C nie wymaga takiego zachowania. Wszystko, co mówi o __FILE__, to to, że rozszerza się do " domniemanej nazwy bieżącego pliku źródłowego (znaku string literal)".

Alternatywą jest użycie dyrektywy #line. Nadpisuje bieżący numer linii i opcjonalnie nazwę pliku źródłowego. Jeśli chcesz nadpisać nazwę pliku, ale pozostawić tylko numer linii, użyj makra __LINE__.

Na przykład, możesz dodać to w pobliżu góry file.c:

#line __LINE__ "file.c"

Upewnienie się, że nazwa pliku w dyrektywie #line odpowiada rzeczywistej nazwie pliku jest pozostawione jako ćwiczenie.

Przynajmniej dla gcc, będzie to miało również wpływ nazwa pliku zgłaszana w komunikatach diagnostycznych.

 8
Author: Keith Thompson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-10 18:44:58

Nie ma na to czasu kompilacji. Oczywiście możesz to zrobić w trybie runtime używając C runtime, jak wykazały niektóre inne odpowiedzi, ale w czasie kompilacji, kiedy Pre-procesor uruchamia się, masz pecha.

 4
Author: Sean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-13 11:29:32

Ponieważ używasz GCC, Możesz wykorzystać z

__BASE_FILE__ makro to rozszerza się do nazwy głównego pliku wejściowego w postaci stałej ciągu C. Jest to plik źródłowy, który został określone w wierszu poleceń preprocesora lub kompilatora C

A następnie kontroluj sposób wyświetlania nazwy pliku, zmieniając reprezentację pliku źródłowego (pełna ścieżka/względna ścieżka/nazwa Podstawowa) w czasie kompilacji.

 4
Author: ziu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-13 11:44:21

Użyj funkcji basename () lub, jeśli używasz systemu Windows, _splitpath () .

#include <libgen.h>

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

Spróbuj również man 3 basename w powłoce.

 4
Author: Prof. Falken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-13 11:54:33

Mała różnica w tym, co @ red1ynx zaproponował, aby utworzyć następujące makro:

#define SET_THIS_FILE_NAME() \
    static const char* const THIS_FILE_NAME = \
        strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__;
W każdym z was .pliki c (pp) dodaj:
SET_THIS_FILE_NAME();

Wtedy możesz odnieść się do THIS_FILE_NAME zamiast __FILE__:

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

Oznacza to, że konstrukcja jest wykonywana raz na .plik C (pp) zamiast za każdym razem, gdy makro jest odwołane.

Jest ograniczony do stosowania tylko z .pliki C (pp) i byłyby bezużyteczne z plików nagłówkowych.

 3
Author: hmjd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-13 11:39:58

Zrobiłem makro __FILENAME__, które pozwala uniknąć cięcia pełnej ścieżki za każdym razem. Problem polega na trzymaniu wynikowej nazwy pliku w zmiennej lokalnej cpp.

Można to łatwo zrobić definiując statyczną zmienną globalną w .h Plik. Definicja ta daje oddzielne i niezależne zmienne w każdym .plik cpp zawierający .h . Aby być odpornym na wielowątkowość warto uczynić zmienną (- ami) również thread local (TLS).

Jedna zmienna przechowuje nazwę pliku (skurczył). Inny posiada wartość non-cut, którą __FILE__ dał. Plik h:

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

Samo makro wywołuje metodę z całą logiką:

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

I funkcja jest zaimplementowana w ten sposób:

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 () może być zaimplementowane na różne sposoby, ale szybkie i proste wydaje się być za pomocą 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;
}
 3
Author: Kunis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-18 14:49:46

Po prostu mam nadzieję poprawić trochę makro pliku:

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

To łapie / i\, jak prosił Czarek Tomczak, a to świetnie sprawdza się w moim mieszanym środowisku.

 2
Author: alexander golks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-06 13:49:42

Jeśli używasz CMAKE z kompilatorem GNU to global define działa dobrze:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")
 2
Author: GerneralAmerica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-28 11:09:50

Try

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

Po poleceniach include w pliku źródłowym i dodaj

#pragma pop_macro("__FILE__")

Na końcu pliku źródłowego.

 1
Author: Alain Totouom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-12 09:42:57

W vs, gdy z /FC, Plik jest równy pełnej ścieżce, bez /FC Plik jest równy nazwie pliku. ref tutaj

 1
Author: user7382523,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-06 05:33:05

Oto rozwiązanie, które działa w środowiskach, które nie mają biblioteki string (jądro Linuksa, systemy wbudowane itp.):

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

Teraz po prostu użyj FILENAME zamiast __FILENAME__. Tak, to nadal jest runtime, ale działa.

 0
Author: beavis9k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-17 19:41:18

Oto przenośna funkcja, która działa zarówno dla Linuksa (ścieżka'/'), jak i Dla Windows (połączenie '\' i '/').
kompiluje z gcc, clang i 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;
}

Standardowe wyjście:

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: 
 0
Author: Sasha Zezulinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 13:27:54

Oto rozwiązanie, które wykorzystuje obliczenia czasu kompilacji:

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__);
 0
Author: Dmitry Gordon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 16:17:17