Преобразования верхнего и нижнего регистров C/С++ UTF-8
Проблема:
Существует метод с соответствующим тестовым сценарием, который работает на одной машине и не работает с другой (подробнее см. Ниже). Я предполагаю, что что-то не так с кодом, заставляя его работать случайно на одной машине. К сожалению, я не могу найти проблему.
Обратите внимание, что использование кодировок std::string и utf-8 - это требования, на которые я не влияю. Использование С++-методов было бы вполне нормально, но, к сожалению, я ничего не нашел. Следовательно, использование С-функций.
Метод:
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 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.
В любом случае, есть машина, которая компилирует этот код и запускает его, пока он терпит неудачу. Что-то не так с кодом, и мне интересно, что. Указание на методы С++ (в частности, STL) также будет отличным. Boost и другие библиотеки следует избегать из-за других внешних требований.
Ответы
Ответ 1
мелкий случай sharp s: ß; верхний регистр резкий s: ẞ. Использовали ли вы прописную букву в своем утверждении?
Похоже, glibg 2.14 следует за инструментами pre unicode5.1 без версии верхнего острия s, а на другой машине libc использует unicode 5.1 ẞ = U1E9E...
Ответ 2
Может быть, кто-то будет использовать его (возможно, для тестов)
С этим вы можете сделать простой конвертер:) Нет дополнительных libs:)
http://pastebin.com/fuw4Uizk
1482 буквы
Пример
Ь <> ь
Э <> э
Ю <> ю
Я <> я
Ѡ <> ѡ
Ѣ <> ѣ
Ѥ <> ѥ
Ѧ <> ѧ
Ѩ <> ѩ
Ѫ <> ѫ
Ѭ <> ѭ
Ѯ <> ѯ
Ѱ <> ѱ
Ѳ <> ѳ
Ѵ <> ѵ
Ѷ <> ѷ
Ѹ <> ѹ
Ѻ <> ѻ
Ѽ <> ѽ
Ѿ <> ѿ
Ҁ <> ҁ
Ҋ <> ҋ
Ҍ <> ҍ
Ҏ <> ҏ
Ґ <> ґ
Ғ <> ғ
Ҕ <> ҕ
Җ <> җ
Ҙ <> ҙ
Қ <> қ
Ҝ <> ҝ
Ҟ <> ҟ
Ҡ <> ҡ
Ң <> ң
Ответ 3
Для меня работает следующий код С++ 11 (без учета
вопрос о том, как следует перевести острые вопросы, - это
оставлен без изменений. В любом случае, он постепенно прекращается с немецкого языка).
Оптимизация и верхняя буква только первая буква
оставленный как упражнение.
Изменить: Как указано, codecvt, похоже, устарел. Однако он должен оставаться в стандарте до тех пор, пока не будет определена соответствующая замена. См. Устаревший заголовок <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);
}
Ответ 4
Что вы ожидаете от версии немецкого ß-символа в верхнем регистре, для этого тестового примера?
Другими словами, ваши основные допущения ошибочны.
Обратите внимание, что в Википедии в комментарии указано:
Sharp s почти уникален среди букв латинского алфавита тем, что у него нет традиционной формы в верхнем регистре (одним из немногих других примеров является kra, ĸ, который использовался в Гренландии). Это связано с тем, что он никогда не встречается первоначально в немецком тексте, и традиционная немецкая печать (которая использовала черный текст) никогда не использовала все шапки. При использовании all-caps правила текущей орфографии требуют замены ß на SS. [1] Однако в 2010 году его использование стало обязательным в официальной документации при написании географических названий во всех шапках. [2]
Итак, основной тестовый пример, с острым, как начальный, нарушает правила немецкого языка. Я все еще думаю, что у меня есть точка в том, что исходное помещение для плакатов ошибочно, строки вообще не могут быть свободно преобразованы между верхним и нижним регистром для всех языков.
Ответ 5
Проблема заключается в том, что ваши локали, которые не утверждают, являются совместимыми, ваши локали, на которых действует assert, не соответствуют требованиям.
Технический отчет N897, требуемый в B.1.2 [ LC_CTYPE
Обоснование]:
Поскольку классы символов LC_CTYPE
основаны на определении символьного класса C Standard, категория не поддерживает многохарактерные элементы. Например, немецкий характер традиционно классифицируется как строчная буква. Нет соответствующей прописной буквы; при правильной капитализации немецкого текста будет заменена СС; то есть двумя символами. Этот вид преобразования выходит за рамки ключевых слов toupper
и tolower
.
Настоящий технический отчет был опубликован в 25 декабря 2010 года. Но согласно: https://en.wikipedia.org/wiki/Capital_%E1%BA%9E
В 2010 году использование капитала ẞ стало обязательным в официальной документации в Германии при написании географических названий во всех шапках
Но эта тема не была пересмотрена стандартным комитетом, настолько технически независимым от того, что говорит немецкое правительство, стандартизованное поведение toupper
должно состоять в том, чтобы не вносить изменения в символ ß.
Причина, по которой это работает непоследовательно над машинами, setlocale
:
Устанавливает указанный языковой стандарт системы или ее часть в качестве новой локали языка
Таким образом, это не совместимый системный язык, en_US.utf8
, который инструктирует toupper
изменять символ ß. К сожалению, специализация ctype<char>::clasic_table
не доступна на ctype<wchar_t>
, поэтому вы не можете изменять поведение. Оставляя вас с двумя вариантами:
- Создайте
const map<wchar_t, wchar_t>
для преобразования из всех возможных строчных wchar_t
в соответствующий заглавный wchar_t
-
Добавьте проверку для L'ß'
следующим образом:
int ret = wcrtomb(buf, wChar == L'ß' ? L'ẞ' : towupper(wChar), &state);
Live Example