这是一个相当不雅的问题,所以没有一种方法会真的很优雅。
不过,我们当然可以改进一些东西。哪种方法最适合取决于需要进行的更改的数量(以及要更改的字符串的大小,尽管通常最好假定这种更改是或可能很大)。
在一个替换字符中,目前使用的方法 - 使用.Replace
比较好,但我会用"\u2013"
替换char.ConvertFromUtf32(8211)
。对性能的影响可以忽略不计,但它更具可读性,因为在U + 2013中以十六进制形式引用该字符比使用十进制符号更为常见(当然,char.ConvertFromUtf32(0x2013)
在那里会有相同的优势,但仅使用char符号)。 (也可以直接将'–'
直接放入代码中 - 在某些情况下更具可读性,但在这种情况下,读者看起来与“ - ”或“ - ”几乎相同)。
我还将字符串替换替换为稍微快一点的字符替换(在这种情况下,至少,您用一个字符替换单个字符)。
采取这种方法,你的代码就变成:
formattedString = formattedString.Replace('\u2013', '-');
formattedString = formattedString.Replace('\u2014', '-');
formattedString = formattedString.Replace('\u2015', '-');
即使有少更换为3,这很可能比做一个通所有这些替代品(我不打算效率较低做一个测试来找出需要多长时间,超过一定的数字,即使对于只有几个字符的字符串,使用单次传递也变得更有效率)。一种方法是:
StringBuilder sb = new StringBuilder(formattedString.length);//we know this is the capacity so we initialise with it:
foreach(char c in formattedString)
switch(c)
{
case '\u2013': case '\u2014': case '\u2015':
sb.Append('-');
default:
sb.Append(c)
}
formattedString = sb.ToString();
(另一种可能性是,以检查是否(int)c >= 0x2013 && (int)c <= 0x2015
但在分行数目减少是小的,不着边际的话你最期待的人物都没有数值接近对方)。使用各种变体(例如,如果formattedString要在某个时刻输出到流中,最好在获取每个最终字符时进行,而不是再次缓冲)。
请注意,此方法在您的搜索中不处理多字符串,但可以在输出中使用字符串,例如我们可能包括:
case 'ß':
sb.Append("ss");
现在,这是比以前更有效率,但一定数量的更换情况后,仍然变得很困难。它还涉及许多分支机构,这些分支机构都有自己的绩效问题
让我们考虑一下相反的问题。假设您想转换仅来自US-ASCII范围的来源的字符。你将只有128个可能的字符,所以你的方法可能是:
char[] replacements = {/*list of replacement characters*/}
StringBuilder sb = new StringBuilder(formattedString.length);
foreach(char c in formattedString)
sb.Append(replacements[(int)c]);
formattedString = sb.ToString();
现在,这是不使用Unicode,其中有超过范围会从0到1114111.然而分配109,000个字符的实际,没准你关心的角色不仅比这个小得多(如果你真的关心很多情况,你会想要上面给出的方法),而且在相对受限的块中。
还可以考虑如果你不特别关心任何代理人(我们会在后面介绍)。那么,大多数人物你根本不关心,那么,让我们来看看这个:
char[] unchanged = new char[128];
for(int i = 0; i != 128; ++i)
unchanged[i] = (char)i;
char[] error = new string('\uFFFD', 128).ToCharArray();
char[] block0 = (new string('\uFFFD', 13) + "---" + new string('\uFFFD', 112)).ToCharArray();
char[][] blocks = new char[8704][];
for(int i = 1; i != 8704; ++i)
blocks[i] = error;
blocks[0] = unchanged;
blocks[64] = block0;
/* the above need only happen once, so it could be done with static members of a helper class that are initialised in a static constructor*/
StringBuilder sb = new StringBuilder(formattedString.Length);
foreach(char c in formattedString)
{
int cAsI = (int)c;
sb.Append(blocks[i/128][i % 128]);
}
string ret = sb.ToString();
if(ret.IndexOf('\uFFFD') != -1)
throw new ArgumentException("Unconvertable character");
formattedString = ret;
之间是否是更好地测试在最后一气呵成的uncovertable字符(如上)的余额或每个根据这种情况发生的可能性,转换会有所不同。如果你能确定(由于你的数据的知识),它肯定会更好,并且可以删除该检查 - 但你必须确实确实是。
这里的好处是,虽然我们使用查找方法,但我们只占用384个字符的内存来保存查找(还有一些更多用于数组开销),而不是109,000个字符'价值。其中的块的最佳尺寸根据您的数据而不同(也就是您想要制作的替代品),但假设存在彼此相同的块往往会成立。
现在,最后,如果您关心在.NET中内部使用的UTF-16中表示为代理对的“星座飞机”中的某个角色,或者如果您关心将某些多字符字符串替换为一种特殊的方式?
在这种情况下,您可能必须至少在开关中读取一个或更多字符(如果在大多数情况下使用块方法,则可以使用不可转换的情况来指示此类工作是需要)。在这种情况下,可能需要转换为US-ASCII,然后从System.Text.Encoding
以及EncoderFallback
和EncoderFallbackBuffer
的自定义实现转换回US-ASCII,并在那里处理它。这意味着大多数转换(明显的情况)将为您完成,而您的实现只能处理特殊情况。
听起来类似于http://stackoverflow.com/questions/2624646/convert-unicode-char-to -closest-most-similar-char-in-ascii-net – 2011-01-21 16:29:59