The following Powershell command fails to copy the entire file; a few characters are always missing from the end.
[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())
I suspect it's because the writer doesn't flush the last bits because this does copy the entire file:
$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()
Is it possible to dispose of (and flush) the reader & writer without having created variables to reference them?
EDIT: I tried this one-liner using streamreader/writer hoping the reader's read buffer would directly transfer to the writer's write buffer rather than waiting for the reader to read the entire file into memory and then write. What technique might achieve that?
I personally find code that does not declare a single-use object to often be cleaner / more succinct, but my focus is on understanding whether/how objects dispose of themselves, not the style.
There's no need to eschew variables or write on one line, but this behaviour isn't what I expected. In VBA one can copy a file like so and trust it will dispose of itself properly without having to declare a variable and explicitly flush (I think).
Sub Cpy()
With New Scripting.FileSystemObject
.CreateTextFile("c:\Temp\Out.txt").Write .OpenTextFile("C:\Temp\In.txt", ForReading).ReadAll
End With
End Sub
One can achieve similar behaviour in a custom VBA class by writing appropriate 'clean-up' code in a Class_Terminate() procedure. I assumed the Streamwriter would similarly flush data upon termination via garbage collection once the line executes and there's no longer a variable associated with it.
I also noticed that the file remains locked and I cannot delete it until I close the powershell session. Is there a way to flush contents and release the file without having declared a variable to work with?
Just to show you that this is possible, and easier to do, using the static methods of System.IO.File, WriteAllText() and ReadAllText().
The following queries the https://loripsum.net/ API to get random paragraphs and writes to a file using the iso-8859-1 encoding. Then reads that files and writes a copy using the same encoding and lastly compares both file hashes. As you can see reading and writing is all done as a one-liner.
The using statements can be removed but you would need to use the Full Type Names.
Set location to a temporary folder for testing.
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