У меня есть список файлов, хранящихся в .log
в этом синтаксисе:
c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg
Я хочу извлечь имя и расширение из этих файлов. Можете ли вы привести пример простого способа сделать это?
У меня есть список файлов, хранящихся в .log
в этом синтаксисе:
c:\foto\foto2003\shadow.gif
D:\etc\mom.jpg
Я хочу извлечь имя и расширение из этих файлов. Можете ли вы привести пример простого способа сделать это?
Чтобы извлечь имя файла без расширения, используйте boost:: filesystem:: path:: stem вместо уродливого 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
Если вам нужен безопасный способ (т.е. переносимый между платформами и не устанавливающий предположения на пути), я бы рекомендовал использовать boost::filesystem
.
Это выглядело бы так:
boost::filesystem::path my_path( filename );
Затем вы можете извлечь различные данные с этого пути. Здесь документация объекта пути.
Кстати: Также помните, что для использования пути, типа
c:\foto\foto2003\shadow.gif
вам нужно избежать \
в строковом литерале:
const char* filename = "c:\\foto\\foto2003\\shadow.gif";
Или используйте /
вместо этого:
const char* filename = "c:/foto/foto2003/shadow.gif";
Это относится только к заданию литеральных строк в кавычках ""
, проблема не возникает при загрузке путей из файла.
Вам нужно будет прочитать ваши имена файлов из файла в std::string
. Вы можете использовать оператор извлечения строки std::ostream
. Когда у вас есть имя файла в std::string
, вы можете использовать метод std::string::find_last_of
, чтобы найти последний разделитель.
Что-то вроде этого:
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 = "";
}
}
Не код, но вот идея:
std::string
из входного потока (std::ifstream
), каждый прочитанный экземпляр будет полным путемfind_last_of
в строке для \
find_last_of
для .
, а подстрока с обеих сторон даст вам имя + расширение.Для С++ 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"
Справка о файловой системе: http://en.cppreference.com/w/cpp/filesystem
Как было предложено @RoiDanto, для форматирования вывода std::out
может окружать вывод с помощью котировок, например:
filename and extension: "file.ext"
Вы можете преобразовать std::filesystem::path
в std::string
на p.filename().string()
, если это вам нужно, например:
filename and extension: file.ext
Я также использую этот фрагмент, чтобы определить соответствующий символ косой черты:
boost::filesystem::path slash("/");
boost::filesystem::path::string_type preferredSlash = slash.make_preferred().native();
а затем замените косые черты на предпочтительную косую черту для ОС. Полезно, если вы постоянно развертываете Linux/Windows.
Для Linux или Unix-устройств os имеет две функции, связанные с именами путей и файлов. используйте man 3 basename, чтобы получить дополнительную информацию об этих функциях. Преимущество использования предоставляемой системой функциональности заключается в том, что вам не нужно устанавливать boost или писать собственные функции.
#include <libgen.h>
char *dirname(char *path);
char *basename(char *path);
Пример кода с man-страницы:
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);
Из-за типа аргумента non-const функции basename() это немного не прямолинейно, используя этот код внутри С++. Вот простой пример из моей базы кода:
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;
}
Использование нового/удаления не является хорошим стилем. Я мог бы положить его в попытку/поймать блок в случае, если что-то произошло между двумя вызовами.