【问题标题】:Regex pattern for data cleaning with windows batch file and使用 Windows 批处理文件进行数据清理的正则表达式模式和
【发布时间】:2017-03-11 14:40:00
【问题描述】:

我有一个文件,有时由于 CR/LF 格式不正确。

一个好的文件是这样的:

R00023j Field1 Field2 .... CR/LF
R00024n Field1 Field2 .... CR/LF
R00025k Field1 Field2 .... CR/LF

但有时我在一个字段中插入了一个 CR/LF,它会生成一个像这样的文件:

R00023j Fiel CR/LF
d1 Field2 .... CR/LF
R00024n Field1 Field2 .... CR/LF
R00025k Field1 Field2 .... CR/LF

我们可以认为我们有“货物”CR/LF(在行尾)和“坏”CR/LF(进入一个字段)。

我们可以认为一个好的 CR/LF 是紧随其后的RxxxxxY,在下一行。 所有其他 CR/LF 都是错误的,必须用 .(点)替换。

x:数字
Y:字母

如何使用 Windows 批处理文件和 RegEx 实现文件数据清理?

【问题讨论】:

  • 请分享您到目前为止所尝试的内容!
  • 字段数是否恒定?什么是字段分隔符?提供更多细节。像这样给你一个好的答案很复杂.....

标签: regex batch-file data-cleaning


【解决方案1】:

您的规范不完整 - 如果 CR/LF 位于文件的最后,它也很好。

我有一个使用JREPL.BAT - A regex find/replace utility 的简单解决方案。 JREPL 是纯脚本(混合批处理/JScript),可以在 XP 以后的任何 Windows 机器上本地运行。完整的文档可通过命令行通过jrepl /?jrepl /?? 获得分页帮助。

所需要的只是命令行中的一个简单的单行代码。如果您的来源是 bad.txt,并且您想创建 good.txt,那么:

jrepl "\r?\n(?=.)(?!R\d{5}[a-z])" "." /i /m /f bad.txt /o good.txt

您可以通过/o -覆盖原始文件:

jrepl "\r?\n(?=.)(?!R\d{5}[a-z])" "." /i /m /f file.txt /o -

如果将命令放在批处理脚本中,请使用 CALL JREPL。

请注意,您必须跨行搜索,因此必须使用/M 选项,它将整个文件加载到内存中。这限制了可以处理的文件大小。我相信限制在 1 到 2 GB 之间。

【讨论】:

    【解决方案2】:

    如果您的文件中没有特殊字符并且RxxxxxY 中没有出现额外的 CRLF,则以下内容应该可以工作

    @echo off
    setlocal enabledelayedexpansion
    for /f "delims=" %%a in (t.txt) do (
      echo %%a|findstr /b "R[0-9][0-9][0-9][0-9][0-9][a-z]">nul && (
        echo(!line!
        set line=%%a
      ) || (
        set line=!line!%%a
      )
    )
    echo %line%
    

    当你必须适应你的需要时,请注意一些findstr limitations

    【讨论】:

      【解决方案3】:

      虽然你没有表现出任何自己的努力,但我还是决定提供一个脚本,因为手头的任务对我来说似乎很有挑战性;所以我们开始(代码包含很多解释性注释,所以不要害怕):

      @echo off
      setlocal EnableExtensions DisableDelayedExpansion
      
      rem // Define constants here:
      rem /* Regular expression string for `findstr` command (to match `RxxxxxY`);
      rem    do not state `[a-z]` expression due to a nasty flaw of `findstr`!: */
      set "_SEARCH=R[0-9][0-9][0-9][0-9][0-9][abcdefghijklmnopqrstuvwxyz]"
      set "_REPLAC=." & rem // (character which each bad CR+LF is to be replaced by)
      
      rem // Enumerate all files provided by command line arguments:
      for %%F in (%*) do (
          rem /* Store paths of input and output files; to overwrite input files,
          rem    set `FILENEW` to `%%~fF` also: */
          set "FILEOLD=%%~fF"
          set "FILENEW=%%~dpnF_NEW%%~xF"
          rem // Initialise buffer for concatenated line strings:
          set "LBUF="
          rem // Read currently iterated file line by line (ignoring empty lines):
          setlocal EnableDelayedExpansion
          for /F "delims=" %%L in ('type "!FILEOLD!" ^& ^> "!FILENEW!" rem/') do (
              endlocal
              rem // Store current line string:
              set "LINE=%%L"
              setlocal EnableDelayedExpansion
              rem/ Double " due to pipe:
              set "LINE=!LINE:"=""!"
              rem /* Loop iterating once only over the current line with quotation
              rem    marks doubled in order to avoid trouble with the pipe later;
              rem    this allows disabling delayed expansion which might cause
              rem    trouble with pipes too in case `!` or `^` characters appear: */
              for /F "delims=" %%K in (^""!LINE!"^") do (
                  endlocal
                  rem /* Feed line string into `findstr` command using a pipe:
                  rem    for case-insensitivity, add switch `/I` to `findstr`: */
                  echo("%%~K"| > nul findstr /X /R /C:\"%_SEARCH%.*\"
                  rem // Test whether `findstr` encountered a match:
                  if ErrorLevel 1 (
                      rem /* No match encountered, so CR+LF was bad, hence
                      rem    concatenate previous buffer with current line,
                      rem    separated by the predefined character; due to a
                      rem    preceding `endlocal` command, `LINE` no longer
                      rem    contains the doubled quotation marks at this point;
                      rem    the `for /F` loop transfers the resulting string over
                      rem    the `endlocal` barrier safely: */
                      setlocal EnableDelayedExpansion
                      for /F "delims=" %%E in (^""!LBUF!%_REPLAC%!LINE!"^") do (
                          endlocal
                          set "LBUF=%%~E"
                      )
                  ) else (
                      rem /* Match encountered, so CR+LF is good, hence return
                      rem    the current buffer; the `if` query avoids to output
                      rem    an empty line initially: */
                      if defined LBUF (
                          setlocal EnableDelayedExpansion
                          >> "!FILENEW!" echo(!LBUF!
                          endlocal
                      )
                      rem // Store the current line to the buffer:
                      set "LBUF=%%L"
                  )
              )
              setlocal EnableDelayedExpansion
          )
          rem // Return the remaining content of the buffer finally:
          >> "!FILENEW!" echo(!LBUF!
          endlocal
      )
      
      endlocal
      exit /B
      

      如果搜索模式 (RxxxxxY) 应不区分大小写,只需将 /I 开关添加到 findstr 命令即可。

      请注意,每个(连接的)行的总长度限制为大约 8190 个字符。

      【讨论】:

        【解决方案4】:

        感谢大家的贡献。 Dbenham,当您说文件末尾的 CR/LF 很好时,您说我的规范不完整,您是对的。感谢您提供 JREPL 链接!

        我用 Regex 和 Powershell 解决了这个问题: $FileOut = $fileIn -creplace '\x0D\x0A(?![R][0-9]{5}[a-z])', '. '

        使用 FileIn 读取选项:-Encoding UTF8 -Raw

        【讨论】:

        • 不要发布此“谢谢”评论作为答案,请考虑接受最有帮助的答案并发表评论。
        猜你喜欢
        • 2014-02-05
        • 1970-01-01
        • 2018-06-04
        • 2021-04-09
        • 1970-01-01
        • 2015-05-09
        • 2018-06-14
        • 1970-01-01
        • 2021-02-04
        相关资源
        最近更新 更多