Существует ли стандартный соответствующий способ написания переносимой утилиты ls на C ++? - PullRequest
0 голосов
/ 15 ноября 2018

Давайте рассмотрим следующий код, перечисляющий содержимое каталога пути, указанного в качестве первого аргумента программы:

#include <filesystem>
#include <iostream>

int main(int argc, char **argv)
{

    if(argc != 2)
        std::cerr << "Please specify a directory.\n";

    for(auto& p: std::filesystem::directory_iterator(argv[1]))
        std::cout << p << '\n';

}

На первый взгляд это кажется очень компактным, переносимым и соответствующим стандарту C ++(пожалуйста, не обращайте внимания на то, что он не перехватывает исключения, если каталог не существует).

Однако, похоже, есть несколько подводных камней.В частности, стандарт C ++, по-видимому, не предписывает, что кодировка argv[1] соответствует принятой конструкторами std::filesystem::path, и не требует, чтобы кодировка, возвращаемая std::filesystem::path::string(), совпадала с принятой std::cout.

Скорее наоборот, стандарт, кажется, вводит новый термин «собственное кодирование», которое может отличаться от кодировки набора символов выполнения и определяется как:

Собственное кодирование узкогоСтрока символов - это текущая кодировка путей, зависящая от операционной системы ([fs.class.path]).

Из моего прочтения стандарта не происходит преобразования между кодировками, если std::filesystem::path::value_type соответствует типу char argv[1] (что верно для любой системы POSIX).

Это, кажется, позволяет, например, соответствовать реализации, в которой кодирование набора символов выполнения (и, следовательно,кодировка argv[1] и принятая std::cout) является EBCDIC, но кодировка строк принята и предоставленаБиблиотека файловой системы соответствует стандарту ISO 8859-1, и между ними нет преобразования, что делает библиотеку файловой системы практически бесполезной. Хуже того, нет способа выяснить, одинаковы ли эти две кодировки.

Это может даже стать опасным, если вы начнете писать утилиты, которые удаляют файлы иУдаленный файл, предоставленный argv[1], соответствует совершенно другому файлу, если он интерпретируется в собственной кодировке библиотеки файловой системы.

Обратите внимание, что файловые системы не используют кодировки, отличающиеся от тех, которые используются программами.Меня беспокоит то, что стандарт, по-видимому, не требует какого-либо преобразования этих кодировок.

Функции u8path() и u8string() здесь также бесполезны, поскольку стандарт также не предоставляет способа преобразования между UTF-8 и кодировка набора символов выполнения (используется argv[1] и std::cout).

Существует ли какой-либо переносимый, независимый от кодирования и совместимый со стандартами способ сделать это?

1 Ответ

0 голосов
/ 15 ноября 2018

Нет, и это не просто теоретический подход.

В системах Windows пути - это UTF-16, а path::value_type - это wchar_t, а не char, полученное из char** argv.Само по себе это не проблема - path можно создать из char*.Однако не каждое имя файла Windows может быть выражено как char*.Следовательно, программа не может перечислить содержимое некоторых каталогов, имя которых не может быть выражено как char*.

Теперь вы думаете, что Linux будет лучше.На самом деле это не совсем так - байты, которые вы получаете для имени файла, могут зависеть от , введете ли вы их с клавиатуры или через TAB!

...