tl; dr
Użyj biblioteki ICU . Jeśli tego nie zrobisz, procedura konwersji po cichu załamie się na przypadkach, o których prawdopodobnie nawet nie wiesz.
Najpierw musisz odpowiedzieć na pytanie: jakie jest twoje kodowaniestd::string
? Czy to jest ISO-8859-1? A może ISO-8859-8? Lub Windows Codepage 1252? Czy to, czego używasz do konwersji wielkich i małych liter, wie o tym? (A może źle to kończy się w przypadku postaci 0x7f
?)
Jeśli używasz UTF-8 (jedyny rozsądny wybór wśród kodowań 8-bitowych) z std::string
jako kontenerem, już oszukujesz siebie, aby uwierzyć, że nadal kontrolujesz rzeczy, ponieważ przechowujesz wielobajtową sekwencję znaków w kontenerze który nie zna koncepcji wielobajtowej. Nawet coś tak prostego jak .substr()
tykająca kula czasowa. (Ponieważ podział sekwencji wielobajtowej spowoduje niepoprawny (pod-) ciąg znaków.)
I gdy tylko spróbujesz czegoś takiego std::toupper( 'ß' )
, w jakimkolwiek kodowaniu, będziesz miał poważne kłopoty. (Ponieważ po prostu nie jest możliwe zrobienie tego „dobrze” ze standardową biblioteką, która może dostarczyć tylko jeden znak wyniku, a nie "SS"
tutaj potrzebny.) [1] Innym przykładem byłby std::tolower( 'I' )
inny wynik, w zależności od ustawień regionalnych . W Niemczech 'i'
byłoby poprawne; w Turcji 'ı'
(LATIN SMALL LETTER DOTLESS I) to oczekiwany wynik (który w kodowaniu UTF-8 to więcej niż jeden bajt). Jeszcze innym przykładem jest język grecki Sigma , wielkie litery '∑'
, małe litery 'σ'
... z wyjątkiem końca słowa, gdzie jest 'ς'
.
Więc, każda konwersja przypadku, która działa na znak na raz lub, co gorsza, bajt na raz, jest zepsuta przez projekt.
To jest sens, że standardowa biblioteka, po co to , zależnie od obsługiwanych ustawień narodowych, zależy od jest w stanie zrobić na komputerze, na którym działa twoje oprogramowanie ... i co robisz, jeśli nie jest?
Tak więc naprawdę szukasz klasy ciągów, która jest w stanie poradzić sobie z tym wszystkim poprawnie i nie jest to żaden z std::basic_string<>
wariantów .
(Uwaga C ++ 11: std::u16string
i std::u32string
są lepsze , ale wciąż nie są idealne. Przyniesiono C ++ 20std::u8string
, ale wszystko to określa kodowanie. Pod wieloma innymi względami nadal nie znają mechaniki Unicode, takiej jak normalizacja, zestawianie, .. .)
Podczas gdy Boost wygląda ładnie, pod względem API, Boost.Locale jest zasadniczo otoczeniem ICU .Jeśli Boost jest skompilowany z obsługą ICU ... jeśli nie jest, Boost.Locale jest ograniczony do obsługi ustawień regionalnych skompilowanej dla standardowej biblioteki.
I uwierz mi że kompilacja Boosta na OIOM-ie może czasem być prawdziwym bólem. (Nie ma wstępnie skompilowanych plików binarnych dla systemu Windows, więc musisz je dostarczyć razem z aplikacją, a to otwiera zupełnie nową puszkę robaków ...)
Więc osobiście poleciłbym uzyskanie pełnego wsparcia Unicode prosto z pyska konia i korzystanie z OIOM biblioteki :
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>
#include <iostream>
int main()
{
/* "Odysseus" */
char const * someString = u8"ΟΔΥΣΣΕΥΣ";
icu::UnicodeString someUString( someString, "UTF-8" );
// Setting the locale explicitly here for completeness.
// Usually you would use the user-specified system locale,
// which *does* make a difference (see ı vs. i above).
std::cout << someUString.toLower( "el_GR" ) << "\n";
std::cout << someUString.toUpper( "el_GR" ) << "\n";
return 0;
}
Skompiluj (z G ++ w tym przykładzie):
g++ -Wall example.cpp -licuuc -licuio
To daje:
ὀδυσσεύς
Zauważ, że konwersja Σ <-> σ w środku słowa, a konwersja Σ <-> ς na końcu słowa. Nie<algorithm>
rozwiązanie nie może tego zapewnić.
[1] W 2017 r. Rada Ortografii Niemieckiej orzekła, że „ẞ” U + 1E9E LATIN CAPITAL LETTER SHARP S może być oficjalnie używany jako opcja obok tradycyjnej konwersji „SS”, aby uniknąć dwuznaczności, np. W paszportach (gdzie nazwy są pisane wielkimi literami ). Mój piękny przykład, który stał się nieaktualny decyzją komisji ...