大文件的 UTF-8 BOM 到 UTF-8 的转换
UTF-8 BOM to UTF-8 Conversion for a large file
根据这个帖子的建议,我已经使用 powershell 进行了 UTF-8 转换,现在我 运行 遇到了另一个问题,我有一个大约 18 GB 的非常大的文件,我正在尝试在一台有大约 50GB 空闲内存的机器上进行转换,但是这个转换过程会耗尽所有内存并且编码失败,有没有办法限制 RAM 使用或分块进行转换?
Using PowerShell to write a file in UTF-8 without the BOM
下面是准确的代码
foreach ($file in ls -name $Path\CM*.csv)
{
$file_content = Get-Content "$Path$file";
[System.IO.File]::WriteAllLines("$Path$file", $file_content);
echo "encoding done : $file"
}
不要将文件内容存储在内存中。如前所述 here,这样做需要 RAM 中文件大小的 3-4 倍。 Get-Content
很慢但内存效率很高。所以一个简单的解决方案可能是
Get-Content -Path <FilePath> | Out-File -FilePath <FilePath> -Encoding UTF8
注意:虽然我没有尝试过,但您可能希望使用 Add-Content
而不是 Out-File
。后者有时会根据控制台宽度重新格式化。 Out-* cmdlet 的特征,它们遍历用于显示的格式化系统。
因为内容是通过管道流式传输的,所以一次只有一行存储在 RAM 中。 .Net 内存垃圾收集 运行 在后台释放和管理 RAM。
注意:[System.IO.StreamReader]
和 [System.IO.StreamWriter]
可能也可以解决这个问题。它们可能更快,并且内存效率一样高,但是它们带来了可能不值得的语法负担,特别是如果这是一次性的......也就是说,你可以用 System.Text.Encoding枚举,所以理论上可以使用它们进行转换。
您可以使用 StreamReader and StreamWriter 进行转换。
StreamWriter 默认输出 UTF8NoBOM。
这将需要很多磁盘操作,但会占用内存。
请记住,.Net 需要完整的绝对路径。
$sourceFile = 'D:\Test\Blah.txt' # enter your own in- and output files here
$destinationFile = 'D:\Test\out.txt'
$reader = [System.IO.StreamReader]::new($sourceFile, [System.Text.Encoding]::UTF8)
$writer = [System.IO.StreamWriter]::new($destinationFile)
while ($null -ne ($line = $reader.ReadLine())) {
$writer.WriteLine($line)
}
# clean up
$writer.Flush()
$reader.Dispose()
$writer.Dispose()
以上代码将在输出文件中添加最后一个换行符。如果不需要,请改为执行此操作:
$sourceFile = 'D:\Test\Blah.txt'
$destinationFile = 'D:\Test\out.txt'
$reader = [System.IO.StreamReader]::new($sourceFile, [System.Text.Encoding]::UTF8)
$writer = [System.IO.StreamWriter]::new($destinationFile)
while ($null -ne ($line = $reader.ReadLine())) {
if ($reader.EndOfStream) {
$writer.Write($line)
}
else {
$writer.WriteLine($line)
}
}
# clean up
$writer.Flush()
$reader.Dispose()
$writer.Dispose()
当您知道输入文件始终是带 BOM 的 UTF-8 时,您只需从文件中去除前三个字节(BOM)。
使用缓冲流,您只需将文件的一小部分加载到内存中。
为了获得最佳性能,我会使用 FileStream
。这是原始二进制流,因此开销最少。
$streamIn = $streamOut = $null
try {
$streamIn = [IO.FileStream]::new( $fullPathToInputFile, [IO.FileMode]::Open )
$streamOut = [IO.FileStream]::new( $fullPathToOutputFile, [IO.FileMode]::Create )
# Strip 3 bytes (the UTF-8 BOM) from the input file
$null = $streamIn.Seek( 3, [IO.SeekOrigin]::Begin )
# Copy the remaining bytes to the output file
$streamIn.CopyTo( $streamOut )
# You may try a custom buffer size for better performance:
# $streamIn.CopyTo( $streamOut, 1MB )
}
finally {
# Make sure to close the files even in case of an exception
if( $streamIn ) { $streamIn.Close() }
if( $streamOut ) { $streamOut.Close() }
}
您可以尝试 FileStream.CopyTo()
overload that has a bufferSize parameter. In ,较大的缓冲区大小(例如 1 MiB)可以显着提高性能,但是当它太大时,性能将再次受到影响,因为缓存不好使用。
根据这个帖子的建议,我已经使用 powershell 进行了 UTF-8 转换,现在我 运行 遇到了另一个问题,我有一个大约 18 GB 的非常大的文件,我正在尝试在一台有大约 50GB 空闲内存的机器上进行转换,但是这个转换过程会耗尽所有内存并且编码失败,有没有办法限制 RAM 使用或分块进行转换?
Using PowerShell to write a file in UTF-8 without the BOM
下面是准确的代码
foreach ($file in ls -name $Path\CM*.csv)
{
$file_content = Get-Content "$Path$file";
[System.IO.File]::WriteAllLines("$Path$file", $file_content);
echo "encoding done : $file"
}
不要将文件内容存储在内存中。如前所述 here,这样做需要 RAM 中文件大小的 3-4 倍。 Get-Content
很慢但内存效率很高。所以一个简单的解决方案可能是
Get-Content -Path <FilePath> | Out-File -FilePath <FilePath> -Encoding UTF8
注意:虽然我没有尝试过,但您可能希望使用 Add-Content
而不是 Out-File
。后者有时会根据控制台宽度重新格式化。 Out-* cmdlet 的特征,它们遍历用于显示的格式化系统。
因为内容是通过管道流式传输的,所以一次只有一行存储在 RAM 中。 .Net 内存垃圾收集 运行 在后台释放和管理 RAM。
注意:[System.IO.StreamReader]
和 [System.IO.StreamWriter]
可能也可以解决这个问题。它们可能更快,并且内存效率一样高,但是它们带来了可能不值得的语法负担,特别是如果这是一次性的......也就是说,你可以用 System.Text.Encoding枚举,所以理论上可以使用它们进行转换。
您可以使用 StreamReader and StreamWriter 进行转换。
StreamWriter 默认输出 UTF8NoBOM。
这将需要很多磁盘操作,但会占用内存。
请记住,.Net 需要完整的绝对路径。
$sourceFile = 'D:\Test\Blah.txt' # enter your own in- and output files here
$destinationFile = 'D:\Test\out.txt'
$reader = [System.IO.StreamReader]::new($sourceFile, [System.Text.Encoding]::UTF8)
$writer = [System.IO.StreamWriter]::new($destinationFile)
while ($null -ne ($line = $reader.ReadLine())) {
$writer.WriteLine($line)
}
# clean up
$writer.Flush()
$reader.Dispose()
$writer.Dispose()
以上代码将在输出文件中添加最后一个换行符。如果不需要,请改为执行此操作:
$sourceFile = 'D:\Test\Blah.txt'
$destinationFile = 'D:\Test\out.txt'
$reader = [System.IO.StreamReader]::new($sourceFile, [System.Text.Encoding]::UTF8)
$writer = [System.IO.StreamWriter]::new($destinationFile)
while ($null -ne ($line = $reader.ReadLine())) {
if ($reader.EndOfStream) {
$writer.Write($line)
}
else {
$writer.WriteLine($line)
}
}
# clean up
$writer.Flush()
$reader.Dispose()
$writer.Dispose()
当您知道输入文件始终是带 BOM 的 UTF-8 时,您只需从文件中去除前三个字节(BOM)。
使用缓冲流,您只需将文件的一小部分加载到内存中。
为了获得最佳性能,我会使用 FileStream
。这是原始二进制流,因此开销最少。
$streamIn = $streamOut = $null
try {
$streamIn = [IO.FileStream]::new( $fullPathToInputFile, [IO.FileMode]::Open )
$streamOut = [IO.FileStream]::new( $fullPathToOutputFile, [IO.FileMode]::Create )
# Strip 3 bytes (the UTF-8 BOM) from the input file
$null = $streamIn.Seek( 3, [IO.SeekOrigin]::Begin )
# Copy the remaining bytes to the output file
$streamIn.CopyTo( $streamOut )
# You may try a custom buffer size for better performance:
# $streamIn.CopyTo( $streamOut, 1MB )
}
finally {
# Make sure to close the files even in case of an exception
if( $streamIn ) { $streamIn.Close() }
if( $streamOut ) { $streamOut.Close() }
}
您可以尝试 FileStream.CopyTo()
overload that has a bufferSize parameter. In