C / C ++ UTF-8 преобразования верхнего / нижнего регистра

Эта проблема:
Существует метод с соответствующим тестовым набором, который работает на одном компьютере и не работает на другом (подробности ниже). Я предполагаю, что с кодом что-то не так, и он случайно работает на одной машине. К сожалению, я не могу найти проблему.

Обратите внимание, что использование std :: string и utf-8 — это требования, на которые я не имею реального влияния. Использование методов C ++ было бы вполне нормально, но, к сожалению, я ничего не смог найти. Отсюда и использование C-функций.

Метод:

std::string firstCharToUpperUtf8(const string& orig) {
std::string retVal;
retVal.reserve(orig.size());
std::mbstate_t state = std::mbstate_t();
char buf[MB_CUR_MAX + 1];
size_t i = 0;
if (orig.size() > 0) {
if (orig[i] > 0) {
retVal += toupper(orig[i]);
++i;
} else {
wchar_t wChar;
int len = mbrtowc(&wChar, &orig[i], MB_CUR_MAX, &state);
// If this assertion fails, there is an invalid multi-byte character.
// However, this usually means that the locale is not utf8.
// Note that the default locale is always C. Main classes need to set them
// To utf8, even if the system's default is utf8 already.
assert(len > 0 && len <= static_cast<int>(MB_CUR_MAX));
i += len;
int ret = wcrtomb(buf, towupper(wChar), &state);
assert(ret > 0 && ret <= static_cast<int>(MB_CUR_MAX));
buf[ret] = 0;
retVal += buf;
}
}
for (; i < orig.size(); ++i) {
retVal += orig[i];
}
return retVal;
}

Тест:

TEST(StringUtilsTest, firstCharToUpperUtf8) {
setlocale(LC_CTYPE, "en_US.utf8");
ASSERT_EQ("Foo", firstCharToUpperUtf8("foo"));
ASSERT_EQ("Foo", firstCharToUpperUtf8("Foo"));
ASSERT_EQ("#foo", firstCharToUpperUtf8("#foo"));
ASSERT_EQ("ßfoo", firstCharToUpperUtf8("ßfoo"));
ASSERT_EQ("Éfoo", firstCharToUpperUtf8("éfoo"));
ASSERT_EQ("Éfoo", firstCharToUpperUtf8("Éfoo"));
}

Неудачный тест (происходит только на одной из двух машин):

Failure
Value of: firstCharToUpperUtf8("ßfoo")
Actual: "\xE1\xBA\x9E" "foo"Expected: "ßfoo"

На обеих машинах установлен языковой стандарт en_US.utf8. Однако они используют разные версии libc. Он работает на компьютере с GLIBC_2.14 независимо от того, где он был скомпилирован, и не работает на другом компьютере, хотя его можно скомпилировать только там, потому что в противном случае ему не хватает правильной версии libc.

В любом случае, есть машина, которая компилирует этот код и запускает его во время сбоя. Там должно быть что-то не так с кодом, и мне интересно, что. Указывать на методы C ++ (в частности, STL) также было бы здорово. Boost и других библиотек следует избегать из-за других внешних требований.

16

Решение

маленький корпус острый s: ß; верхний регистр резкий s: ẞ. Вы использовали прописную версию в вашем утверждении?
Похоже, что glibg 2.14 следует реализует pre unicode5.1 без версии прописного s в верхнем регистре, а на другой машине libc использует unicode 5.1 ẞ = U1E9E …

1

Другие решения

Может быть, кто-то будет использовать его (может быть, для испытаний)

С этим вы можете сделать простой конвертер 🙂 Никаких дополнительных библиотек 🙂

http://pastebin.com/fuw4Uizk

1482 буквы

пример

Ь <> ь
Э <> э
Ю <> ю
Я <> я
Ѡ <> ѡ
Ѣ <> ѣ
Ѥ <> ѥ
Ѧ <> ѧ
Ѩ <> ѩ
Ѫ <> ѫ
Ѭ <> ѭ
Ѯ <> ѯ
Ѱ <> ѱ
Ѳ <> ѳ
Ѵ <> ѵ
Ѷ <> ѷ
Ѹ <> ѹ
Ѻ <> ѻ
Ѽ <> ѽ
Ѿ <> ѿ
Ҁ <> ҁ
Ҋ <> ҋ
Ҍ <> ҍ
Ҏ <> ҏ
Ґ <> ґ
Ғ <> ғ
Ҕ <> ҕ
Җ <> җ
Ҙ <> ҙ
Қ <> қ
Ҝ <> ҝ
Ҟ <> ҟ
Ҡ <> ҡ
Ң <> ң
9

Следующий код C ++ 11 работает для меня (без учета
момент, когда вопрос о том, как острые s должны быть переведены — это
оставить без изменений. Во всяком случае, он постепенно вытесняется с немецкого.

Оптимизации и заглавные буквы только первая буква
оставил как упражнение.

Изменить: Как указано, кодеквт, кажется, устарела. Однако он должен оставаться в стандарте до тех пор, пока не будет определена подходящая замена. Увидеть Устаревший заголовок <codecvt> замена

#include <codecvt>
#include <iostream>
#include <locale>

std::locale const utf8("en_US.UTF-8");

// Convert UTF-8 byte string to wstring
std::wstring to_wstring(std::string const& s) {
std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
return conv.from_bytes(s);
}

// Convert wstring to UTF-8 byte string
std::string to_string(std::wstring const& s) {
std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
return conv.to_bytes(s);
}

// Converts a UTF-8 encoded string to upper case
std::string tou(std::string const& s) {
auto ss = to_wstring(s);
for (auto& c : ss) {
c = std::toupper(c, utf8);
}
return to_string(ss);
}

void test_utf8(std::ostream& os) {
os << tou("foo" ) << std::endl;
os << tou("#foo") << std::endl;
os << tou("ßfoo") << std::endl;
os << tou("Éfoo") << std::endl;
}

int main() {
test_utf8(std::cout);
}
3

Как вы думаете, какой должна быть заглавная версия немецкого символа ß для этого теста?

Другими словами, ваши основные предположения неверны.

Обратите внимание, что Википедия в комментарии гласит:

Sharp s почти уникален среди букв латинского алфавита тем, что у него нет традиционной заглавной буквы (один из немногих других примеров — kra, ĸ, который использовался в гренландском языке). Это связано с тем, что в немецком тексте это никогда не встречалось изначально, а традиционная немецкая печать (в которой использовалась blackletter) никогда не использовала заглавные буквы. При использовании всех заглавных букв текущие правила правописания требуют замены ß на SS. [1] Однако в 2010 году его использование стало обязательным в официальной документации при написании географических названий заглавными буквами. [2]

Таким образом, основной тестовый пример с резким s, возникающим в качестве инициала, нарушает правила немецкого языка. Я все еще думаю, что у меня есть точка зрения, в которой исходная предпосылка постеров неверна, строки вообще нельзя свободно конвертировать между прописными и строчными буквами для всех языков.

1

Проблема заключается в том, что ваши локали, которые не утверждают, соответствуют требованиям, ваши локали, на которых запускается подтверждение, не соответствуют требованиям.

Технический отчет N897 требуется в B.1.2 [LC_CTYPE Обоснование]:

Как LC_CTYPE классы символов основаны на стандартном определении класса символов C, категория не поддерживает элементы с несколькими символами. Например, немецкий символ традиционно классифицируется как строчная буква. Там нет соответствующей заглавной буквы; при правильном использовании заглавных букв немецкого текста он будет заменен на SS; т.е. двумя символами. Этот вид конверсии выходит за рамки toupper а также tolower ключевые слова.

Этот технический отчет был опубликован 25 декабря 2001 года. Но согласно: https://en.wikipedia.org/wiki/Capital_%E1%BA%9E

В 2010 году использование капитала mandatory стало обязательным в официальной документации в Германии при написании географических названий заглавными буквами

Но эта тема не была вновь рассмотрена комитетом по стандартизации, поэтому технически независимым от того, что говорит правительство Германии, стандартизированного поведения toupper не должно быть никаких изменений в характере ß.

Причина, по которой это работает непоследовательно на машинах setlocale:

Устанавливает указанную локаль системы или ее часть как новую локаль C

Так что это несовместимый язык системы, en_US.utf8 это инструктирует toupper изменить символ ß. К сожалению, специализация ctype<char>::clasic_table, недоступно на ctype<wchar_t> поэтому вы не можете изменить поведение. Оставляя вас с 2 вариантами:

  1. Создать const map<wchar_t, wchar_t> для преобразования из всех возможных строчных wchar_t в соответствующий верхний регистр wchar_t
  2. Добавить чек для L'ß' как это:

    int ret = wcrtomb(buf, wChar == L'ß' ? L'ẞ' : towupper(wChar), &state);
    

Живой пример

0
По вопросам рекламы [email protected]