Установить заголовок пути 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.

Другие вопросы по тегам