RegEx 适用于在线模拟器,但不适用于带有 findstr 的 BatchFile

RegEx works on Online Simulator but not inside BatchFile with findstr

我试图设置一个批处理文件,它使用 findstr 来杀死所有具有特定模式的行。我要分析的源文件如下所示(我将除第 16 位以外的所有值都更改为数字,通常它们是名称、网址、空字符或单个字符,如 Y/N):

ProductCode|SkuID|Bestellnr|ProductName|locale_de-DE_ProductName|locale_it-IT_ProductName|locale_nl-NL_ProductName|locale_fr-FR_ProductName|locale_en-GB_ProductName|locale_da-DA_ProductName|locale_cs-CZ_ProductName|locale_sv-SE_ProductName|locale_pl-PL_ProductName|locale_sk-SK_ProductName|ProductType|ProduktLink|OnlineAvailability|ProductNumber|IsProdukt|TerritoryAvailability|Category|SubCategory|ImageLink|Status|Flag0|Flag1|Flag2
0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|Y|17|18|19|20|21|22|23|24|25|26
0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|N|17|18|19|20|21|22|23|24|25|26
0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|N|17|18|19|20|21|22|23|24|25|26
0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|Y|17|18|19|20|21|22|23|24|25|26
0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|Y|17|18|19|20|21|22|23|24|25|26

我只想排除所有在第 16 个参数中有 N 的行。因此我想出了一个正则表达式模式来做到这一点:

^([^|]*\|){16}N

RegEx 工作的演示(在线资源)

https://regex101.com/r/mE5HVR/1/

当我尝试像这样将此功能与 findstr 一起使用时:

FINDSTR /V "^([^|]*\|){16}N" H:\BatchTest\LineProcessing\myfile.txt >H:\BatchTest\LineProcessing\result.txt
pause
exit

我总能得到完整的文件,但好像连正则表达式都没用过。任何人都可以指出我可以搜索错误的正确方向吗?我尝试通过此 What are the undocumented features and limitations of the Windows FINDSTR command? post 获取更多信息,但我找不到我的缺陷或监督它。

感谢任何帮助

从批处理中调用 powershell 作为工具:

@Echo off
Set "FileIn=H:\BatchTest\LineProcessing\myfile.txt"
Set "FileOut=H:\BatchTest\LineProcessing\result.txt"
powershell -NoP -C "Get-Content '%FileIn%' |Where-Object {$_ -notmatch '^([^|]*\|){16}N'}"  >"%FileOut%"
pause
exit

在 powershell 中使用别名可以缩短命令

powershell -NoP -C "gc '%FileIn%'|?{$_ -notmatch '^([^|]*\|){16}N'}"  >"%FileOut%"

根据 documentationfindstr 对正则表达式的支持非常有限。

您可能想尝试这样的事情:

findstr /V "^[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|[^|]*|N|" "myfile.txt"

但不幸的是,这会导致错误 (FINDSTR: Search string too long.),因为指定的字符 类 [] 太多了,我认为(参考您已经引用的有用线程在你的问题中:What are the undocumented features and limitations of the Windows FINDSTR command?).


但是,我可以想出一种变通方法,使用 for /F loop 来读取文件并删除感兴趣的列之前的所有 16 列;这仅在前面几列的 none 为空的情况下有效:

@echo off
set "HEAD=" & set "FLAG="
for /F "usebackq tokens=1-16* delims=| eol=|" %%A in ("%~1") do (
    if not defined HEAD (
        set "HEAD=#" & set "FLAG=#"
    ) else (
        set "LINE=%%Q"
        cmd /V /C echo(!LINE!| > nul findstr "^N|" || set "FLAG=#"
    )
    if defined FLAG (
        echo(%%A^|%%B^|%%C^|%%D^|%%E^|%%F^|%%G^|%%H^|%%I^|%%J^|%%K^|%%L^|%%M^|%%N^|%%O^|%%P^|%%Q
        set "FLAG="
    )
)

这使得有趣的列显示为第一个,因此 findstr 现在可以使用了。

或者这是另一种完全不使用 findstr 的方法:

@echo off
set "HEAD=" & set "FLAG="
for /F "usebackq tokens=1-17* delims=| eol=|" %%A in ("%~1") do (
    if not defined HEAD (
        set "HEAD=#" & set "FLAG=#"
    ) else (
        if not "%%Q"=="N" set "FLAG=#"
    )
    if defined FLAG (
        echo(%%A^|%%B^|%%C^|%%D^|%%E^|%%F^|%%G^|%%H^|%%I^|%%J^|%%K^|%%L^|%%M^|%%N^|%%O^|%%P^|%%Q^|%%R
        set "FLAG="
    )
)

如果任何列可能为空,您可以使用以下改编代码:

@echo off
set "LINE="
for /F usebackq^ delims^=^ eol^= %%L in ("%~1") do (
    if not defined LINE (
        set "LINE=%%L"
        echo(%%L
    ) else (
        set "LINE=%%L"
        setlocal EnableDelayedExpansion
        for /F "tokens=17 delims=| eol=|" %%K in ("_!LINE:|=|_!") do (
            endlocal
            set "ITEM=%%K"
            setlocal EnableDelayedExpansion
        )
        if not "!ITEM:~1!"=="N" echo(!LINE!
        endlocal
    )
)

在提取值并根据 N 进行检查之前,这会间歇性地为每个项目添加下划线前缀 _,因此 for /F.

没有任何列显示为空

用户 aschipfl 有 。没有使用 FINDSTR 的简单解决方案。

你可以用我的JREPL.BAT regex utility轻松解决问题。 JREPL 是纯脚本(混合 JScript/batch),从 XP 开始可以在任何 Windows 机器上本地运行 - 不需要第 3 方 exe 文件。

从命令行你可以简单地使用:

jrepl "^([^|]*\|){16}(?!N\|)" "" /k 0 /f myfile.txt /o result.txt

在批处理文件中,您需要使用 CALL,不幸的是,这会使引用加倍 ^。添加 \XSEQ 以便可以使用扩展转义序列 \c 代替 ^.

call jrepl "\c([\c|]*\|){16}(?!N\|)" "" /k 0 /xseq /f myfile.txt /o result.txt

以上解决方案只保留至少有17列的行并且没有N作为第17列;这意味着它将排除没有 17 列的行。

如果您想使用最初的策略简单地排除具有 N 作为第 17 列的行,那么

jrepl "" "" /exc "/^([^|]*\|){16}N\|/" /k 0 /f myfile.txt /o result.txt

call jrepl "" "" /exc "/\c([\c|]*\|){16}N\|/" /k 0 /f myfile.txt /o result.txt

/XSEQ 不是必需的,因为 /EXC 正则表达式自动支持扩展转义序列。

为了补充我之前的评论并配合现有的 PowerShell 答案,这里有一个批处理文件行,它利用 PowerShell 但绕过了执行 RegEx 的需要。

它将文件读取为竖线分隔的csv,并输出OnlineAvailability字段匹配Y的行,(可以修改为-NotMatch 'N':

@PowerShell -NoP "IpCSV 'H:\BatchTest\LineProcessing\myfile.txt' -Del '|'|?{$_.OnlineAvailability -Match 'Y'}|EpCSV 'H:\BatchTest\LineProcessing\result.txt' -NoT -Del '|'"

结果应该是格式正确的 csv,带有双引号字段。


如果您不希望有那些双引号字段,也许这种修改是合适的:

@PowerShell -NoP "IpCSV 'H:\BatchTest\LineProcessing\myfile.txt' -Del '|'|?{$_.OnlineAvailability -Match 'Y'}|ConvertTo-CSV -NoT -Del '|'|%%{$_ -Replace '""',''}|Out-File 'H:\BatchTest\LineProcessing\result.txt'"