我有一个大文件> 1.5GB,它有'#@#@#'作为行分隔符。在通过Informatica处理它之前,我将用CRLF字符替换它。问题是,我在文件中有CR,LF字符,我需要在替换之前删除它们。我找到了几个选项来做到这一点,但由于尺寸的原因,我得到OutofMemory例外。
param
(
[string]$Source,
[string]$Destination
)
echo $Source
echo $Destination
$Writer = New-Object IO.StreamWriter $Destination
$Writer.Write( [String]::Join("", $(Get-Content $Source)) )
$Writer.Close()
我的问题是,无论如何将行分隔符设置为'#@#@#'然后逐行读取文件以删除CR,LF字符。
答案 0 :(得分:3)
我使用了混合批处理/ JScript,所以它可以在XP之后的任何Windows机器上运行 - 不需要第三方exe文件,也不需要任何编译。
我读写大约1 MB的块。逻辑实际上非常简单。
我用一个空格替换所有\ r \ n,用@ r \ n替换#@#@#。您可以轻松更改代码中的字符串值以满足您的需求。
<强> fixLines.bat 强>
@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
::--- Batch section within JScript comment that calls the internal JScript ----
@echo off
setlocal disableDelayedExpansion
if "%~1" equ "" (
echo Error: missing input argument
exit /b 1
)
if "%~2" equ "" (
set "out=%~f1.new"
) else (
set "out=%~2"
)
<"%~1" >"%out%" cscript //nologo //E:JScript "%~f0"
if "%~2" equ "" move /y "%out%" "%~1" >nul
exit /b
----- End of JScript comment, beginning of normal JScript ------------------*/
var delim='#@#@#',
delimReplace='\r\n',
nl='\r\n',
nlReplace=' ',
pos=0,
str='';
var delimRegex=new RegExp(delim,"g"),
nlRegex=new RegExp(nl,"g");
while( !WScript.StdIn.AtEndOfStream ) {
str=str.substring(pos)+WScript.StdIn.Read(1000000);
pos=str.lastIndexOf(delim)
if (pos>=0) {
pos+=delim.length;
WScript.StdOut.Write(str.substring(0,pos).replace(nlRegex,nlReplace).replace(delimRegex,delimReplace));
} else {
pos=0
}
}
if (str.length>pos) WScript.StdOut.Write(str.substring(pos).replace(nlRegex,nlReplace));
修复input.txt并将输出写入output.txt:
fixLines input.txt output.txt
覆盖原始文件test.txt
fixLines test.txt
为了解决问题,我尝试使用JREPL.BAT处理1.8 GB文件。我认为它不会起作用,因为它必须将整个文件加载到内存中。无论计算机中安装了多少内存 - JScript的最大字符串大小限制为2GB。我认为还有其他限制因素可以发挥作用。
jrepl "\r?\n:#@#@#" " :\r\n" /m /x /t : /f input.txt /o output.txt
命令失败并出现“Out Of Memory”错误需要5分钟。然后我的计算机需要很长时间才能从严重的内存滥用中恢复过来。
以下是我一次读取和写入一个字符的原始自定义批处理/ JScript解决方案。
<强> slow.bat 强>
@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
::--- Batch section within JScript comment that calls the internal JScript ----
@echo off
setlocal disableDelayedExpansion
if "%~1" equ "" (
echo Error: missing input argument
exit /b 1
)
if "%~2" equ "" (
set "out=%~f1.new"
) else (
set "out=%~2"
)
<"%~1" >"%out%" cscript //nologo //E:JScript "%~f0"
if "%~2" equ "" move /y "%out%" "%~1" >nul
exit /b
----- End of JScript comment, beginning of normal JScript ------------------*/
var delim='#@#@#',
delimReplace='\r\n',
nlReplace=' ',
read=1,
write=2,
pos=0,
char;
while( !WScript.StdIn.AtEndOfStream ) {
chr=WScript.StdIn.Read(1);
if (chr==delim.charAt(pos)) {
if (++pos==delim.length) {
WScript.StdOut.Write(delimReplace);
pos=0;
}
} else {
if (pos) {
WScript.StdOut.Write(delim.substring(0,pos));
pos=0;
}
if (chr=='\n') {
WScript.StdOut.Write(nlReplace);
} else if (chr!='\r') {
WScript.StdOut.Write(chr);
}
}
}
if (pos) WScript.StdOut.Write(delim.substring(0,pos));
它有效,但它是一只狗。以下是处理155 MB文件的计时结果摘要:
slow.bat 3120 sec (52 min)
jrepl.bat 55 sec
fixLines.bat 15 sec
我确认所有三种解决方案都给出了相同的结果。
答案 1 :(得分:2)
这个PowerShell(v2 +)解决方案是慢,但它在概念上很简单,你不应该耗尽内存,因为使用{{1}一次处理一个输入行}作为行分隔符。
注意:此解决方案结合了您的两个步骤:
它用每个单独的空格替换原始换行符,
并用换行符替换每个#@#@#
序列。
#@#@#
注意:
当您使用# Create sample input file.
@'
line 1 starts here
and
ends here#@#@#line 2 is all on one line#@#@#line 3 spans
two lines#@#@#
'@ > file
# Determine the input file.
$inFile = 'file'
# Create the output file.
$outFile = 'out'
$null = New-Item -Type File $outFile
Get-Content -Delimiter '#@#@#' $inFile | % {
Add-Content -Value ($_.Replace("`r`n", " ").Replace($sep, '')) $outFile
}
时,在通过管道传递的每个项目中指定的分隔符包含(与默认行为不同,默认分隔符(换行符)为剥离)。
-Delimiter
会自动在其输出中添加一个尾随的CRLF(在PSv5 +中,可以使用Add-Content
来抑制它。)
该方法使用-NoNewLine
类型的[string]
方法,而不是PowerShell灵活的,基于正则表达式的.Replace()
运算符,因为-replace
执行 literal 替换,速度更快(等效命令为
.Replace()
。
也就是说,速度增益可以忽略不计;它是文件I / O部分占用大部分时间。)
dbenham's clever and elegant batch + JScript solution明显快于上述PowerShell解决方案。
以下是他在按需编译的PowerShell脚本中使用 C#代码的方法的改编。
编译非常快(在我2012年末的iMac上大约0.3秒),使用编译代码处理文件可以显着提高性能。
另请注意,每个会话只执行一次编译,因此后续调用不会支付此罚金。
使用下面打印的脚本处理~1 GB文件(通过重复上述示例文件的内容创建)会产生以下结果:
Add-Content -Value (($_ -replace '\r\n', ' ') -replace '#@#@#') $outFile
实际应用程序中的执行时间因许多因素而异,但根据下面评论中提到的@ dbenham的时序,按需编译解决方案的速度是批处理+ JScript解决方案的两倍。
快速PowerShell解决方案的源代码:
Compiling...
Processing file...
Completed:
Compilation time: 00:00:00.2343647
File-processing time: 00:00:26.0714467
Total: 00:00:26.3278546