U + 10FFFC to jeden kodowy kod Unicode, ale interfejs string
nie odsłania bezpośrednio sekwencji punktów kodu Unicode. Jego interfejs udostępnia sekwencję jednostek kodowych UTF-16. To jest bardzo niski poziom tekstu. To dość niefortunne, że taki niskopoziomowy widok tekstu został przeszczepiony na najbardziej oczywisty i intuicyjny interfejs ... Postaram się nie wyrażać zbyt wiele na temat tego, jak nie podoba mi się ten projekt, i po prostu powiedzieć, że nie ma znaczenia jak nieszczęśliwy, to tylko (smutny) fakt, z którym musisz żyć.
Po pierwsze, zasugeruję użycie char.ConvertFromUtf32
, aby uzyskać początkowy ciąg znaków. Znacznie prostsza, o wiele bardziej czytelny:
var s = char.ConvertFromUtf32(0x10FFFC);
Tak, ten ciąg na Length
nie jest 1, ponieważ, jak powiedziałem, zajmuje interfejsu w UTF-16 jednostek, a nie kodu Unicode punkty kodowe. U + 10FFFC używa dwóch jednostek kodowych UTF-16, więc s.Length
ma wartość 2. Wszystkie punkty kodowe powyżej U + FFFF wymagają dwóch jednostek kodowych UTF-16 do ich reprezentacji.
Należy zauważyć, że ConvertFromUtf32
nie zwraca wartości char
: char
to jednostka kodowa UTF-16, a nie kodowy kod Unicode. Aby móc zwracać wszystkie punkty kodu Unicode, ta metoda nie może zwrócić pojedynczego char
. Czasami trzeba zwrócić dwa, i dlatego robi to ciąg. Czasami znajdziesz kilka interfejsów API obsługujących int
zamiast char
, ponieważ int
może być używany do obsługi wszystkich punktów kodowych (to jest to, co ConvertFromUtf32
bierze jako argument, a co ConvertToUtf32
tworzy jako wynik).
string
realizuje IEnumerable<char>
, co oznacza, że podczas iteracji nad string
dostać jeden UTF-16 jednostki kodu na iteracji. To dlatego iteracja twojego ciągu i wydrukowanie go daje pewne zepsute wyniki z dwoma "rzeczami" w nim. Są to dwie jednostki kodowe UTF-16, które składają się na reprezentację U + 10FFFC.Nazywa się je "surogatami". Pierwszy to surogat o wysokiej/ołowiu, a drugi to surogat nisko/szlakowy. Gdy drukujesz je pojedynczo, nie dają one znaczących wyników, ponieważ samotne surogaty nie są nawet poprawne w UTF-16 i nie są one również uważane za znaki Unicode.
Po dołączeniu tych dwóch surogatów do struny w pętli, skutecznie zrekonstruujesz zastępczą parę i wydrukujesz tę parę później jako jeden, aby uzyskać właściwe wyjście.
I na tyłku notowania, zauważ, że nic nie narzeka, że użyłeś źle sformułowanej sekwencji UTF-16 w tej pętli. To tworzy ciąg z samotnym surogat, a mimo wszystko wykonuje tak, jakby nic się nie stało: typ string
nie jest nawet rodzaj sensownych UTF-16 sekwencji jednostkowych kod, ale rodzaj dowolny UTF-16 sekwencja jednostek kodu.
The char
structure zapewnia statyczne metody radzenia sobie z surogatów: IsHighSurrogate
, IsLowSurrogate
, IsSurrogatePair
, ConvertToUtf32
i ConvertFromUtf32
. Jeśli chcesz możesz napisać iterator że iteracje nad znaków Unicode zamiast UTF-16 jednostek kod:
static IEnumerable<int> AsCodePoints(this string s)
{
for(int i = 0; i < s.Length; ++i)
{
yield return char.ConvertToUtf32(s, i);
if(char.IsHighSurrogate(s, i))
i++;
}
}
Następnie można iterację jak:
foreach(int codePoint in s.AsCodePoints())
{
// do stuff. codePoint will be an int will value 0x10FFFC in your example
}
Jeśli wolisz, aby każdy punkt kodu jako ciąg zamiast zmienić typ zwracany do IEnumerable<string>
i linię ustąpić:
yield return char.ConvertFromUtf32(char.ConvertToUtf32(s, i));
z tą wersją, następujące prace jak jest:
foreach(string codePoint in s.AsCodePoints())
{
Console.WriteLine(codePoint);
}
System.Globalization.StringInfo jest drogą do zrobienia. Reszta kodu jest nieprawidłowa. Zajrzyj na: https://msdn.microsoft.com/en-us/library/system.globalization.stringinfo(v=vs.110).aspx – X181
Nie jest jasne, co masz na myśli. Czy jest jakiś problem z kodem z tej odpowiedzi? –