tl; dr
Use a biblioteca da UTI . Caso contrário, sua rotina de conversão será interrompida silenciosamente nos casos que você provavelmente nem conhece.
Primeiro você tem que responder a uma pergunta: Qual é a codificação do seu std::string? É ISO-8859-1? Ou talvez ISO-8859-8? Ou página de código 1252 do Windows? O que você está usando para converter maiúsculas para minúsculas sabe disso? (Ou falha miseravelmente para os personagens terminados 0x7f?)
Se você estiver usando UTF-8 (a única opção sensata entre as codificações de 8 bits) std::stringcomo contêiner, já estará enganando-se a acreditar que ainda está no controle das coisas, porque está armazenando uma sequência de caracteres multibyte em um contêiner que não conhece o conceito multibyte. Mesmo algo tão simples quanto .substr()uma bomba-relógio. (Como a divisão de uma sequência multibyte resultará em uma (sub)) string inválida.)
E assim que você tenta algo como std::toupper( 'ß' ), em qualquer codificação, está com problemas profundos. (Como simplesmente não é possível fazer isso "corretamente" com a biblioteca padrão, que pode fornecer apenas um caractere de resultado, não o "SS"necessário aqui.) [1] Outro exemplo seria std::tolower( 'I' ): o que deve gerar resultados diferentes, dependendo da localidade . Na Alemanha, 'i'estaria correto; na Turquia, 'ı'(LETRA LATINA PEQUENA I) é o resultado esperado (que, novamente, é mais de um byte na codificação UTF-8). Ainda outro exemplo é o sigma grego , maiúsculo '∑'e minúsculo 'σ'... exceto no final de uma palavra, onde está 'ς'.
Portanto, qualquer conversão de caso que funcione em um caractere de cada vez, ou pior, em um byte de cada vez, é interrompida pelo design.
Depois, há o ponto de que a biblioteca padrão, para o que é capaz de fazer, depende de quais localidades são suportadas na máquina em que seu software está executando ... e o que você faz se não estiver?
Então, o que você realmente está procurando é uma classe de string capaz de lidar com tudo isso corretamente, e essa não é nenhuma das std::basic_string<>variantes .
(Nota do C ++ 11: std::u16stringe std::u32stringsão melhores , mas ainda não são perfeitas. O C ++ 20 trouxe std::u8string, mas tudo o que faz é especificar a codificação. Em muitos outros aspectos, eles ainda permanecem ignorantes da mecânica do Unicode, como normalização, agrupamento, etc. .)
Enquanto o Boost parece bom, em termos de API, o Boost.Locale é basicamente um invólucro em torno da ICU . Se o Boost for compilado com suporte à ICU ... se não for, o Boost.Locale será limitado ao suporte de localidade compilado para a biblioteca padrão.
E acredite em mim, fazer com que o Boost compile com a UTI pode ser uma dor real às vezes. (Como não existem binários pré-compilados para o Windows, você precisará fornecê-los juntamente com o aplicativo e isso abre uma nova lata de worms ...)
Então, pessoalmente, eu recomendaria obter suporte completo a Unicode diretamente da boca do cavalo e usar a biblioteca da ICU diretamente:
#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;
}
Compile (com G ++ neste exemplo):
g++ -Wall example.cpp -licuuc -licuio
Isto dá:
ὀδυσσεύς
Observe que a conversão Σ <-> σ no meio da palavra e a conversão Σ <-> ς no final da palavra. Uma <algorithm>solução não baseada pode lhe dar isso.
[1] Em 2017, o Conselho de Ortografia Alemã determinou que "ẞ" U + 1E9E LETRA DE CAPITAL LATINA SHARP S poderia ser usada oficialmente, como uma opção ao lado da conversão tradicional de "SS" para evitar ambiguidade, por exemplo, em passaportes (onde os nomes são maiúsculos ) Meu lindo exemplo, tornado obsoleto por decisão do comitê ...