批量DOS复制文件的最后一行限制65 536个字符

我有一个沉重的XML文件1Go有以下结构:

<?xml version='1.0' encoding='windows-1252'?> <ext:BookingExtraction> <Booking><Code>2016Z00258</Code><Advertiser><Code>00123</Code<Name>LOUIS VUITTON</Name></Advertiser></Booking> <Booking><Code>2016Z00259</Code><Advertiser><Code>00124</Code<Name>Adidas</Name></Advertiser></Booking> </ext:BookingExtraction> 

由于结构非常简单,因此我的目标是将XML文件的最后150行复制到新文件中,并在第一行中添加开始标记以形成格式良好的XML。

该algorithm工作正常,但有超过65 536个字符的行被分成几行。 我读了DOS限制每行字符数为65 536.这就是为什么在这个65 536个字符之后添加一个回车符。

结果是最后的XML没有正确的形成,因为在行的中间input了马车。 例如:

  <ext:BookingExtraction> <Booking><Code>2016Z00258</Code><Advertiser><Code>00123</Code><Name>LOUIS VUIT TON</Name></Advertiser></Booking> </ext:BookingExtraction> 

我试图删除字符进入,但它不起作用。 你有什么想法我怎么能解决这个问题?

 `@echo off setLocal EnableDelayedExpansion ::Get XML file for /r %%a in (extractedBookings_BookingWithoutUnitsContent_PRD_*.xml) do ( ::echo "%%~dpa" and full path is "%%~nxa" set fileName="%%~nxa" ) ::Get the 150 last line of the file echo File path: "%fileName%" for /f %%i in ('find /v /c "" ^< "%fileName%"') do set /a lines=%%i echo nb lines: "%lines%" set /a startLine=%lines% - 150 echo Start line "%startLine%" more /e +%startLine% "%fileName%" > extractedBookings_BookingWithoutUnitsContent_PRD.xml ::adding opening tag to the new file echo ^<?xml version='1.0' encoding='windows-1252'?^> > newFile.xml echo ^<ext:BookingExtraction^> >> newFile.xml ::Get the final file type extractedBookings_BookingWithoutUnitsContent_PRD.xml >> newFile.xml type newFile.xml > extractedBookings_BookingWithoutUnitsContent_PRD.xml` 

先谢谢你

你的问题很混乱 “DOS限制65536个字符的行数”这个短语是不准确的。 当多个命令的输出被重定向到一个磁盘文件时,它等待65536 之后的一个字符,并且这个字符被插入到输出中。 此外,FIND命令中的最大行长度是1070个字符(相应地, 这个网站 ),所以我猜你的文件有更短的行。 你只需要一个可以干净地输出超过64K行的方法。

下面的解决方案基本上是相同的代码,但它使用set /P命令的组合来跳过第一行,并使用findstr命令来显示其余的,而不是more +%startLine%命令。

 @echo off setLocal EnableDelayedExpansion ::Get XML file for /r %%a in (extractedBookings_BookingWithoutUnitsContent_PRD_*.xml) do ( ::echo "%%~dpa" and full path is "%%~nxa" set fileName="%%~nxa" ) ::Get the 150 last line of the file echo File path: "%fileName%" for /f %%i in ('find /v /c "" ^< "%fileName%"') do set /a lines=%%i echo nb lines: "%lines%" set /a startLine=%lines% - 150 echo Start line "%startLine%" REM Use a code block to read from redirected input file (and write to output file) < "%fileName%" ( rem adding opening tag to the new file echo ^<?xml version='1.0' encoding='windows-1252'?^> echo ^<ext:BookingExtraction^> REM Skip the first total-150 lines for /L %%i in (1,1,%startLine%) do set /P "=" REM Copy the rest findstr "^" ) > extractedBookings_BookingWithoutUnitsContent_PRD.xml 

如果输入行超过1023个字符,则此方法仍可能失败,因为这是set /P命令的限制。

正如我之前评论的那样,“把XML解析为层次结构,而不是将其理解为可预测格式的平面文本更好。 如果平面文字被美化,丑化,缩小,无论如何,平面文字刮板都会失败。

您的示例XML仍然有点模糊,所以我假设您已经有一个<ext:BookingExtraction>标记,并且有大量的<Booking>子节点希望减少到最后的150个。

然而,在你的例子XML可以被解析之前,(除了修复</code>的missing </code> )之外,我们需要通过定义ext所属的名称空间来略微修改它。

之前:

 <ext:BookingExtraction> 

后:

 <ext:BookingExtraction xmlns:ext="http://localhost"> 

尽管严格来说这可能是一个虚假的命名空间,但是尽管如此,仍然可以进行XML解析。 我们可以通过将XML读入一个变量并执行一个正则表达式来编程。 之后,这只是一个简单的问题,在一个while循环内移除子节点,直到达到150个元素的目标为止。

将其保存为.bat扩展名,将“test.xml”替换为XML文件的位置,然后运行它。

 @if (@CodeSection == @Batch) @then @echo off & setlocal cscript /nologo /e:JScript "%~f0" "test.xml" "output.xml" goto :EOF @end // end Batch / begin JScript hybrid code var args = { infile: WSH.Arguments(0), outfile: WSH.Arguments(1) }, fso = WSH.CreateObject('Scripting.FileSystemObject'), file = fso.OpenTextFile(args.infile, 1), xml = file.ReadAll(), DOM = WSH.CreateObject('MSXML2.DOMDocument.6.0'), ns = 'xmlns:ext="http://localhost"', xpath = '/ext:BookingExtraction/Booking'; file.Close(); DOM.loadXML(xml.replace( /<(ext:BookingExtraction)>/i, function($0, $1) { return '<' + $1 + ' ' + ns + '>' } )); if (DOM.parseError.errorCode) { var e = DOM.parseError; WSH.StdErr.WriteLine('Error in ' + args.infile + ' line ' + e.line + ' char ' + e.linepos + ':\n' + e.reason + '\n' + e.srcText); WSH.Quit(1); } DOM.setProperty('SelectionNamespaces', ns); while (DOM.selectNodes(xpath).length > 150) { var node = DOM.selectSingleNode(xpath) node.parentNode.removeChild(node) } DOM.save(args.outfile) 

…或者只是删除ext:命名空间并稍后替换它可能会更容易一些。 这是一个批处理+ PowerShell混合脚本,演示。 它不像批处理+ Jscript混合版本那么快,并且它有一个美化所有标签的副作用,不管你是否希望它们缩进。 但它确实具有简单的优点。

 <# : batch portion @echo off & setlocal set "infile=test.xml" set "outfile=out.xml" powershell -noprofile "iex (${%~f0} | out-string)" goto :EOF : end batch / begin PowerShell hybrid #> [xml]$xml = (gc $env:infile) -replace "ext:" $xpath = "/BookingExtraction/Booking" $deleted = 0 while ($xml.selectNodes($xpath).Count -gt 150) { $node = $xml.selectSingleNode($xpath) [void]$node.parentNode.removeChild($node) $deleted++ } write-host "Removed $deleted ndoes" -f magenta $xml.save($env:outfile) (gc $env:outfile) -replace "BookingExtraction", "ext:BookingExtraction" | sc $env:outfile 

编辑:如果处理大文件(1GB +),也许它会更好地修剪为平面文本的脂肪,而不是作为结构化的对象数据操纵。 如果你想要最后的150条线,我认为从底部开始倒退,而不是从顶部开始,跳过数百万行,效率会更高。 使用.NET方法打开XML文件将允许您几乎立即找到文件的结尾,然后走。 试试这个批处理+ PowerShell脚本,看看它是否更有效地为你工作:

 <# : batch portion @echo off & setlocal set "infile=test.xml" set "outfile=out.xml" powershell -noprofile "iex (${%~f0} | out-string)" goto :EOF : end batch / begin PowerShell hybrid #> $lines = 150 $found = 0 $reader = new-object IO.StreamReader((gi $env:infile).FullName) $stream = $reader.BaseStream $xml = $reader.ReadLine(), $reader.ReadLine() $pos = $stream.Seek(0, [IO.SeekOrigin]::End) while ($found -le $lines) { $reader.DiscardBufferedData() $stream.Position = --$pos $char = $reader.Peek() if ($char -eq -1) { break } else { if ($char -eq 10) { $found++ } } } $reader.DiscardBufferedData() $stream.Position = ++$pos $xml += $reader.ReadToEnd() $reader.Close() $xml -join "`r`n" | out-file $env:outfile