今天遇到一個奇妙的情況,同事上傳的檔案內容出現一個奇妙的字元亂碼,上傳的程式修正了,但已經上傳的檔案內容需要移除那個奇妙的字元,因此想用 PowerShell 寫一隻指令來處理看看,順便也補充一下 PowerShell 知識。

前情提要

該檔案使用 UCS-2 LE BOM 編碼,其實這相當於 UTF-16 編碼(兩者的關係參考這裡),再加上使用 little-endian 的排序方式。

之所以會出現奇妙的字元,是因為每此 Append 內容進檔案的時候,把編碼標頭也 Append 上去,這就造成了亂碼。

亂碼 \ufeff

這個亂碼其實是個 \ufeff 字元,只是它顯示不出來。

處理幾件小事情

要調整的檔案數量不在少數,要修改成千上百個檔案可不是開玩笑的。

第一步當然就是取得所有檔案的路徑,在 PowerShell 的環境中取得指定資料夾底下的所有檔案路徑算簡單的,Get-ChildItem -Path "YOUR_FOLDER_PATH" -File 一行搞定。

第二步就是逐一打開前一步取得的檔案內容,然後替換掉指定的字元,這裡稍微不好處理,PowerShell 沒有提供直接取代檔案內容的 Cmdlet,為了讓指令盡可能單純,我選擇這樣的解法:

(Get-Content -path "FILE_PATH" -Raw) -replace "SEARCH_TARGET", "REPLACE_VALUE"

先將該檔案內容用 Get-Content 取出,然後這個值當然就是個字串,這時再使用字串的 Replace 功能,這樣指令就相當簡潔了。

第三步就是把還存在記憶體中的修改後字串,存進原本的檔案中,這只需要用到 Set-Content 這支 Cmdlet 就搞定了!不過有一點要注意的是,預設會使用 UTF-8 without BOM 的編碼,前情提要有講到編碼要用 UCS-2 LE BOM,因此要再指定 Set-Content-Encoding 參數為 unicode

把二、三部的指令用 | 管線符號串起來,就差不多完成囉!

(Get-Content -path "FILE_PATH" -Raw) -replace "SEARCH_TARGET", "REPLACE_VALUE" | Set-Content -Path "FILE_PATH" -Encoding unicode

完整的 Function

寫成 PowerShell Function 就是下面這樣囉,開放三個參數以便後續使用,其中第三個 Replace 的參數要稍微注意一下,因為有可能是要替換成空字串,因此這個參數需要掛上 [AllowEmptyString()] 讓它可以接受空字串,不然執行的時候可是會報錯的。

function Update-FileContent {
    Param(
        [Parameter(
            Mandatory = $true,
            Position = 0,
            HelpMessage = "Folder path"
        )]
        [string]$Path,
        [Parameter(
            Mandatory = $true,
            Position = 1,
            HelpMessage = "Search content"
        )]
        [string]$Search,
        [Parameter(
            Mandatory = $true,
            Position = 2,
            HelpMessage = "Replace to"
        )]
        [AllowEmptyString()]
        [string]$Value
    )

    $files = Get-ChildItem -Path $Path -File
    foreach ($file in $files) {
        Write-Verbose -Message "Replace From: $file.FullName"
        ((Get-Content -path $file.FullName -Raw) -replace $Search, $Value) | Set-Content -Path $file.FullName -Encoding unicode
    }
}

在 Function 裡面用到了 Write-Verbose 這個 Cmdlet,這是當執行此指令時,有加上 -Verbose 參數才會輸出該段訊息。


參考資料:


Poy Chang

Trial and Error