1

Здравствуйте. Работаю с архивами: создание, добавление элементов и т.д..

Такая проблема: сторонний метод принимает имя элемента или архива const char*, если имя содержит русские символы, то работает не правильно.

Конвертирую в Utf8, работает, но делаю это через wstring(пока тестирую), который не знаю как заполнять снаружи, например из консоли или какого-нибудь пользовательского интерфейса.

Можно ли обойтись без wstring или заполнять wstring снаружи?

Консоль здесь ни при чём.

Пример того, что делаю я:

std::wstring zipName = L"C:\\Users\\malenkov\\Desktop\\Test\\маша.zip";

/// Конвертация
inline std::string ToUtf8(const std::wstring& str)
{
    static std::wstring_convert<std::codecvt_utf8<wchar_t>> Utf8conv;
    return Utf8conv.to_bytes(str);
}
/// Создание архива
zip_t* zipArchive = ::zip_open(ToUtf8(zipName).c_str(), ZIP_CREATE, &error);

И вот я хочу узнать: как можно задать zipName в рантайме, ведь без wstring, как я понимаю, не обойтись.

  • Возможный дубликат вопроса: Русский язык в консоли – αλεχολυτ Oct 31 '16 at 11:02
  • У меня проблема не с консолью. Я работаю с архивом. И дело в именах архива и его элементов. – Сергей Oct 31 '16 at 11:07
  • 1
    Если решения в упомянутой теме Вам не помогли, приведите [mcve] и внесите изменения в тело вопроса. – αλεχολυτ Oct 31 '16 at 11:11
  • 3
    Вопрос в том, в какой кодировке функция ожидает строку. Если ответ «ни в какой, строка себе и строка, а чё», то у вас проблема. – VladD Oct 31 '16 at 11:33
  • Я, конечно, в этом не специалист, но похоже это обычный ASCII, раз кириллица не работает. – Сергей Oct 31 '16 at 11:52
  • Консоль я написал для примера. Также я указал пользовательский интерфейс, чтобы не заострять внимание на консоли. Для приложения будет написан GUI, но также она будет получать команды через именованные каналы от других программ. Задать я имею в виду, что-то типа cin для string, но через wcin не получается, ведь он принимает int... – Сергей Oct 31 '16 at 12:19
  • 1
    С кодом понятнее. Я вам предлагаю два варианта 1. рукописный - конвертируйте ASCII в UTF8 вручную, можно даже найти в нете. Может есть встроена функция конвертации ASCII-Utf8. 2. перекодируйте ASCII в WCHAR а потом вызовите ToUtf8, можно даже совместить две функции так std::string ToUtf8(char * str){ wchar_t str2[1024/*max*/]; wsprintfW(str2,"%hs", str); return ToUtf8(str2); }; – nick_n_a Oct 31 '16 at 12:22
  • Попробую ваш способ, спасибо. Сообщу о результатах. – Сергей Oct 31 '16 at 12:28
  • По хорошему нужно проверить длинну строки, сделать malloc, потом free, много мороки. Я в таких случаях конвертирую именно как написал, а число /max/ подбираю по-случаю. Ещё это для windows – nick_n_a Oct 31 '16 at 12:30
  • А wsprintfW будет работать под Linux? Забыл написать, что программа должна быть кроссплатформенная. – Сергей Oct 31 '16 at 12:33
  • 1
    Если хочется в 1 строку - boost::locale::conv::to_utf<char>(text, locale); – J. Doe Oct 31 '16 at 12:46
  • @nick_n_a Всё получилось, спасибо ещё раз. – Сергей Nov 01 '16 at 06:46
  • 1
    1- каждая ascii строка является utf-8 строкой (utf-8 специально так был создан) 2- можно везде текст как utf-8 байты хранить (в char*, string) внутри программы, а на границе преобразовывать то что система даёт, например, для ввода/вывода через стандартные потоки в кроссплатформенном коде можно nowide::cin/cout потоки попробовать, которые автоматически конвертируют utf-8 строки в wchar_t строки, если необходимо для Windows Unicode API—на других системах это обычные std::cin/cout. – jfs Nov 01 '16 at 10:11
  • ASCII является подмножеством UTF-8. Вы видимо про что-то другое хотели спросить. – 0andriy Jan 09 '17 at 01:54

1 Answers1

1

Такая проблема: сторонний метод принимает имя элемента или архива const char*, если имя содержит русские символы, то работает не правильно.

Конвертирую в Utf8, работает, но делаю это через wstring(пока тестирую), который не знаю как заполнять снаружи, например из консоли или какого-нибудь пользовательского интерфейса.

Это говорит только о том, что сторонний метод ждет UTF-8, представленное в виде массива 8-битных символов (const char*).

Консоль здесь ни при чём.

Сама консоль возможно не причем (хотя как сказать), а вот какая локаль выставлена - это существенно. Это определяет в каком именно виде будут переданы вводимые данные в рантайме. Например, если вы вводите строку из консоли, работающей в локали ru_RU.CP1251 - то прилетят 8-битные символы, если ru_RU.UTF-8 - на вход прилетит уже сформированная UTF-8 строка.

Таким образом, задача сводится к шагам:

  • Выяснить в каком виде (в какой локали) производиться ввод данных
  • Если 8-битный ввод, предварительно преобразовать в UTF-8
  • Если UTF-8 - просто передать введенное

Но есть важные замечания

Первое

UI предназначается для работы с символами, а не с кодами символов! Что это значит? В различных 8-битовых кодировках (в расширенной части 128-255) определены собственные символы, которые могут отсутствовать в "соседних" 8-битных кодировках. Поэтому "почти" однозначное преобразование возможно только в одном направлении ASCII->UTF-8. Почему "почти"? Например, если взять символы кириллицы, то символ с кодом 0x98 из cp1251 - не имеет аналога ни в Unicode, ни в UTF-8.

Второе

"Широкие символы" в стандарте Unicode 4.0 (ISO/IEC 10646:2003) определяются как compiler-specific, и могут быть даже 8-ми битными. Поэтому, дабы подстраховаться от потерь при преобразованиях, я бы посоветовал вместо std::wstring использовать std::u32string

Третье

Используя STL, конечно же, лучше всего использовать предоставляемые средства перекодировок, это обеспечит большую кроссплатформенность кода. И, тем не менее, если программа должна обеспечивать наиболее полный набор UTF-8, включая разнообразные иероглифические кодировки, можно глянуть внешнюю библиотеку ICU.

Четвертое

Для работы сугубо с кириллицей можно попробовать написать свои процедуры перекодировок. Для C++ я не вижу необходимости. Хотя для чистого Си, в принципе можно. Ниже представлю таблицу, которую собирал по различным стандартам из unicode.org которую в данный момент собираюсь использовать для написания собственной библиотеки для Lua.

Majestio
  • 5,050
  • Я, например, автор, хочу сделать копипаст - оно работает. Можно привести рабочий пример? – nick_n_a Nov 01 '16 at 09:41
  • @nick_n_a, для данного вопроса копипасты быть не может - т.к. не все условия оговорены. А именно - в какой кодировке предполагается ввод. Автоматически определение так достоверно невозможно - метод ввода не обозначен. Например, читаем STDIN. Да, можно определить текущую локаль (если она есть), но данные могут быть введены через pipe, в совершенно другой кодировке. Поэтому и мой ответ не предполагает наличие кода. – Majestio Nov 01 '16 at 13:30