如何快速替换数组中的字符

我在XML文件上使用XML文本阅读器,该文件可能包含对读者无效的字符。 我最初的想法是创建我自己的流阅读器版本并清除坏角色,但它严重减慢了我的程序。

public class ClensingStream : StreamReader { private static char[] badChars = { '\x00', '\x09', '\x0A', '\x10' }; //snip public override int Read(char[] buffer, int index, int count) { var tmp = base.Read(buffer, index, count); for (int i = 0; i < buffer.Length; ++i) { //check the element in the buffer to see if it is one of the bad characters. if(badChars.Contains(buffer[i])) buffer[i] = ' '; } return tmp; } } 

根据我的探查器,代码花费88%的时间在if(badChars.Contains(buffer[i]))这样做的正确方法是什么,所以我不会造成可怕的缓慢?

它在该行中花费如此多时间的原因是因为Contains方法遍历数组以查找该字符。

将字符放在HashSet

 private static HashSet badChars = new HashSet(new char[] { '\x00', '\x09', '\x0A', '\x10' }); 

检查集合是否包含字符的代码与查看数组时的代码相同,但它使用字符的哈希码来查找它,而不是循环遍历数组中的所有项目。

或者,您可以将字符放在开关中,这样编译器就可以创建有效的比较:

 switch (buffer[i]]) { case '\x00': case '\x09': case '\x0A': case '\x10': buffer[i] = ' '; break; } 

如果你有更多的字符(五个或六个IIRC),编译器实际上会创建一个哈希表来查找案例,这与使用HashSet类似。

使用switch语句可能会有更好的结果:

 switch (buffer[i]) { case '\x00': case '\x09': case '\x0A': case '\x10': buffer[i] = ' '; break; } 

这应该由JIT编译器在运行时编译为快速代码。 哎呀,编译器可能也会接近。 您也不需要以这种方式调用方法。

您可以使用正则表达式来进行优化。 将文本读入字符串,然后使用Replace为正则表达式中的字符。

但是,你的代码对我来说也很好看,我猜正则表达式除了搜索你的文本之外也做不了什么……你需要在那里拿一个你不需要用其他选项做的字符串。

你可以通过检查读取字符来检查它的优化程度

 for (int i = index; i < index + count; i++){ //etc } 

不知道这对你有多大帮助,你需要分析你的真实应用程序来检查

尝试将char[]转换为字符串,然后使用IndexOfAny

你可以使用布尔数组

 char[] badChars = { '\x00', '\x09', '\x0A', '\x10' }; char maxChar = badChars.Max(); Debug.Assert(maxChar < 256); bool[] badCharsTable = new bool[maxChar + 1]; Array.ForEach(badChars, ch => badCharsTable[ch] = true); 

并用(ch < badCharsTable.Length && badCharsTable[ch])替换badChars.Contains(...) (ch < badCharsTable.Length && badCharsTable[ch])

编辑:最后有时间改进答案。