﻿# 魔鬼般的細節：使用 C# 的 String.Trim() 方法刪除空白字元

昨天同事提到一個網後台內容上稿的問題，客戶反映一個 Bug 說有網站的連結發生問題，點進去之後會發生 **A potentially dangerous Request.Path value was detected from the client (:)** 的錯誤訊息，查了好一段時間後終於發現在從後台上稿的內容中，該網址的最前面原來有一個 Unicode 的不可見字元 ([U+200B](http://www.fileformat.info/info/unicode/char/200B/index.htm))，導致網址錯誤，所以瀏覽器當然無法解析該網址。當在思考著如何防呆的時候，卻一時找不到一個漂亮的解決方法，這才發現到一個 [String.Trim()](http://msdn.microsoft.com/en-us/library/d4tt83f9.aspx) 魔鬼般的細節。

我們先來看看滑鼠點下這個錯誤連結後所出現的畫面，如下圖示（點圖可放大）：

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/07cb16cc69bd7ef3a171-image_thumb_195.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/96162175710413b19d34-image_2407.png)

打開網頁原始碼，也**看不出**這個超連結的網址到底有什麼異狀（該網站用 AngularJS 製作才有 [ng-href](http://docs.angularjs.org/api/ng.directive:ngHref) ）

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/2017b9a5a8463860fa3b-image_2408.png)

進一步調查後發現，該網址最前方原來出現了一個 Unicode 字元（ZERO WIDTH SPACE）( U+200B )

-   [Unicode Character 'ZERO WIDTH SPACE' (U+200B)](http://www.fileformat.info/info/unicode/char/200B/index.htm) 

這是一個所謂的「**零寬度空白字元**」 ( 英文簡稱 **ZWSP** )，一個很特殊的空白字元，通常用於排版用途，因為他的**零寬度**特性，所以不但可以用來間隔文字與文字之間，還能讓排版軟體透過 **左右對齊** (justification) 時可以自動調整文字之間的間距。

我們就很好奇，到底客戶是怎樣上稿的，可以輸入到這個特別的文字？我研究了一下發現，在 Thai (泰文)、Khmer (高棉文) 與 Myanmar (緬甸文) 語系中，預設輸入空白時，就是用 [U+200B](http://www.fileformat.info/info/unicode/char/200B/index.htm) 這個字元做文字間格的（在他們的鍵盤配置裡預設就是這樣）。當然，我們的客戶不太可能輸入這三種語系或用這種鍵盤配置。我想到另一種可能是他們上稿的時候，是從排版軟體複製網址下來的，以至於複製到這個 **ZWSP** 字元而不自知。  
（註：看網路說好像在 Mac 作業系統可透過 **Shift+Space** 輸入 **ZWSP** 字元，但我無法確定！）

總之，這字元的由來，已經不可考，現在能想的是怎樣落實防呆措施，下次客戶輸入的時候可以幫他們過濾掉這個不可見字元。

我首先嘗試利用 [String.Trim()](http://msdn.microsoft.com/en-us/library/d4tt83f9.aspx) 清除這個「**零寬度空白字元**」，結果竟然刪不掉。透過 [Regex](http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx) 也想不出更完美的解決方法，因為會擔心是否有可能出現任何其他的空白字元。

查詢 MSDN 之後，發現在 Remarks (備註) 的段落中，出現一些關於該字元的相關說明，大概的意思是這樣的：

> 在 .NET Framework 3.5 SP1 與先前的版本，[String.Trim()](http://msdn.microsoft.com/en-us/library/d4tt83f9.aspx) 方法在不傳入參數或傳入空陣列的時候，在內部會維護一份「空白字元」的清單，在移除空白字元時會包含 [ZERO WIDTH SPACE (U+200B)](http://www.fileformat.info/info/unicode/char/200B/index.htm) 與 [ZERO WIDTH NO-BREAK SPACE (U+FEFF)](http://www.fileformat.info/info/unicode/char/FEFF/index.htm) 字元。但從 .NET Framework 4.0 以後版本，變更為判斷 [Char.IsWhiteSpace()](http://msdn.microsoft.com/en-us/library/system.char.iswhitespace.aspx) 方法的回傳結果，如果回傳為 true 才會清除該空白字元，可惜這兩個字元在 [Char.IsWhiteSpace()](http://msdn.microsoft.com/en-us/library/system.char.iswhitespace.aspx) 方法的回傳結果卻為 false，也代表使用 [String.Trim()](http://msdn.microsoft.com/en-us/library/d4tt83f9.aspx) 方法時，不會刪除這兩個字元。

所以，如果我們要刪除這兩個字元，可能會用以下解法：

-   先刪除 [Char.IsWhiteSpace()](http://msdn.microsoft.com/en-us/library/system.char.iswhitespace.aspx) 方法認得的空白字元，再刪除這兩個字元
    
    ```
    var result = str.Trim().Trim(new char[] { '\uFEFF', '\u200B' });
    ```
    
-   先刪除這兩個字元，再刪除 [Char.IsWhiteSpace()](http://msdn.microsoft.com/en-us/library/system.char.iswhitespace.aspx) 方法認得的空白字元
    
    ```
    var result = str.Trim(new char[] { '\uFEFF', '\u200B' }).Trim();
    ```
    
-   僅刪除空白字元與這兩個字元 ( 不含 Unicode 空白字元與 ASCII 控制字元 )
    
    ```
    var result = str.Trim(new char[] { '\uFEFF', '\u200B', ' ' });
    ```
    
-   最後則是我[依據 MSDN 文件](http://msdn.microsoft.com/en-us/library/system.char.iswhitespace.aspx)所列出的**空白字元清單**加上本文提到的這兩個空白字元的完整版本
    
    ```
    char[] all_whitespaces = new char[] {    // SpaceSeparator category    '\u0020', '\u1680', '\u180E', '\u2000', '\u2001', '\u2002', '\u2003',     '\u2004', '\u2005', '\u2006', '\u2007', '\u2008', '\u2009', '\u200A',     '\u202F', '\u205F', '\u3000',    // LineSeparator category    '\u2028',    // ParagraphSeparator category    '\u2029',    // Latin1 characters    '\u0009', '\u000A', '\u000B', '\u000C', '\u000D', '\u0085', '\u00A0',    // ZERO WIDTH SPACE (U+200B) & ZERO WIDTH NO-BREAK SPACE (U+FEFF)    '\u200B', '\uFEFF'};var result = str.Trim(all_whitespaces);
    ```
    

最後補充一下，我們常在 PowerPoint 中按 **Shift+Enter** 來輸入**強制斷行**，這個強制斷行所產生的是一個 [VT (vertical tabulation)](http://www.fileformat.info/info/unicode/char/000B/index.htm) 字元，這個字元基本上也是個空白字元，透過 [String.Trim()](http://msdn.microsoft.com/en-us/library/d4tt83f9.aspx) 是可以清除掉字串開頭或結尾的 VT 字元。不過，如果你希望客戶從後台上稿時，可能會從 PowerPoint 文件中複製包含斷行符號的內容時，記得要將 VT 字元替換成 CRLF ( \\r\\n ) 字元，否則在顯示上可能會出問題。

另外，現在除了 IE6 之外，瀏覽器都支援 **ZWSP** 字元，我們以 Wikipedia 上的 [Zero-width space](http://en.wikipedia.org/wiki/Zero-width_space) 頁面為例，在該網頁上就用了不可見的 **ZWSP** 字元，這個字元你從檢視網頁原始碼也看不到，要用 HEX 十六進制的編輯器才可以看的見。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/af80455948fadde3c862-image_2409.png)

除了直接在文件中輸入 **ZWSP** 零寬度字元外（你可以透過 **Alt+8203** 輸入該字元）\[[參考文章](/post/2010/07/04/Windows-typing-Unicode-character-symbol-tips.aspx)\]，你也可以透過 HTML Entity 的 **&#8203;** 設定在網頁中，這只是 **ZWSP** 零寬度字元的另一種表示法，這兩個都是跨瀏覽器支援的，相容性沒什麼問題。雖然你也可以透過 [<wbr> 標籤](http://www.w3schools.com/tags/tag_wbr.asp)來設定 **ZWSP** 零寬度字元，不過即便到了 IE11 也都還不支援 [<wbr> 標籤](http://www.w3schools.com/tags/tag_wbr.asp)，所以建議各位不要使用這個 **<wbr>** 標籤當成 **ZWSP** 零寬度字元。

### 相關連結

-   [Zero-width space - Wikipedia, the free encyclopedia](http://en.wikipedia.org/wiki/Zero-width_space)
-   [How do I remove zero width characters from an excel file?](http://superuser.com/questions/658125/how-do-i-remove-zero-width-characters-from-an-excel-file)
-   [Unicode spaces](https://www.jkorpela.fi/chars/spaces.html)
-   [ZERO WIDTH SPACE (U+200B)](http://www.fileformat.info/info/unicode/char/200B/index.htm)
-   [ZERO WIDTH NO-BREAK SPACE (U+FEFF)](http://www.fileformat.info/info/unicode/char/FEFF/index.htm)
-   [Unicode Character 'LINE TABULATION' (U+000B)](http://www.fileformat.info/info/unicode/char/000B/index.htm)
-   [String.Trim Method (Char\[\]) (System)](http://msdn.microsoft.com/en-us/library/d4tt83f9.aspx)
-   [Char.IsWhiteSpace Method  (System)](http://msdn.microsoft.com/en-us/library/system.char.iswhitespace.aspx)
-   [利用【 Alt + 數字鍵 】可輸出任意 Unicode 字元與符號](/post/2010/07/04/Windows-typing-Unicode-character-symbol-tips.aspx)
