【问题标题】:Extracting a string between the first and second comma in a specific line in all files in a directory提取目录中所有文件的特定行中第一个和第二个逗号之间的字符串
【发布时间】:2012-07-09 07:19:59
【问题描述】:

我正在尝试提取位于一系列文本文件(字幕文件)中特定行的第一个和第二个逗号之间的字符串。文本文件的格式如下:

字幕01.txt

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour
Style: Default, Estrangelo Edessa, 57, &H00FFFFFF
Style: Title1, Arno Pro, 65, &H00606066

字幕02.txt

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour
Style: OP Eng, Arno Pro, 45, &H00100F11
Style: ED Romaji, Nueva Std Cond, 46, &H00FFFFFF

字幕03.txt

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour
Style: OP Eng, Estrangelo Edessa, 45, &H00100F11
Style: Default, Arno Pro, 45, &H00100F11
Style: ED Romaji, Nueva Std Cond, 46, &H00FFFFFF

我想在这里实现的是提取以“样式:”开头的每一行的字体名称,然后以不重复的方式确定哪些字幕包含我想要的字体。所以基本上最终结果将输出到如下文本文件;

Subtitles01.txt: Estrangelo Edessa
Subtitles01.txt: Arno Pro
Subtitles02.txt: Arno Pro
Subtitles02.txt: Nueva Std Cond
Subtitles03.txt: Estrangelo Edessa
Subtitles03.txt: Arno Pro
Subtitles03.txt: Nueva Std Cond

Only Subtitles03.txt is needed.

由于 Subtitles03.txt 包含了 Subtitles01.txt 和 Subtitles02.txt 中的所有字体,所以只需要 Subtitles03.txt。目标是使用最少的文件来找到所有文件中的唯一字体。我想出了以下批处理脚本,使用 findstr 来提取以“Style:”开头的行,但我被困在了这个之外。

@echo off
findstr /B /C:"Style:" *.txt > results.txt
if %errorlevel%==0 (
    echo Found! logged files into results.txt
) else (
    echo No matches found
)

任何帮助将不胜感激。谢谢各位!

【问题讨论】:

    标签: regex string batch-file command findstr


    【解决方案1】:

    我想使用批处理以外的其他语言会更容易,或者至少使用非本地实用程序。但这里是纯原生批处理解决方案。

    我看不出 FINDSTR 正则表达式如何帮助解决这个问题。它不能像许多其他非本地批处理正则表达式实用程序一样提取匹配行的一部分。

    您可以使用 FOR /F 从每个文件中提取字体:

    for /f "tokens=2 delims=," %%A in ('findstr /lb "Style:" file.txt') do echo font=%%A
    

    您可以使用环境变量来列出唯一字体。用变量名中的字体名定义变量,都以font_为前缀。一个给定的名称只能定义一个变量。分配的值无关紧要。然后,您可以使用set font_ 列出所有唯一字体名称。可以统计唯一名称的个数,也可以解析出实际的字体名称(去掉font_前缀)。

    棘手的部分是建立涵盖完整唯一字体名称集所需的最小文件集。我想有人可以想出一个有效的解决方案。我刚刚采用了一种强力递归排列方法:我计算在每个排列中找到的唯一字体的数量,并将该数字与唯一字体的总数进行比较。如果我已经找到比当前集合更小的完整集合,我已经添加了一些快捷方式来避免沿着特定的排列路径继续前进。

    如果我在递归中使用 SETLOCAL,代码可能会更简单,但批处理仅限于 32 个级别的 SETLOCAL。我想要一个可以支持超过 32 个文件的解决方案,尽管我有点担心这么多文件的性能。

    编辑 -我修复了我的 :permuteFiles 例程中的一个错误,一旦我有超过 3 个文件就会浮出水面

    @echo off
    setlocal enableDelayedExpansion
    
    ::Make sure there are no font_ variables defined
    for /f "delims==" %%A in ('set font_ 2^>nul') do set "%%A="
    
    ::Read all the Subtitle files and
    :: - create an "array" of file names
    :: - create a file of font names for each input file
    :: - create an "associative array" of unique font names
    :: - List the available file/font pairs in the final results
    :: - List the unique fonts in the final results
    set fileCount=0
    >results.txt (
      echo Available fonts
      echo ----------------------------
      for %%F in (subtitles*.txt) do (
        set /a totalFiles+=1
        set "file_!totalFiles!=%%F"
        3>"%%F.fonts" (
          for /f "tokens=2 delims=," %%A in ('findstr /lb "Style:" "%%F"') do (
            set "font_%%A=1"
            >&3 echo %%A
            echo %%F:%%A
          )
        )
      )
      echo(
      echo Unique fonts
      echo ----------------------------
      for /f "delims==" %%A in ('set font_') do (
        set "font=%%A"
        echo !font:~5!
      )
    )
    
    ::Count the number of unique fonts
    for /f %%N in ('set font_ ^| find /c /v ""') do set uniqueFonts=%%N
    
    ::Test all the permutations
    set /a minFileCount=%totalFiles%+1
    for /l %%N in (1 1 %totalFiles%) do (
      call :permuteFiles %%N 0 ""
    )
    
    ::List the required files in the final results
    >>results.txt (
      echo(
      echo The following files contain the complete set of unique fonts:
      echo -------------------------------------------------------------
      for %%N in (%minFileList:~1,-1%) do echo !file_%%N!
    )
    type results.txt
    
    ::Cleanup
    del subtitles*.txt.fonts
    exit /b
    
    
    :permuteFiles  fileNumber  fileCount  fileList
    if %1==%totalFiles% (
      if %2 gtr 0 call :testPermutation %2 %3
      set /a fileCount=%2+1
      if !fileCount! lss !minFileCount! call :testPermutation !fileCount! "%~3,%1"
    ) else (
      set /a nextFile=%1+1
      if %2 gtr 0 call :permuteFiles !nextFile! %2 %3
      set /a "nextFile=%1+1, fileCount=%2+1"
      if !fileCount! lss !minFileCount! call :permuteFiles !nextFile! !fileCount! "%~3,%1"
    )
    exit /b
    
    
    :testPermutation  fileCount  fileList
    for /f "delims==" %%A in ('set font_ 2^>nul') do set "%%A="
    for %%N in (%~2) do (
      for /f "usebackq delims=" %%A in ("!file_%%N!.fonts") do set "font_%%A=1"
    )
    for /f %%N in ('set font_ ^| find /c /v ""') do if %%N==%uniqueFonts% (
      set minFileList=%2
      set minFileCount=%1
    )
    exit /b
    

    以下是使用您的示例输入的结果:

    Available fonts
    ----------------------------
    Subtitles01.txt: Estrangelo Edessa
    Subtitles01.txt: Arno Pro
    Subtitles02.txt: Arno Pro
    Subtitles02.txt: Nueva Std Cond
    subtitles03.txt: Estrangelo Edessa
    subtitles03.txt: Arno Pro
    subtitles03.txt: Nueva Std Cond
    
    Unique fonts
    ----------------------------
     Arno Pro
     Estrangelo Edessa
     Nueva Std Cond
    
    The following files contain the complete set of unique fonts:
    -------------------------------------------------------------
    subtitles03.txt
    

    【讨论】:

      【解决方案2】:

      编辑:使用这个:

      ^Style:\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*(.+)\s*
      

      【讨论】:

      • \s 是多余的。 [^,] 匹配 \s 匹配的所有内容。否则,很好。 :)
      【解决方案3】:

      我知道您显然是在一个没有默认安装 awk 或 Perl 的平台上,但您可能仍想考虑安装其中一个工具,特别是如果您将来需要执行类似的任务。

      awk -F, '/^Style:/ { print FILENAME ":" $2 }' *.txt
      

      或者使用 Perl:

      perl -ne 'print "$ARGV:$1\n" if m/^Style: [^,]*,([^,]*)/' *.txt
      

      随后的优化(删除与其他匹配文件重叠的任何文件)在任何一种语言中都不难做到。 Perl 可以更好地适应更大、更多样化的任务,所以如果你对两者都不熟悉,这将是我的第一个建议(代价是学习曲线更长、更坎坷,然后你也应该考虑使用 Python)。

      【讨论】:

        【解决方案4】:

        如果“目标是使用最少的文件来找到所有文件中的唯一字体”,那么下面的批处理文件可以解决您的问题:

        编辑:操作!我之前的代码中有一个小错误:在处理目标文件中包含的每个 Fontname 时,我不应该删除 整个 文件,只需从文件计数中减去 that Fontname。我修复了下面代码中的错误(现在确实更简单了):

        @echo off
        setlocal EnableDelayedExpansion
        
        rem Create "Files with Fontnames" and "Fontnames in Files" sets, 
        rem and FileCount with number of Fontnames in each file
        for %%a in (*.txt) do (
           for /F "tokens=2 delims=," %%b in ('findstr /B /C:"Style:" %%a') do (
              set File[%%~Na]=!File[%%~Na]!"%%b",
              set Fontname[%%b]=!Fontname[%%b]!%%~Na,
              set /A FileCount[%%~Na]+=1
           )
        )
        
        echo Fonts by File:
        set File[
        echo/
        echo/
        
        echo Following files provide all fonts:
        
        rem For each non-processed "File with Fontnames"
        :nextFile
        
           rem Process File with larger number of Fontnames first
           set fontCount=0
           for /F "tokens=2,3 delims=[]=" %%a in ('set FileCount[') do (
              if %%b gtr !fontcount! (
                 set fontCount=%%b
                 set nextFile=%%a
              )
           )
           if %fontCount% equ 0 goto exit
        
           rem Show this file as result
           echo File %nextFile%.txt
        
           rem For each Fontname in this file
           for %%a in (!File[%nextFile%]!) do (
              rem Subtract this Fontname from the Files that include it
              for %%b in (!Fontname[%%~a]!) do (
                 set /A FileCount[%%b]-=1
              rem and delete this Fontname
              set Fontname[%%~a]=
              )
           )
        
        rem Go back to process next file
        goto nextFile
        
        :exit
        

        例如:

        Fonts by File:
        File[Subtitles01]=" Estrangelo Edessa"," Arno Pro",
        File[Subtitles02]=" Arno Pro"," Nueva Std Cond",
        File[Subtitles03]=" Estrangelo Edessa"," Arno Pro"," Nueva Std Cond",
        
        
        Following files provide all fonts:
        File Subtitles03.txt
        

        【讨论】:

        • 我不明白你的逻辑应该如何工作,但你的代码肯定不会。尝试为 02 添加一个独特的字体 - 答案应该是 01,02。然后给 03 添加一个独特的字体,答案应该是 02,03。最后给 01 添加一个唯一的字体,答案应该是 01,02,03。您的代码不会打印出完整的唯一字体集,也不会打印出包含所有唯一字体的一组文件名。
        • 是的。我的代码有一个小错误,现在已修复。我认为 OP 没有请求一组独特的字体,但很容易在我的代码中添加它...
        • 仍然没有。尝试使用 01:A,B 02:C,D 03:E,F 04:A,C,E 进行测试。正确答案是 01,02,03。您的代码给出 04,01,02,03。注意 - 我的代码中有一个错误,该错误未能通过现在修复的相同测试:-) 但我看不出如何使您的算法工作。您处理的顺序不应影响结果,但可能会影响性能。
        【解决方案5】:
        Style: (.*),(.*),(.*),(.*)
        

        然后,只需获得第二个匹配的结果。只需确保您使用 whole 字符串。不只是从括号开始。

        编辑

        抱歉,我错过了实际上有四个块,三个逗号,而不是三个块,两个逗号。代码现在可以正常工作并已修复。

        【讨论】:

        • 不。 That doesn't work. 请记住 . 匹配“任何字符”,包括逗号。您需要使其不贪婪,或不匹配逗号。 ([^,]* 可以在这里工作。)
        • @Li-aungYip 我看错了这个例子,我错过了一个逗号。我的代码已修复,并被视为有效here。如果您删除了反对票,我将不胜感激。 (:
        • 仍然不是很健壮。您依赖于恰好有四个字段 - 这不是一个好主意。更强大的版本可能类似于 ^Style: [^,]+,[^,]+,.+$
        猜你喜欢
        • 1970-01-01
        • 2021-10-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多