以下 Powershell 命令無法復制整個檔案;最后總是缺少一些字符。
[System.IO.StreamWriter]::new('C:\TEMP\b.csv', [System.Text.Encoding]::UTF8).Write([System.IO.StreamReader]::new('C:\Temp\a.csv', [System.Text.Encoding]::GetEncoding('iso-8859-1')).ReadToEnd())
我懷疑這是因為作者沒有重繪 最后一位,因為這確實復制了整個檔案:
$X = [System.IO.StreamReader]::new('C:\Temp\a.csv', [System.Text.Encoding]::GetEncoding('iso-8859-1'))
$Y = [System.IO.StreamWriter]::new('C:\TEMP\b.csv', [System.Text.Encoding]::UTF8)
$Y.Write($X.ReadAll())
$X.Dispose()
$Y.Dispose()
是否可以在沒有創建變數來參考它們的情況下處理(和重繪 )讀取器和寫入器?
編輯:我嘗試了使用流讀取器/寫入器的單線器,希望讀取器的讀取緩沖區會直接傳輸到寫入器的寫入緩沖區,而不是等待讀取器將整個檔案讀入記憶體然后寫入。什么技術可以做到這一點?
我個人發現不宣告一次性物件的代碼通常更簡潔/更簡潔,但我的重點是了解物件是否/如何處理自己,而不是樣式。
沒有必要避開變數或寫在一行上,但這種行為不是我所期望的。在 VBA 中,可以像這樣復制檔案并相信它會正確處理自己,而無需宣告變數并顯式重繪 (我認為)。
Sub Cpy()
With New Scripting.FileSystemObject
.CreateTextFile("c:\Temp\Out.txt").Write .OpenTextFile("C:\Temp\In.txt", ForReading).ReadAll
End With
End Sub
通過在程序中撰寫適當的“清理”代碼,可以在自定義 VBA 類中實作類似的行為Class_Terminate()。我假設一旦該行執行并且不再有與之關聯的變數,Streamwriter 將在終止時通過垃圾收集類似地重繪 資料。
我還注意到該檔案仍處于鎖定狀態,在關閉 powershell 會話之前我無法洗掉它。有沒有辦法在沒有宣告要使用的變數的情況下重繪 內容并釋放檔案?
uj5u.com熱心網友回復:
System.IO.File只是為了向您展示,使用 和 的靜態方法,這是可能的,WriteAllText()而且更容易做到ReadAllText()。
以下查詢https://loripsum.net/iso-8859-1 API 以獲取隨機段落并使用編碼寫入檔案。然后讀取該檔案并使用相同的編碼寫入副本,最后比較兩個檔案哈希。正如你所看到的,閱讀和寫作都是單線完成的。
可以洗掉陳述句,using但您需要使用完整型別名稱。
將位置設定為臨時檔案夾以進行測驗。
using namespace System.IO
using namespace System.Text
$fileRead = [Path]::Combine($pwd.Path, 'test.txt')
$fileWrite = [Path]::Combine($pwd.Path, 'test-copy.txt')
$loremIpsum = Invoke-RestMethod 'https://loripsum.net/api/5/short/headers/plaintext'
[File]::WriteAllText($fileWrite, $loremIpsum, [Encoding]::GetEncoding('iso-8859-1'))
[File]::WriteAllText(
$fileWrite,
[File]::ReadAllText($fileRead, [Encoding]::GetEncoding('iso-8859-1')),
[Encoding]::GetEncoding('iso-8859-1')
)
(Get-FileHash $fileRead).Hash -eq
(Get-FileHash $fileWrite).Hash # => Should be True
uj5u.com熱心網友回復:
對于給出的特定用例,Santiago Squarzon 的有用答案確實是最好的解決方案:使用靜態類的靜態方法
System.IO.File消除了對表示需要呼叫方法或顯式處理的檔案的實體的需要。.Close()要延遲讀取并因此支持逐行重疊讀取和寫入,您可以使用靜態和方法,但請注意,這種方法 (a) 總是在輸出檔案中使用平臺原生格式換行符,而不管換行符格式是什么輸入檔案使用,并且 (b) 總是以這種格式添加一個尾隨換行符,即使輸入檔案沒有尾隨換行符。
[System.IO.File]::ReadLines()[System.IO.File]::WriteAllLines()[Environment]::NewLine克服這些限制需要使用較低級別的原始位元組 API,
System.IO.FileStream這同樣需要顯式處理(參見底部)。
鑒于您的方法首先將整個輸入檔案讀入記憶體然后寫入,您甚至可以使用 PowerShell cmdlet,假設您正在運行PowerShell (Core) 7 ,它默認寫入無 BOM 的 UTF-8 檔案,并且其
-Encoding引數接受任何支持的編碼,例如在您的情況下為 ISO-8859-1:# PowerShell (Core) 7 only Get-Content -Raw -Encoding iso-8859-1 C:\TEMP\a.csv | Set-Content -NoNewLine C:\TEMP\b.csv
至于你的一般問題:
從 PowerShell(核心)7.2.1 開始:
PowerShell沒有與 C#
using陳述句等效的構造,該陳述句允許自動處置其型別實作System.IDisposable介面的物件(在檔案 I/O API 的情況下,隱式關閉檔案)。GitHub issue #9886討論了添加這樣的宣告,但討論表明它可能不會被實施。
注意:雖然 PowerShell 確實有一系列以關鍵字開頭的陳述句
using,但它們有不同的用途 - 請參閱概念about_Using幫助主題。
未來的PowerShell 版本將支持在
clean { ... }cleanup { ... }高級函式或腳本終止時自動呼叫的(或)塊,這允許執行任何必要的函式腳本級清理(物件的處置) - 請參閱RFC #294。
IDisposable是否從終結.Dispose()器呼叫方法取決于實作介面的每種型別。只有這樣,垃圾收集器才會最終自動處理掉一個物件。
對于System.IO.StreamWriter較低級別的System.IO.FileStream類,情況似乎并非如此,因此在 PowerShell 中您必須顯式呼叫.Close()or ,這最好從//陳述句的塊中完成。.Dispose() finallytrycatchfinally
你可以通過結合物件構造和變數賦值來減少儀式,但是一個健壯的習語仍然需要很多儀式:
$x = $y = $null
try {
($y = [System.IO.StreamWriter]::new('C:\TEMP\b.csv', [System.Text.Encoding]::UTF8)).
Write(
($x = [System.IO.StreamReader]::new('C:\Temp\a.csv', [System.Text.Encoding]::GetEncoding('iso-8859-1'))).
ReadToEnd()
)
} finally {
if ($x) { $x.Dispose() }
if ($y) { $y.Dispose() }
}
一個輔助函式Use-Object(下面的源代碼)可以稍微緩解這個問題:
Use-Object
($x = [System.IO.StreamReader]::new('C:\Temp\a.csv',[System.Text.Encoding]::GetEncoding('iso-8859-1'))),
($y = [System.IO.StreamWriter]::new('C:\TEMP\b.csv', [System.Text.Encoding]::UTF8)) `
{ $y.Write($x.ReadToEnd()) }
Use-Object源代碼:
function Use-Object {
param(
[Parameter(Mandatory)] [array] $ObjectsToDispose,
[Parameter(Mandatory)] [scriptblock] $ScriptBlock
)
try {
. $ScriptBlock
} finally {
foreach ($o in $ObjectsToDispose) {
if ($o -is [System.IDisposable]) {
$o.Dispose()
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/414563.html
標籤:
