2011-03-10 57 views
4

我在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]))是什麼做到這一點,所以我不會引起可怕緩慢的正確方法?

+0

你用'tmp'做什麼? – 2011-03-10 15:31:14

+0

'badChars.Contains()'是什麼樣的? – 2011-03-10 15:31:54

+2

我認爲最好能夠看到它需要的時間總量,而不是與其他任何事物相比較。它是否強加這麼多額外的時間? – 2011-03-10 15:33:03

回答

8

它在該行中花費太多時間的原因是因爲Contains方法遍歷數組來查找字符。

把人物的HashSet<char>代替:

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

的代碼檢查,如果集包含的字符看上去一樣尋找數組中的時候,但它使用的字符的哈希碼看而不是遍歷數組中的所有項目。

或者,你可以把人物的開關,這樣編譯器會創建一個有效的比較:

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

如果你有更多的字符(五六IIRC),編譯器將實際創建散列表來查找案例,所以這將類似於使用HashSet

+0

我正忙着寫同樣的東西。如果您使用的是.NET 3.5或更高版本,或者將Mono中的System.Core拆分並重新編譯爲2.0,則由於查找複雜性,HashSet 是要走的路。 – 2011-03-10 15:47:26

+0

將其更改爲散列集將運行時間形式從35秒改爲4秒。 – 2011-03-10 16:01:35

4

你可能有一個switch陳述更好的結果:

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

這應該在運行時的JIT編譯器編譯成快速的代碼。哎呀,編譯器也可能會關閉。你不需要這種方法調用。

+0

如果我不得不計時,我會嘗試一個帶有if(buffer [i] <=(char)0x10)的變體,因此99%的時間只進行一次比較(如果開關不是沒有轉換爲哈希表或類似的) – xanatos 2011-03-10 15:41:23

+0

測量,在沒有調試器的情況下發布速度提高20%。 – xanatos 2011-03-10 15:49:11

0

您可以使用regular expressions進行優化。將文本讀入一個字符串,然後在正則表達式中使用Replace

但是,你的代碼對我來說看起來也很好,我想正則表達式除了搜索文本之外也不能做其他任何事情......而且你需要在那裏使用一個你不需要做的事情的字符串其他選項。

0

你可以檢查以及它如何與剛檢查讀出的字符,從而可以優化

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

不知道/多少,這會幫助你,你必須分析您的實際應用程序檢查

0

嘗試將char[]轉換爲字符串,然後使用IndexOfAny

0

你可以使用一個布爾數組

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(...)

編輯:終於有時間來改善答案。

+0

這將會遇到非ASCII字符的問題。 – 2011-03-10 15:42:38

+0

是的,的確如此。這個例子只包含ASCII字符,所以我認爲這種方法在這裏是安全的。 – mgronber 2011-03-10 15:46:43

+0

這太空間效率很低。哈希表隨時可以跳過稀疏數組。 – Kugel 2011-03-10 15:49:17