1

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

字幕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
)

任何帮助,将不胜感激。谢谢你们!

4

5 回答 5

1

编辑:使用这个:

^Style:\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*(.+)\s*
于 2012-07-09T07:40:02.550 回答
1

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

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

或者使用 Perl:

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

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

于 2012-07-09T08:33:26.327 回答
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
于 2012-07-09T23:46:24.660 回答
1

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

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

@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
于 2012-07-10T00:51:36.990 回答
0
Style: (.*),(.*),(.*),(.*)

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

编辑

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

于 2012-07-09T07:26:03.380 回答