通过批处理文件删除文本文件中包含关键字的部分行



我试图删除某些行,其中包含一个关键字在使用CMD或VBS文本文件。

我读过这个,但是不一样。我想删除一段行

原文件:

ABCDEFGXXX
ABCD
A
AE
AXXXLKGUSP
0000ASD
ASD

已处理文本文件:

0000ASD
ASD

我想删除包含'XXX'的第一个实例和第二个实例的行范围。这个"XXX"只有两个实例。两个实例之间的行数是随机的,并且可能存在两个实例位于同一行的场景。4个零也出现在'XXX'的第二个实例的行之后。请注意,它可能包含下面的字符,所以如果您尝试处理。

--------------------- 编辑08/04/2015 41点 -----------------------------------"XXX"都是大写的……文本文件可能包含以下字符…但它在记事本中显示了一行。我知道它们是什么

PK
    ¨‘G_t¥0  8ˆ     XXXç¸[~-ÄWÀ¨Ì’gÝ

使用JREPL.BAT(一个纯基于脚本的实用程序(JScript/batch的混合),可以在XP以后的任何Windows机器上本机运行),这真的很简单。关键特性是它支持多行正则表达式搜索和替换。

下面的内容覆盖原始文件:

jrepl "^.*?XXX[sS]*XXX.*n?" "" /m /f "test.txt" /o -

如果您想创建一个新文件,那么只需为/O选项指定一个文件名而不是-:

jrepl "^.*?XXX[sS]*XXX.*n?" "" /m /f "input.txt" /o "output.txt"

或者您可以完全省略/O选项并将结果打印到屏幕(stdout)

jrepl "^.*?XXX[sS]*XXX.*n?" "" /m /f "input.txt"


如果将命令放在批处理脚本中,则使用call jrepl ...


编辑:这是一个纯批量解决方案。通常情况下,我已经放弃使用批处理进行文本处理,因为一个健壮的解决方案需要太多的疯狂。下面的代码是相当健壮和优化的,但即使如此,仍然有以下限制:

  • 行限<8k
  • 搜索关键字不能以*开头,不能包含=!"
  • 搜索忽略大小写,没有选项使其区分大小写

我可能还漏掉了一些。

但是代码保留空行,并且不会阻塞内容中的!。(处理这些可能性是许多复杂性的原因)

@echo off
setlocal disableDelayedExpansion
set "in=input.txt"
set "out=output.txt"
set "find=XXX"
set "cnt=0"
>"%out%" (
  for /f "delims=" %%A in ('findstr /n "^" "%in%"') do (
    set "ln=%%A"
    setlocal enableDelayedExpansion
    set "ln=!ln:*:=!"
    if defined ln if !cnt! equ 0 (
      set "test=!ln:*%find%=!"
      if !test! neq !ln! set "cnt=1"
    ) else set "test=!ln!"
    if !cnt! neq 1 echo(!ln!
    if defined test if !cnt! neq 2 if "!test:%find%=!" neq "!test!" set "cnt=2"
    for %%N in (!cnt!) do endlocal&set "cnt=%%N"
  )
)

假设您提到VBS是为了表明可以接受非纯批处理语法解决方案,这里有一个powershell一行程序,您可以从.bat调用它。

default系统编码读取文本文件,其他有用的值有UTF8Unicode
一个100MB的文件在2秒内处理完毕。

@echo off
set "string=XXX"
set "infile=input file.txt"
set "outfile=output file.txt"
set "encoding=default"
powershell -ExecutionPolicy bypass -c "$txt=(get-content '%infile%' -raw -encoding default); $i=$txt.indexof('%string%'); if($i -ge 0) { $j=$txt.indexof('%string%',$i+'%string%'.length); if($j -ge 0) {$k=$txt.indexof("`n",$j); if($k -ge $j){$txt2=$txt.substring($k)} else {$txt2=''} $txt.substring(0,[math]::max(0,$txt.lastindexof("`n",$i))) + $txt2 | out-file '%outfile%' -encoding %encoding%}}"
pause
  • -ExecutionPolicy bypass允许在非admin用户帐户上执行powershell。
  • PowerShell 3.0及更新版本是必需的,它是默认的Windows 7 SP1, 8, 10。

这是一个纯CMD实现:

@echo off
rem DEFINITIONS:
set "KEYWD=XXX"
set "INFILE=original.txt"
set "OUTFILE=modified.txt"
setlocal EnableExtensions EnableDelayedExpansion
rem GET_LINE_NUMBERS:
set "NUMONE="
set "NUMTWO="
for /F %%F in ('findstr /N /L /C:"%KEYWD%" "%INFILE%"') do (
  for /F "delims=:" %%N in ("%%F") do (
    if not defined NUMONE (
      set "NUMONE=%%N"
    ) else (
      set "NUMTWO=%%N"
    )
  )
)
if not defined NUMTWO set "NUMTWO=%NUMONE%"
rem RETURN_BEFORE_BLOCK:
set /A "COUNT=0"
rem.> "%OUTFILE%"
for /F "delims=" %%L in ('findstr /N /R "^" "%INFILE%"') do (
  set /A "COUNT+=1"
  if !COUNT! geq !NUMONE! (
    goto :NEXT
  ) else (
    setlocal DisableDelayedExpansion
    set "LINE=%%L"
    setlocal EnableDelayedExpansion
    echo(!LINE:*:=!
    endlocal
    endlocal
  ) >> "%OUTFILE%"
)
rem RETURN_AFTER_BLOCK:
:NEXT
if defined NUMTWO set "SKIP=skip=!NUMTWO!"
for /F "%SKIP% delims=" %%L in ('findstr /N /R "^" "%INFILE%"') do (
  setlocal DisableDelayedExpansion
  set "LINE=%%L"
  setlocal EnableDelayedExpansion
  echo(!LINE:*:=!
  endlocal
  endlocal
) >> "%OUTFILE%"
endlocal

代码由四个部分组成(参见注释rem):

  • 定义:这里需要定义搜索关键字、输入和输出文件;
  • GET_LINE_NUMBERS:该部分搜索给定关键字出现两次的行号;生成的行号分别存储在NUMONENUMTWO变量中;如果只找到一行(a)匹配(es),则NUMTWO设置为NUMONE;如果没有找到匹配项,则两个变量都为空;
  • RETURN_BEFORE_BLOCK:这里的所有内容直到但不包括第一个关键字匹配的行;它依赖于goto打破任何正在进行的for循环上下文的事实;
  • RETURN_AFTER_BLOCK:在本节中,返回第二个关键字匹配之后的每一行,并附加到前一节的输出中;这里for /F选项参数skip是动态构建的;

这是另一个JScript/Batch混合解决方案。

假设文本文件在data.txt中,我们可以调用一个替换脚本:

cscript //nologo j.js .*XXX.*n[sS]*?.*n*XXX.* "" < data.txt

其中j.js是用Microsoft JScript编写的支持脚本:

var txt = WScript.StdIn.ReadAll();
var pattern = new RegExp( WScript.Arguments.Item(0), "g" );
var newvalue = WScript.Arguments.Item(1);
txt = txt.replace( pattern, newvalue );
WScript.StdOut.Write( txt );
j.js脚本的高尔夫优化版本是:
WScript.StdOut.Write(WScript.StdIn.ReadAll().replace(new RegExp(WScript.Arguments.Item(0),"g"),WScript.Arguments.Item(1)));

一般来说,j.js的用法是:

cscript //nologo j.js pattern_regular_expression new_value < input.dat > output.dat

其中input.dat为输入文本/数据文件。如果省略< input.dat,则将从用户输入中获取输入。output.dat指的是输出文本/数据文件。如果省略> output.dat,输出将显示在控制台上

最新更新