РЕЗЮМЕ
Как я могу написать zip-файл, используя libarchive на C ++, чтобы имена путей были в кодировке UTF-8? При использовании путей к UTF-8 специальные символы будут правильно декодироваться при использовании OS X / Linux / Windows 8/7-Zip / WinZip.
ПОДРОБНОСТИ
Я пытаюсь написать zip-архив с помощью libarchive, компилируя с Visual C ++ 2013 для Windows.
Я хотел бы иметь возможность добавлять файлы с не-ASCII-символами (например, äöü.txt) в zip-архив.
В libarchive есть четыре функции для установки заголовка пути:
void archive_entry_set_pathname(struct archive_entry *, const char *);
void archive_entry_copy_pathname(struct archive_entry *, const char *);
void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
int archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
К сожалению, ни один из них, кажется, не работает.
В частности, я попробовал:
const char* myUtf8Str = ...
archive_entry_update_pathname_utf8(entry, myUtf8Str);
// this sounded like the most straightforward solution
а также
const wchar_t* myUtf16Str = ...
archive_entry_copy_pathname_w(entry, myUtf16Str);
// UTF-16 encoded strings seem to be the default on Windows
В обоих случаях полученный zip-архив неправильно отображает имена файлов как в Windows Explorer, так и в 7-Zip.
Я уверен, что мои входные строки закодированы правильно, так как я конвертирую их из Qt QString
примеры, которые отлично работают в других частях моего кода:
const char* myUtf8Str = filename.toUtf8().constData();
const wchar_t* myUtf16Str = filename.toStdWString().c_str();
Например, это работает даже для другого вызова libarchive, при создании zip-файла:
archive_write_open_filename_w(archive, zipFile.toStdWString().c_str());
// creates a zip archive file where the non-ASCII
// chars are encoded correctly, e.g. äöü.zip
Я также попытался изменить параметры для libarchive, как предложено этот пример:
archive_write_set_options(a, "hdrcharset=UTF-8");
Но этот вызов не удался, поэтому я предполагаю, что мне нужно установить какой-то другой вариант, но у меня заканчиваются идеи …
ОБНОВЛЕНИЕ 2
Я сделал еще немного чтения о формате zip. Он позволяет записывать имена файлов в UTF-8, так что OS X / Linux / Windows 8/7-Zip / WinZip всегда будет правильно их декодировать, см., Например, Вот.
Это то, чего я хочу достичь с помощью libarchive, то есть я хотел бы передать его в кодировке UTF-8 pathname
и сохраните его в zip-файле без каких-либо преобразований.
Я добавил подход «установить локаль» в качестве (неудовлетворительного) ответа.
Это обходной путь, при котором имена путей будут храниться с использованием настроек локали системы, т. Е. Полученный файл zip может быть правильно декодирован в той же системе, но не переносим.
Это не удовлетворяет, я просто публикую это, чтобы показать, что это не то, что я ищу.
Установите глобальную локаль на ""
как объяснил здесь:
std::locale::global(std::locale(""));
и затем прочитайте это назад:
std::locale loc;
std::cout << loc.name() << std::endl;
// output: English_United States.1252
// may of course be different depending on system settings
Затем установите pathname
используя archive_entry_update_pathname_utf8
,
Файл zip теперь содержит имена файлов, закодированные с помощью Windows-1252, поэтому моя Windows может их прочитать, но они отображаются как мусор, например, на. Linux.
Будущее
Eсть проблема с архивами для файлов UTF-8. Вся история довольно сложная, но похоже, что они могут добавить лучшую поддержку UTF-8 в libarchive 4.0.
Я добавлю это как ответ, потому что это превышает ограничения текста для комментария.
При запуске программы глобальная локаль совпадает с классической локалью. Классическая локаль C — это английская локаль ASCII в США в стандартной библиотеке C, которая неявно используется в программах, которые не интернационализированы.
И в качестве этот источник предлагает —
…Если вы планируете локализовать свою программу,
подходящая стратегия может заключаться в том, чтобы получить родную локаль один раз на
начало вашей программы, и никогда, никогда не меняйте эту настройку снова.
Таким образом, ваше приложение адаптируется к одной конкретной локали, и
использует это на протяжении всего времени выполнения. Пользователи таких приложений
может явно установить свою любимую локаль перед началом
приложение. В системах UNIX они делают это, устанавливая среду
переменные, такие как LANG; другие операционные системы могут использовать другие методы.В вашей программе вы можете указать, что вы хотите использовать пользователя
предпочитаемый родной язык, позвонивstd::setlocale("")
при запуске,
передавая пустую строку в качестве имени локали. Пустая строка говорит
setlocale для использования локали, указанной пользователем в среде.