Форматный вывод в консоли линукс
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Есть вот такая программа:
Если я ввожу строку в латинице — работает как и надо. Но если в кириллице, snprintf в спецификации %10s считает не code-point Юникода, а сырые байты. Т.е. на каждый кириллический символ функция считает 2 позиции, а реально отображается один символ. Соответственно форматирование рушится.
Если использовать потоковый вывод — эффект аналогичный. Вызов setlocale ничего не меняет.
Как можно побороть проблему? Хотелось бы с форматной строкой. Но на крайний случай можно и с потоковым выводом.
Да, это всё под Убунтой. Под виндой с кодировкой в cp-1251 проблем нет.
Большей частью под линуксом используется utf-8,
так что нужно научиться с ней работать.
Один из вариантов — конвертация в строку широких символов:
Спасибо, с swprintf работает ))
А как будет с потоками? Если я в строку
добавленную в самый конец вашего варианта, вместо s тупо вставляю wstr — компилятор ругается матерно.
Если вставляю converter.to_bytes(wstr), то опять сбивается форматирование.
И еще вопрос. Вы написали «один из вариантов». А какие есть еще варианты?
Например, работать везде с широкими символами,
а при выводе уже конвертировать:
http://rextester.com/HQJH24524
Например, установить какую-нибудь
однобайтную кодировку в терминале.
Спасибо.
Не вариант.
Кстати интересно, а как из программы определить какая кодировка установлена в терминале?
А не могли бы вы на пальцах объяснить что такое wchar_t? А также TCHAR. И как оно соотносится с UTF-8 и лр. разновидностями юникода.
А то в книжках это описано как-то невнятно. И вообще выглядит как wchat_t == UNICODE :(
Возможно, поможет nl_langinfo, но не уверен.
Нужно будет в мануалах смотреть.
Это тип для хранения т.н. широких символов.
Для Linux это 4 байта.
Для Windows это 2 байта.
Это макрос из windows.
В зависимости от определения макросов UNICODE (_UNICODE),
раскрывается либо в char, либо в wchar_t.
Если определены вышеуказанные макросы,
то работа будет производится с широкими символами.
Но utf-8 придется «перекодировать» в нужную кодировку.
Но можете работать и с utf-8, если необходимо.
Например, в glib есть для этого средства,
а в glibmm имеется готовая обертка — ustring.
Про wchar_t упоминают мало и редко в книгах.
Наверное, потому что это добавляет сложности,
которая при обучении и для примеров явно не нужна.
А в wchar_t символы в какой кодировке хранятся?
Я хотел еще попробовать сделать типа платформонезависимый (windows/linux) файл данных. Думал для этого использовать wchar_t. Но с разной длиной wchar_t вы меня как-то подкосили :( Получается, что фактически ни на что нельзя положиться: у числовых типов размерность в байтах не гарантирована, wchar_t тоже разный, хваленый юникод сделан через жопу и там, и там. Ппц!
Попробовал писать в файл wchar_t через wofstream. Под виндой пишет тупо в cp1251. А под линуксом вроде как в UTF-8. Но на каждый символ отводит по 2 байта. Т.е. получается, что внутри программы строка сидит в wchar_t, а при вводе/выводе в файл перекодируется из/в «родную» кодировку? Не, можно конечно тупо привести указатель на буфер к
char*
и писать через fwrite. Но это как-то уж совсем грубо.И не могу понять как определить какие локали присутствуют в системе. Если нераспознаваемую локаль подсовываешь, setlocale игнорирует, а locale() дает исключение. А что оно распознает — хрен его знает. Гарантируется только наличие двух локалей:
"C"
и""
. Где нарыть остальное?В Linux — UTF-32.
В C++ с Unicode беда.
Но обещают сделать нормальную поддержку.
Есть типы char16_t, char32_t.
Врядли. В utf-8 латиница будет занимать один байт,
а кириллица два (конечно, если адекватно закодировать).
Посмотрите как у Вас получается в файле.
Нельзя. UTF-8 и UTF16/32 кодируются по разному.
Предложенный способ даст полную белиберду в файле.
Стандартными средствами — никак.
setlocale возвращает ошибку,
которую в нубокнигах не считают нужной обрабатывать.
Оно и понятно. В разных ОС локали имеют разные названия.
Как-то стандартизировать эту шнягу будет не просто.
В документации к ОС. Ставить нужные и т.д.
Получение списка локалей тоже будет зависеть от системы.
В файле:
А вот если под виндой сделать
то в файле видно, что все символы в двухбайтовой кодировке.
В винде я это раскопал:
А вот с линуксом засада какая-то. В консоли команда locale дает нужную информацию. Но как это сделать программно я не понял. Попробовал покопать исходники locale — жуть и мрак. Эта программа входит в пакет glibc, а там одно на другое завязано и выдрать ее отдельно не получается. Окончательно завяз на том, что отсутствует один из заголовков: видимо он генерируется при построении всего пакета.
Ну да, вместо указателя на строку, возвращает NULL, и даже errno не выставляет. Сиди и гадай, что ей не понравилось в названии локали.
На самом деле, ничего сложного. Просто этим вопросом никто не занимается.
Вот, например, в линуксе русская локаль называется «ru_RU», а в винде «ru-RU». Ну почему по-разному?! Разве сложно закрепить стандартом (или хотя бы RFC) формат наименования локалей? Или такое уже есть, но Майкрософт, как всегда, забил на всех большой болт?
Как я вижу, чем больше я ковыряюсь в этом вопросе, тем дальше от основной цели, ради чего всё и затевалось. Видимо надо забить на это. А то получится, что модуль чтения/записи файла данных будет весить больше, чем вся остальная программа :( Ради теоретической возможности прочитать информацию из файла под другой осью.
Я Вам уже писал, что в windows wchar_t — 2 байта.
Под линуском для символов в подавляющем
большинстве используется мультибайтная кодировка utf-8,
отсюда и разница в количестве байт.
А в локализованной windows cp1251 используется,
но никто не мешает сохранять в utf-8.
Или не так.
В POSIX он есть и под Linux он соблюдается.
Майкрософт, как всегда, идет своей дорогой,
у них свои стандарты именования локалей,
но, под линуксом достаточно поставить дефолтную локаль — «»,
а дальше конвертировать всё в wchar_t и работать. :)
Как раз недавно писал упрощенный парсер markdown
и форматированный вывод в терминал.
Если бы не некоторые требования к приложению,
которые обязывают юзать линукс,
то приложение кроссплатформенное.
Пусть Ваше приложение работает с одной кодировкой — utf-8.
Внутри конвертируется в wchar_t.
Если кодировка файла не utf-8,
то либо белиберда будет,
либо можно распознать, что кодировка не такая,
и выдать ошибку пользователю,
либо попытаться программно определить кодировку
и заюзать соответствующую конвертацию.