从 Powershell 中使用 System.IO.FileStream.WriteByte() 时文件中出现意外字符
Unexpected characters in file when using System.IO.FileStream.WriteByte() from within Powershell
请考虑以下 Powershell 脚本:
Out-File -FilePath "c:\batch\vss\test.txt" -NoNewline -InputObject "0"
$Stream = [System.IO.File]::Open("c:\batch\vss\test.txt",
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::ReadWrite)
$Stream.WriteByte(49)
$Stream.Dispose()
执行脚本后,文件 c:\batch\vss\test.txt
包含意外字符。它包含以下 4 个字节(十六进制):
31 fe 30 00
从这些中,只有第一个是预期的 (0x31 = 49
)。 其他的是意想不到的。 [最后一句是错误的 - 请参阅更新/解决方案部分]
我已验证此文件在执行第一行脚本后正好包含一个字节(0x30
,即'0'
的ASCII码) .所以肯定是 .WriteByte()
添加了额外的字符。 [这个说法是错误的 - 请参阅更新/解决方案]
奇怪的是 .WriteByte()
部分按预期工作:显然,它覆盖了文件中的第一个字节,即脚本第一行 [=57= 之后的 0x30
], 0x31
.
但是为什么它会添加其他三个字节,我该如何防止呢?我是否在 Powershell 中以错误的方式使用了 .NET 库(这里是 Powershell 新手......)? [这个问题无效,因为 .WriteByte()
没有添加其他三个字节 - 请参阅更新部分/ 解决方案]
更新/解决方案
Mathias R. Jessen 的回答完全正确。但是,我想简短地解释一下为什么我自己没有看到这个(尽管之前知道字节顺序标记):
我结合使用 Notepad++ 和十六进制编辑器插件来调查发生了什么。显然,当文件被更改时,这个组合在更新打开文件的 HEX 视图方面存在问题,这就是为什么它让我走错了路。这是我第二次遇到这个问题,所以我以后一定会使用其他 HEX 编辑器。
答案写好后,我重新调查了一下,这次用的是HxD,一下子就明白了。
长话短说:这实际上不是 System.IO.FileStream.WriteByte()
的问题,而是用于调查的工具的问题。
解决方案很简单:如果我想要文件中的一个字节,我可以使用 Out-File
和另一种编码,或者我可以使用 .WriteByte()
首先创建文件。
在 Windows PowerShell 中,Out-File
默认为 little-endian UTF-16 编码(在 Windows 中俗称 Unicode
编码)。
当您使用值 "0"
执行 Out-File
时,它会将以下字节序列写入磁盘文件:
ff fe 48 00
\___/ \___/
| |
| UTF-16LE encoded "0"
UTF-16LE byte order mark
当你用[FileMode]::Open
调用[File]::Open()
时,它returns一个FileStream
对象指向最低偏移量文件,因此 WriteByte(49)
最终会覆盖 byte-order 标记的第一部分:
49 fe 48 00
|
Overwritten
如果您总是想覆盖现有文件中的任何数据,请使用 [FileMode]::Truncate
:
$Stream = [System.IO.File]::Open("c:\batch\vss\test.txt",
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::Truncate,
[System.IO.FileShare]::ReadWrite)
如果您想截断文件以手动删除多余的数据,请使用 SetLength
:
$Stream.SetLength(1)
请考虑以下 Powershell 脚本:
Out-File -FilePath "c:\batch\vss\test.txt" -NoNewline -InputObject "0"
$Stream = [System.IO.File]::Open("c:\batch\vss\test.txt",
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::Write,
[System.IO.FileShare]::ReadWrite)
$Stream.WriteByte(49)
$Stream.Dispose()
执行脚本后,文件 c:\batch\vss\test.txt
包含意外字符。它包含以下 4 个字节(十六进制):
31 fe 30 00
从这些中,只有第一个是预期的 (0x31 = 49
)。 其他的是意想不到的。 [最后一句是错误的 - 请参阅更新/解决方案部分]
我已验证此文件在执行第一行脚本后正好包含一个字节( [这个说法是错误的 - 请参阅更新/解决方案]0x30
,即'0'
的ASCII码) .所以肯定是 .WriteByte()
添加了额外的字符。
奇怪的是 .WriteByte()
部分按预期工作:显然,它覆盖了文件中的第一个字节,即脚本第一行 [=57= 之后的 0x30
], 0x31
.
但是为什么它会添加其他三个字节,我该如何防止呢?我是否在 Powershell 中以错误的方式使用了 .NET 库(这里是 Powershell 新手......)? [这个问题无效,因为 .WriteByte()
没有添加其他三个字节 - 请参阅更新部分/ 解决方案]
更新/解决方案
Mathias R. Jessen 的回答完全正确。但是,我想简短地解释一下为什么我自己没有看到这个(尽管之前知道字节顺序标记):
我结合使用 Notepad++ 和十六进制编辑器插件来调查发生了什么。显然,当文件被更改时,这个组合在更新打开文件的 HEX 视图方面存在问题,这就是为什么它让我走错了路。这是我第二次遇到这个问题,所以我以后一定会使用其他 HEX 编辑器。
答案写好后,我重新调查了一下,这次用的是HxD,一下子就明白了。
长话短说:这实际上不是 System.IO.FileStream.WriteByte()
的问题,而是用于调查的工具的问题。
解决方案很简单:如果我想要文件中的一个字节,我可以使用 Out-File
和另一种编码,或者我可以使用 .WriteByte()
首先创建文件。
在 Windows PowerShell 中,Out-File
默认为 little-endian UTF-16 编码(在 Windows 中俗称 Unicode
编码)。
当您使用值 "0"
执行 Out-File
时,它会将以下字节序列写入磁盘文件:
ff fe 48 00
\___/ \___/
| |
| UTF-16LE encoded "0"
UTF-16LE byte order mark
当你用[FileMode]::Open
调用[File]::Open()
时,它returns一个FileStream
对象指向最低偏移量文件,因此 WriteByte(49)
最终会覆盖 byte-order 标记的第一部分:
49 fe 48 00
|
Overwritten
如果您总是想覆盖现有文件中的任何数据,请使用 [FileMode]::Truncate
:
$Stream = [System.IO.File]::Open("c:\batch\vss\test.txt",
[System.IO.FileMode]::Open,
[System.IO.FileAccess]::Truncate,
[System.IO.FileShare]::ReadWrite)
如果您想截断文件以手动删除多余的数据,请使用 SetLength
:
$Stream.SetLength(1)