Установить заголовок пути UTF-8 в libarchive
РЕЗЮМЕ
Как я могу написать 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-файле без каких-либо преобразований.
Я добавил подход "установить локаль" в качестве (неудовлетворительного) ответа.
2 ответа
Это обходной путь, при котором имена путей будут храниться с использованием системных настроек системы, т. Е. Полученный 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.
Будущее
Существует проблема с библиотеками в именах файлов UTF-8. Вся история довольно сложная, но, похоже, они могут добавить лучшую поддержку UTF-8 в libarchive 4.0.
Я добавлю это как ответ, потому что это превышает ограничения текста для комментария.
При запуске программы глобальная локаль совпадает с классической локалью. Классическим языковым стандартом C является американский язык ASCII в стандартной библиотеке C, который неявно используется в программах, которые не интернационализированы. И как предполагает этот источник -
... Если вы планируете локализовать вашу программу, подходящая стратегия может заключаться в том, чтобы получить исходный языковой стандарт один раз в начале вашей программы и никогда больше не изменять этот параметр. Таким образом, ваше приложение адаптируется к одной конкретной локали и использует его на протяжении всей среды выполнения. Пользователи таких приложений могут явно установить свою любимую локаль перед запуском приложения. В системах UNIX они делают это, устанавливая переменные среды, такие как LANG; другие операционные системы могут использовать другие методы.
В своей программе вы можете указать, что хотите использовать предпочитаемую локальную локаль пользователя, вызвав
std::setlocale("")
при запуске, передавая пустую строку в качестве имени локали. Пустая строка указывает setlocale использовать локаль, указанную пользователем в среде.
Я получил имена файлов UTF-8, работающие в ZIP-архивах с использованием libarchive-3.3.3, с использованием этого точного потока (последовательность важна!):
entry = archive_entry_new();
archive_entry_set_pathname_utf8(entry, utf8Filename);
archive_entry_set_pathname(entry, utf8Filename);
При переключении имя_путь_катего_установки_архива_utf8 / имя_набора_архива_набор_путь в функции ZIP проводника Windows искажаются. Это сработало для меня для немецких умляутов (но должно работать для каждого символа UTF-8). Это работало даже для 2-байтовых и 3-байтовых символов UTF-8 (NFC/NFD).
// Дополнение Процесс должен запускаться в среде с переменной LANG, установленной на язык, поддерживающий UTF-8 (например, "LANG=de_DE.UTF-8" в моем случае). Без этой среды процесс не будет генерировать правильные символы UTF-8.