Некромантия.
Как общедоступная служба, именно так вы на самом деле ПРАВИЛЬНО переворачиваете строку
(обращение строки НЕ равно обращению последовательности символов )
public static class Test
{
private static System.Collections.Generic.List<string> GraphemeClusters(string s)
{
System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();
System.Globalization.TextElementEnumerator enumerator = System.Globalization.StringInfo.GetTextElementEnumerator(s);
while (enumerator.MoveNext())
{
ls.Add((string)enumerator.Current);
}
return ls;
}
// this
private static string ReverseGraphemeClusters(string s)
{
if(string.IsNullOrEmpty(s) || s.Length == 1)
return s;
System.Collections.Generic.List<string> ls = GraphemeClusters(s);
ls.Reverse();
return string.Join("", ls.ToArray());
}
public static void TestMe()
{
string s = "Les Mise\u0301rables";
// s = "noël";
string r = ReverseGraphemeClusters(s);
// This would be wrong:
// char[] a = s.ToCharArray();
// System.Array.Reverse(a);
// string r = new string(a);
System.Console.WriteLine(r);
}
}
См:
https://vimeo.com/7403673
Кстати, на Голанге правильный путь таков:
package main
import (
"unicode"
"regexp"
)
func main() {
str := "\u0308" + "a\u0308" + "o\u0308" + "u\u0308"
println("u\u0308" + "o\u0308" + "a\u0308" + "\u0308" == ReverseGrapheme(str))
println("u\u0308" + "o\u0308" + "a\u0308" + "\u0308" == ReverseGrapheme2(str))
}
func ReverseGrapheme(str string) string {
buf := []rune("")
checked := false
index := 0
ret := ""
for _, c := range str {
if !unicode.Is(unicode.M, c) {
if len(buf) > 0 {
ret = string(buf) + ret
}
buf = buf[:0]
buf = append(buf, c)
if checked == false {
checked = true
}
} else if checked == false {
ret = string(append([]rune(""), c)) + ret
} else {
buf = append(buf, c)
}
index += 1
}
return string(buf) + ret
}
func ReverseGrapheme2(str string) string {
re := regexp.MustCompile("\\PM\\pM*|.")
slice := re.FindAllString(str, -1)
length := len(slice)
ret := ""
for i := 0; i < length; i += 1 {
ret += slice[length-1-i]
}
return ret
}
И неправильный способ таков (ToCharArray.Reverse):
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
Обратите внимание, что вам нужно знать разницу между
- персонаж и глиф
- байт (8 бит) и кодовая точка / руна (32 бита)
- кодовая точка и GraphemeCluster [32+ бит] (он же Grapheme / Glyph)
Справка:
Символ - перегруженный термин, который может означать много вещей.
Кодовая точка - это атомная единица информации. Текст представляет собой последовательность
кодовые точки. Каждая кодовая точка - это число, которому
Стандарт Юникод.
Графема - это последовательность одной или нескольких отображаемых кодовых точек.
как единая графическая единица, которую читатель распознает как единый
элемент системы письма. Например, и a, и ä являются
графемы, но они могут состоять из нескольких кодовых точек (например,
две кодовые точки, одна для базового символа, а затем одна для
диарез; но есть и альтернатива, устаревшая, единая кодовая точка
представляя эту графему). Некоторые кодовые точки никогда не являются частью какого-либо
графема (например, несоединение нулевой ширины или переопределение направления).
Глиф - это изображение, обычно хранящееся в шрифте (который является коллекцией
глифов), используется для обозначения графем или их частей. Шрифты могут
составить несколько глифов в одном представлении, например, если
Вышеприведенный ä представляет собой одну кодовую точку, шрифт может выбрать для
два отдельных, пространственно наложенных глифа. Для OTF, шрифта GSUB и
Таблицы GPOS содержат информацию о замене и позиционировании
эта работа. Шрифт может содержать несколько альтернативных глифов для одного и того же
графема тоже.