2010-12-13 11 views

Odpowiedz

126

Aby wyodrębnić nazwę pliku bez rozszerzenia, należy użyć boost :: :: plików ścieżki :: macierzystych zamiast brzydki std :: string :: find_last_of (”.„)

boost::filesystem::path p("c:/dir/dir/file.ext"); 
std::cout << "filename and extension : " << p.filename() << std::endl; // file.ext 
std::cout << "filename only   : " << p.stem() << std::endl;  // file 
+0

Uzgodnione. Odpowiedzi na pytanie w sposób zwięzły. – AndyUK

+7

W rzeczywistości p.nazwa_pliku() jest ścieżką typu i będzie otoczona cudzysłowami, gdy zostanie niejawnie przekonwertowana, dzięki czemu otrzymasz: nazwa pliku i rozszerzenie: "plik.ekst" Możesz chcieć p.filename(). zamiast tego ciąg(). –

+1

W C++ 14/C++ 17 można użyć 'std :: experimental :: filesystem' lub' std :: filesystem'. Zobacz post z Yuchen Zhong poniżej. –

3

nie kod, ale tutaj jest pomysł:

  1. Odczyt std::string ze strumienia wejściowego (std::ifstream), każda instancja odczytu będzie pełna ścieżka
  2. Wykonaj find_last_of na ciąg dla osób \
  3. Extract podciąg z tej pozycji do końca, ten będzie teraz daje nazwę pliku
  4. zrobić find_last_of dla ., a po obu stronach będzie podciąg podaj nazwę + rozszerzenie.
+0

+1 za udzielenie pomocy bez podawania kodu. – razlebe

+0

I -1 za bycie nieprzenośnym :) – Kos

+0

Dlaczego głosowanie w dół? Jeśli coś jest nie w porządku z tym, co powiedziałem, daj mi znać, a naprawię! – Nim

19

Jeśli chcesz mieć bezpieczny sposób (tj. Przenosić się między platformami i nie umieszczać założeń na ścieżce), polecam użyć boost::filesystem.

To będzie wyglądać jakoś tak:

boost::filesystem::path my_path(filename); 

Następnie można wyodrębnić różne dane z tej drogi. Here's the documentation of path object.


BTW: Należy również pamiętać, że w celu korzystania ścieżkę jak

c:\foto\foto2003\shadow.gif 

trzeba uciec \ w ciąg dosłownym:

const char* filename = "c:\\foto\\foto2003\\shadow.gif"; 

Albo użyć / zamiast:

const char* filename = "c:/foto/foto2003/shadow.gif"; 

Dotyczy to tylko podawania łańcuchów literowych w cudzysłowach "", problem nie istnieje podczas ładowania ścieżek z pliku.

+2

+1 Zdecydowanie do zrobienia.Przykład na stronie głównej umożliwia przeszukiwanie katalogu: Użyj metody path.extension() do wyszukiwania logów (zobacz http://www.boost.org/doc/libs/1_36_0/libs/filesystem/doc/index .htm) – Tom

+0

Rzeczywiście jest to w większości przypadków droga, jednak wymaga to w niektórych przypadkach niepożądanej zależności od zewnętrznej biblioteki. Jeśli chcesz pracować tylko zgodnie ze standardem C++, sugeruję spojrzenie na regex w C++, gdzie możesz zdefiniować wyrażenie regularne, aby zrobić to, co chcesz (mnóstwo przykładów w Internecie). Zaleta - brak obciążenia z powodu dodatkowych zależności. Jednak to również pozostawia jedno pytanie otwarte - czy wymagane jest wieloplatformowe? Boost dba o styl ścieżki niezależnie od tego, czy korzystasz z systemu Windows czy Linux. Używając regex musisz to zrobić sam. – rbaleksandar

13

Będziesz musiał odczytać swoje nazwy plików z pliku w std::string. Możesz użyć operatora wyodrębniania ciągów znaków z std::ostream. Po uzyskaniu nazwy pliku w postaci std::string można użyć metody std::string::find_last_of, aby znaleźć ostatni separator.

coś takiego:

std::ifstream input("file.log"); 
while (input) 
{ 
    std::string path; 
    input >> path; 

    size_t sep = path.find_last_of("\\/"); 
    if (sep != std::string::npos) 
     path = path.substr(sep + 1, path.size() - sep - 1); 

    size_t dot = path.find_last_of("."); 
    if (dot != std::string::npos) 
    { 
     std::string name = path.substr(0, dot); 
     std::string ext = path.substr(dot, path.size() - dot); 
    } 
    else 
    { 
     std::string name = path; 
     std::string ext = ""; 
    } 
} 
+0

Nie chcę być inteligentnym tyłkiem, ale powinien to być path.substr, a nie path.substring, prawda? –

0

Używam również ten fragment kodu w celu określenia odpowiedniego znaku kreski ułamkowej.

boost::filesystem::path slash("/"); 
    boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native(); 

a następnie zastąpić ukośniki z korzystnym ukośnik dla OS Przydatne, jeśli ktoś jest stale wdrażania pomiędzy Linux/. okna

6

Dla C++ 17:

#include <filesystem> 

std::filesystem::path p("c:/dir/dir/file.ext"); 
std::cout << "filename and extension: " << p.filename() << std::endl; // "file.ext" 
std::cout << "filename only: " << p.stem() << std::endl;    // "file" 

Wniosek o systemie plików: http://en.cppreference.com/w/cpp/filesystem


Jak sugeruje @RoiDanto, do formatowania wyjściowego std::out może otaczać wyjście z cytatami, np:

filename and extension: "file.ext" 

Można konwertować std::filesystem::path do std::string przez p.filename().string() jeśli to konieczne, np .:

filename and extension: file.ext 
+0

Hej @RoiDanton, dzięki za edycję! Właśnie sprawdziłem przykładowy kod w odnośniku ponownie, nie wydaje się, że konieczne jest przekonwertowanie zwracanego typu z 'std :: filesystem :: path' na' std :: string', aby móc używać 'std :: cout'. http://en.cppreference.com/w/cpp/filesystem/path/filename Ale jeśli uważasz inaczej, możesz skomentować lub edytować wpis ponownie. –

+0

To prawda, że ​​'std :: cout' może polegać na niejawnej konwersji. Jednak ponieważ komentarze po 'std :: cout' mówią file.ext i file, do komentarzy należy dodać' .string() 'lub powinny one być" file.ext "i" file ". W Visual C++ rzeczywiście nie ma różnicy (nawet bez 'string()' dane wyjściowe są bez cudzysłowów), ale z gcc 6.1 wynik jest z cudzysłowami, jeśli '.string()' zostanie pominięte. Zobacz http://coliru.stacked-crooked.com/view?id=a55ea60bbd36a8a3 –

+0

@RoiDanton, hej, to interesujący wgląd. Zaktualizuję post ponownie. Dzięki za udostępnienie tego! –

0

Do maszyn Linux lub UNIX, OS ma dwie funkcje czynienia z nazw ścieżek i plików. użyj man 3 basename, aby uzyskać więcej informacji o tych funkcjach. Zaletą korzystania z funkcjonalności systemu jest to, że nie trzeba instalować funkcji zwiększania dawki ani konieczności pisania własnych funkcji.

#include <libgen.h> 
     char *dirname(char *path); 
     char *basename(char *path); 

przykład kod ze strony człowieka:

char *dirc, *basec, *bname, *dname; 
      char *path = "/etc/passwd"; 

      dirc = strdup(path); 
      basec = strdup(path); 
      dname = dirname(dirc); 
      bname = basename(basec); 
      printf("dirname=%s, basename=%s\n", dname, bname); 

powodu const typu argumentu funkcji basename(), to jest trochę nie prosto do przodu za pomocą tego wewnątrz kodu C++ . Oto prosty przykład z mojej bazy kodu:

string getFileStem(const string& filePath) const { 
    char* buff = new char[filePath.size()+1]; 
    strcpy(buff, filePath.c_str()); 
    string tmp = string(basename(buff)); 
    string::size_type i = tmp.rfind('.'); 
    if (i != string::npos) { 
     tmp = tmp.substr(0,i); 
    } 
    delete[] buff; 
    return tmp; 
} 

Użycie nowego/usunięcia nie jest dobrym stylem. Mógłbym umieścić go w try-catch bloku na wypadek, gdyby coś się stało między dwoma połączeniami.

Powiązane problemy