2023年10月11日 星期三

在BAT中如何做 Ascii 文本的變換 更換換行符號 (Linux -> Win)

在BAT中如何做 Ascii 文本的變換 更換換行符號 (Linux -> Win)

反過來 dos2Unix 請參考這篇
CHG: 在BAT中如何做 Ascii 文本的變換 更換換行符號 (Win -> Linux) 



Bat能做的事情非常有限,這邊最有效率的辦法是直接呼叫老大哥 PowerShell 來直接擺平這事情。

廢話不多說直接上代碼一行而已

@echo off& Title EOLConverter By Charlotte
echo D:\UnixTextFile.txt | powershell -nop "[string]$fle=$input; if(-not(Test-Path $fle)){Write-Error 'Path is not exist' -ea 1}; $tmp=$fle+'.tmp'; $enc=[Text.Encoding]::Default; $red=New-Object IO.StreamReader($fle,$enc); $wrt=New-Object IO.StreamWriter($tmp,$false,$enc); while(-not $red.EndOfStream){$wrt.WriteLine($red.ReadLine())}; $red.Close();$wrt.Close(); mv $tmp $fle -fo -ea 1"
exit /b %errorlevel%

力求簡約一行直接幹掉,相當於引用一個外部二禁制檔案的概念,內容就不用太糾結了有興趣自己拆行出來看馬上能懂得

不過功能簡單只有針對系統語言編碼做轉換,非系統語言編碼或是涉及到兩種編碼轉換的,自行修改內容吧


內容做的是從Linux轉換到Windwos反過來的自行看著代碼推敲吧





2023-10-16

後來想想發現一個問題是,上面的方法有個致命問題是跟編碼扯上關係了,編碼錯了轉換就錯了,為了撤掉他們相關只能使用 Binary 的方式讀取

一個字符一個字符讀取不解析其具體字符,當遇到 ascii 編碼 10 也就是換行符號時檢查前面有沒有 13 沒的話給他加一下。

不過這帶來另一個更致命的問題是 PowerShell 速度慢到哭,這可扛不起一個字符一個字符讀取,取而代之再乎叫更大的大哥 C# 來做事了


先上在 PowerShell 上的執行方法

$EOLConverter = @"
using System.IO;
public class EOLConverter
{
    public static void Unix2dos(string sourcePath, string destinationPath)
    {
        const int readBufferSize = 16384;
        byte[] readBuffer = new byte[readBufferSize];
        byte[] writeBuffer = new byte[readBufferSize * 2];
        byte previousByte = 0;
        using (FileStream readStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
        using (FileStream writeStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write))
        {
            int bytesRead;
            while ((bytesRead = readStream.Read(readBuffer, 0, readBufferSize)) > 0)
            {
                int writeIndex = 0;
                for (int i = 0; i < bytesRead; i++)
                {
                    if (readBuffer[i] == 10 && previousByte != 13)
                    {
                        writeBuffer[writeIndex++] = 13;
                    }
                    writeBuffer[writeIndex++] = readBuffer[i];
                    previousByte = readBuffer[i];
                }
                writeStream.Write(writeBuffer, 0, writeIndex);
            }
        }
    }
}
"@

Add-Type -TypeDefinition $EOLConverter
[EOLConverter]::Unix2dos("LF.txt", "CRLF.txt")

這個 Add-Type 說穿了其實就是動態編譯 C# 後馬上拿來用


再來把他到 Bat 上實現一連串連續技黑魔法

@echo off& Title EOLConverter By Charlotte
set "EOLConverter.cs=using System.IO; public class EOLConverter { public static void Unix2dos(string sourcePath, string destinationPath) { const int readBufferSize = 16384; byte[] readBuffer = new byte[readBufferSize]; byte[] writeBuffer = new byte[readBufferSize * 2]; byte previousByte = 0; using (FileStream readStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read)) using (FileStream writeStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write)) { int bytesRead; while ((bytesRead = readStream.Read(readBuffer, 0, readBufferSize)) > 0) { int writeIndex = 0; for (int i = 0; i < bytesRead; i++) { if (readBuffer[i] == 10 && previousByte != 13) { writeBuffer[writeIndex++] = 13; } writeBuffer[writeIndex++] = readBuffer[i]; previousByte = readBuffer[i]; } writeStream.Write(writeBuffer, 0, writeIndex); } } } }"
echo D:\UnixTextFile.txt| powershell -nop "Add-Type -TypeDefinition '%EOLConverter.cs%'; [string]$SrcPath=$input; $DstPath=$SrcPath+'.tmp'; [EOLConverter]::Unix2dos($SrcPath, $DstPath); #Move-Item -Path $DstPath -Destination $SrcPath -Force -ErrorAction Stop"
exit /b %errorlevel% 

如此一來就可以完成轉換了

具體行為會將結果存到 D:\UnixTextFile.txt.tmp,要覆蓋原檔請自行刪除第3行後面 #Move-Item 中註解的井號,這邊會把檔案重新命名覆蓋回去。


注意檔案路徑 "D:\UnixTextFile.txt" 盡量用絕對路徑,由於沒有做同步化工作路徑的處理,後面展開的 PowerShell 以及 C# 的工作路徑無法保證是一致的。


已知這種寫法有個問題是他不支援W字元,就是UFT16這類的就沒轍了,只能處理UFT8類的普通文本了 (第一種寫法倒是可以改對編碼就好)



最後真的要完美參考這份開源檔案
dos2unix: unix2dos.c | Fossies

編譯好的檔案
Download dos2unix-7.5.1-win64.zip (dos2unix) (sourceforge.net)

這Unix上的軟體基本上有什麼問題應該都早被修正了,可以當作無Bug來看待

中文使用手冊
dos2unix 7.5.1 - DOS/Mac - Unix文件格式转换器 (xs4all.nl)



沒有留言:

張貼留言