c++ - Iteración multiplataforma de la cadena Unicode(contando Graphemes usando ICU)
icu download (3)
Quiero iterar cada carácter de una cadena Unicode, tratar cada par suplente y combinar la secuencia de caracteres como una sola unidad (un grafema).
Ejemplo
El texto "नमस्ते" se compone de los puntos de código: U+0928, U+092E, U+0938, U+094D, U+0924, U+0947
, de los cuales, U+0938
y U+0947
son marcas de combinación .
static void Main(string[] args)
{
const string s = "नमस्ते";
Console.WriteLine(s.Length); // Ouptuts "6"
var l = 0;
var e = System.Globalization.StringInfo.GetTextElementEnumerator(s);
while(e.MoveNext()) l++;
Console.WriteLine(l); // Outputs "4"
}
Así que ahí lo tenemos en .NET. También tenemos el CharNextW()
#include <Windows.h>
#include <iostream>
#include <string>
int main()
{
const wchar_t * s = L"नमस्ते";
std::cout << std::wstring(s).length() << std::endl; // Gives "6"
int l = 0;
while(CharNextW(s) != s)
{
s = CharNextW(s);
++l;
}
std::cout << l << std::endl; // Gives "4"
return 0;
}
Pregunta
Las dos formas que conozco son específicas de Microsoft. ¿Hay formas portátiles de hacerlo?
- Escuché sobre la UCI pero no pude encontrar algo relacionado rápidamente (
UnicodeString(s).length()
aún da 6). Sería una respuesta aceptable para apuntar a la función / módulo relacionado en la UCI. - C ++ no tiene una noción de Unicode, por lo que una biblioteca liviana multiplataforma para tratar estos problemas sería una respuesta aceptable.
Edición: respuesta correcta utilizando la UCI
@McDowell dio la sugerencia de usar BreakIterator
de la UCI, que creo que puede considerarse como el estándar de facto multiplataforma para tratar con Unicode. Aquí hay un código de ejemplo para demostrar su uso (ya que los ejemplos son sorprendentemente raros):
#include <unicode/schriter.h>
#include <unicode/brkiter.h>
#include <iostream>
#include <cassert>
#include <memory>
int main()
{
const UnicodeString str(L"नमस्ते");
{
// StringCharacterIterator doesn''t seem to recognize graphemes
StringCharacterIterator iter(str);
int count = 0;
while(iter.hasNext())
{
++count;
iter.next();
}
std::cout << count << std::endl; // Gives "6"
}
{
// BreakIterator works!!
UErrorCode err = U_ZERO_ERROR;
std::unique_ptr<BreakIterator> iter(
BreakIterator::createCharacterInstance(Locale::getDefault(), err));
assert(U_SUCCESS(err));
iter->setText(str);
int count = 0;
while(iter->next() != BreakIterator::DONE) ++count;
std::cout << count << std::endl; // Gives "4"
}
return 0;
}
Debería poder usar el BreakIterator para esto (la instancia del personaje suponiendo que es equivalente a la versión de Java).
ICU tiene una interfaz muy antigua, Boost.Locale es mucho mejor:
#include <iostream>
#include <string_view>
#include <boost/locale.hpp>
using namespace std::string_view_literals;
int main()
{
boost::locale::generator gen;
auto string = "noël 😸😾"sv;
boost::locale::boundary::csegment_index map{
boost::locale::boundary::character, std::begin(string),
std::end(string), gen("")};
for (const auto& i : map)
{
std::cout << i << ''/n'';
}
}
El texto es de here
La clase ustring de Glib ustring da cadenas utf-8, si usar utf-8 está bien para usted. Está diseñado para ser similar a std::string
. Como utf-8 es nativo para Linux, su tarea es bastante sencilla:
int main()
{
Glib::ustring s = L"नमस्ते";
cout << s.size();
}
también puede iterar en los caracteres de la cadena como de costumbre con Glib::ustring::iterator