亡灵术。
作为一项公共服务,这就是您实际正确反转 string
的方式(反转字符串 NOT 等于反转 字符序列)
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
顺便说一句,在Golang中,正确的做法是这样的:
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)
Reference:
字符是一个重载的术语,它可以表示很多东西。
代码点是信息的原子单位。文本是一个序列
码点。每个代码点都是一个数字,由
Unicode 标准。
字素是显示的一个或多个代码点的序列
作为一个单一的图形单元,读者将其识别为一个单一的图形单元
书写系统的要素。例如,a 和 ä 都是
字形,但它们可能由多个代码点组成(例如 ä 可能是
两个代码点,一个用于基本字符 a 后跟一个用于
透析;但还有一个替代的、遗留的、单一的代码点
代表这个字形)。某些代码点从不属于任何代码点
字形(例如零宽度非连接符或方向覆盖)。
字形是图像,通常存储在字体中(这是一个集合
字形),用于表示字形或其部分。字体可能
将多个字形组合成一个表示,例如,如果
上面的 ä 是单个代码点,字体可以选择将其呈现为
两个独立的、空间重叠的字形。对于 OTF,字体的 GSUB 和
GPOS 表包含替换和定位信息,以使
这项工作。一个字体可能包含多个相同的替代字形
字形也是。