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

C# 如何快速替换数组中的字符,c#,optimization,C#,Optimization,我在XML文件上使用XML文本读取器,该文件可能包含对读取器无效的字符。我最初的想法是创建自己版本的流阅读器,并清除坏字符,但这严重减慢了我的程序 public class ClensingStream : StreamReader { private static char[] badChars = { '\x00', '\x09', '\x0A', '\x10' }; //snip public override int Read(char[] buf

我在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;
        }
}
公共类ClensingStream:StreamReader
{
私有静态字符[]badChars={'\x00','\x09','\x0A','\x10'};
//剪断
公共重写整型读取(字符[]缓冲区,整型索引,整型计数)
{
var tmp=base.Read(缓冲区、索引、计数);
for(int i=0;i

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

使用
开关
语句,您可能会得到更好的结果:

switch (buffer[i])
{
    case '\x00':
    case '\x09':
    case '\x0A':
    case '\x10':
        buffer[i] = ' ';
        break;
}
JIT编译器应在运行时将其编译为快速代码。见鬼,编译器可能也会接近。您也不需要以这种方式进行方法调用。

您可以使用该方法进行优化。将文本读入字符串,然后与正则表达式中的字符一起使用


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

您只需检查读取字符,就可以检查它的优化程度

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

不知道这是否/有多大帮助,您必须分析您的实际应用程序以检查

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

它在该行花费大量时间的原因是
Contains
方法在数组中循环查找字符

将字符放在
哈希集中

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

如果您有更多的字符(五个或六个IIRC),编译器实际上会创建一个哈希表来查找大小写,这与使用
哈希集类似,您可以使用布尔数组

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);
char[]badChars={'\x00','\x09','\x0A','\x10'};
char maxChar=badChars.Max();
Assert(maxChar<256);
bool[]badCharsTable=新bool[maxChar+1];
ForEach(badChars,ch=>badCharsTable[ch]=true);
并将
badChars.Contains(…)
替换为
(ch


编辑:终于有时间改进答案了。

你用
tmp
做什么?“'badChars.Contains()”是什么样子的?我认为最好看看它所需要的总时间,而不是与其他东西进行比较。它会增加那么多额外的时间吗?@Tobias:我想这是
IEnumerable
上的LINQ实现。请注意,应该循环到
tmp
,而不是
缓冲区。长度
。缓冲区只包含有效数据,直到
tmp
所指示的点为止,因此在缓冲区的其余部分循环是浪费时间的。如果必须计时,我会尝试带有If(buffer[I]的变量经过测量,在没有调试器的情况下,发布速度提高了20%。这会对非ASCII字符产生问题。是的,这是真的。该示例只包含ASCII字符,因此我认为这种方法在这里是安全的。这非常不节省空间。哈希表在任何时候都比稀疏数组快。@Kugel:这取决于大小。您的h表可能与ASCII查找的整个表一样多,而且查找表的速度更快。请记住,哈希表是一个查找表,只是您查找的是哈希而不是值,您得到的是一个指向具有该哈希的值列表的指针,然后您必须按顺序搜索。指针比
bool大得多
@mgronber:使用小的查找表是安全的。但是,强制转换
(byte)ch
会产生误报。最好进行范围检查,而不是屏蔽高字节。例如
(ushort)ch I也在忙着写同样的东西。如果你使用的是.NET 3.5或更高版本,或者是rip System.Core of Mono并将其重新编译为2.0,由于查找的复杂性,HashSet是一个不错的选择。将其更改为hash set将运行时间从35秒更改为4秒。
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);