Использование спецификатора boost::format %s со строками UTF-8

Мы добавляем поддержку UTF8 в существующее приложение с большой базой кода. Это приложение использует boost::format()и вывод в не-ASCII символах не выравнивается должным образом. В частности, при использовании %{width}.{length}спецификатор, boost::format() считает символы, которые не "делают правильные вещи" со строками utf8. Я думаю, что должно быть возможно изменить код длины строки (который, вероятно, string::size()) использовать utf8len() или что-то аналогичное, основанное на... что-то?

В этом случае нецелесообразно изменять существующую кодовую базу для использования UCS2 (или UCS4, или UTF-16 и т. Д.), Но можно изменить boost::format() если необходимо. Я надеялся, что кто-то еще столкнулся с этой потребностью, и может указать мне на возможное решение.

Примечание: я нашел несколько веб-страниц об использовании локалей с utf8, но большая часть этого показалась более подходящей для преобразования в / из utf8 и UCS4 в потоках.

1 ответ

AFAIK Boost Format измеряет все в единицах кода, даже если используется локаль на основе UTF-8.

Если вы можете переключиться на другую библиотеку, рассмотрите C++20 std::format или библиотека форматирования {fmt}, которая считает ширину в единицах ширины отображения (аналогично wcswidth), чтобы выравнивание было правильным. Например

fmt:: print ("┌ {0: ─ ^{2}}┐\ n"
           "│{1: ^{2}}│\ n"
           "└{0: ─ ^{2}}┘\ n", "", "Привет, мир!", 20);

печатает:

┌────────────────────┐
│   Hello, world!    │
└────────────────────┘

Отказ от ответственности: я являюсь автором {fmt} и C++20 std::format

Возможно, это слишком поздно для вас, но, возможно, это поможет кому-то еще. Boost::format принимает std:: locale в качестве необязательного параметра шаблона. (см. http://www.boost.org/doc/libs/1_55_0/libs/format/doc/format.html). Если вы передадите ему локаль с поддержкой юникода, такую ​​как boost::locale("en_US.UTF-8"), вы должны получить желаемое поведение.

Вместо того, чтобы каждый раз передавать языковой стандарт конструктору boost:: format, вы также можете установить языковой стандарт вашего приложения по умолчанию, что может помочь вам избежать других проблем. Если вы выберете этот путь, я бы порекомендовал использовать boost:: locale вместо std:: locale, так как boost:: locale не изменит ваше числовое форматирование, если вы не попросите его об этом (документы здесь).

В общем, это подход goto для того, чтобы приложение на C++ хорошо работало с Unicode. Если функциональность может использовать локаль (std::regex, std::sort, boost::format), предоставьте ей локаль, поддерживающую юникод, и вы должны быть в безопасности (а если нет, пожалуйста, скажите мне, я хочу знать).

Если вы создаете небольшое и легкое приложение и заботитесь только о 80% -ом случае, вы можете не захотеть платить цену за включение ICU (Internation Components for Unicode), который является языковым стандартом для усиления двигателя по умолчанию, когда предоставляется поддержка unicde. В этом случае соберите Boos, используя поддержку Unicode вашей ОС или Posix, и ваше приложение останется небольшим и легким, но у вас не будет большой поддержки Unicode, например, нескольких уровней сортировки.

Для описываемой проблемы поддержки Posix вполне достаточно.

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