Извлечение строки между первой и второй запятыми в определенной строке во всех файлах в каталоге

Я пытаюсь извлечь строку, расположенную между первой и второй запятыми в определенной строке в серии текстовых файлов (файлов субтитров). Текстовые файлы форматируются следующим образом:

Субтитры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
)

Любая помощь будет оценена по достоинству. Спасибо вам, ребята!


person Jin Liu    schedule 09.07.2012    source источник


Ответы (5)


Я полагаю, что было бы намного проще использовать какой-либо другой язык помимо пакетной обработки или, по крайней мере, использовать неродные утилиты. Но вот чисто нативное пакетное решение.

Я не вижу, как регулярное выражение 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
person dbenham    schedule 09.07.2012

Изменить: используйте это:

^Style:\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*(.+)\s*
person Ria    schedule 09.07.2012
comment
\s лишние. [^,] соответствует всему, что соответствует \s. В противном случае хорошо. :) - person Li-aung Yip; 09.07.2012

Я понимаю, что вы, по-видимому, работаете на платформе, на которой по умолчанию не установлены awk или Perl, но вы все равно можете рассмотреть возможность установки одного из этих инструментов, особенно если вам нужно будет выполнять аналогичные задачи в будущем.

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

Или с Перлом:

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

Последующая оптимизация (удаление любых файлов, которые пересекаются с другими совпадающими файлами) будет несложной для любого языка. Perl лучше масштабируется для более крупных и разнообразных задач, поэтому, если вы не знакомы с обоими, это будет моя первая рекомендация (за счет более длинной и несколько более ухабистой кривой обучения, и тогда вам также следует подумать о Python).

person tripleee    schedule 09.07.2012

Если «Цель состоит в том, чтобы использовать наименьшее количество файлов для поиска уникальных шрифтов во всех файлах», то приведенный ниже пакетный файл решит вашу проблему:

РЕДАКТИРОВАНИЕ: упс! У меня была небольшая ошибка в моем предыдущем коде: я не должен был удалять весь файл при обработке каждого имени шрифта, содержащегося в целевом файле, просто вычесть это имя шрифта из числа файлов. Я исправил ошибку в приведенном ниже коде (теперь он действительно проще):

@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
person Aacini    schedule 10.07.2012
comment
Я не понимаю, как должна работать ваша логика, но ваш код точно не работает. Попробуйте добавить уникальный шрифт к 02, - тогда ответ должен быть 01,02. Затем добавьте уникальный шрифт к 03, и ответ должен быть 02,03. Наконец, добавьте уникальный шрифт к 01, и ответ должен быть 01,02,03. Ваш код не выводит ни полный набор уникальных шрифтов, ни набор имен файлов, содержащих все уникальные шрифты. - person dbenham; 10.07.2012
comment
да. В моем коде была небольшая ошибка, сейчас она исправлена. Я думаю, что ОП не запросил набор уникальных шрифтов, но это очень легко добавить в мой код... - person Aacini; 10.07.2012
comment
Все еще не там. Попробуйте выполнить тест с 01:A,B 02:C,D 03:E,F 04:A,C,E. Правильный ответ 01,02,03. Ваш код дает 04,01,02,03. Примечание. У меня была ошибка в моем коде, которая не прошла тот же тест, который исправлен сейчас :-) Но я не понимаю, как заставить ваш алгоритм работать. Порядок обработки не должен влиять на результат, хотя может повлиять на производительность. - person dbenham; 10.07.2012

Style: (.*),(.*),(.*),(.*)

Затем просто получите второй совпадающий результат. Просто убедитесь, что вы используете строку целиком. Не просто начиная со скобки.

ИЗМЕНИТЬ

Извините, я пропустил, что на самом деле было четыре блока с тремя запятыми, а не три блока с двумя запятыми. Код теперь работает и исправлен.

person Connor Deckers    schedule 09.07.2012
comment
Неа. Это не работает. Помните, что . соответствует любому символу, включая запятые. Вам нужно сделать его нежадным или не совпадать с запятыми. (здесь подойдет [^,]*.) - person Li-aung Yip; 09.07.2012
comment
@Li-aungYip Я неправильно прочитал пример и пропустил запятую. Мой код был исправлен и признан рабочим здесь. Я был бы признателен, если бы вы удалили отрицательный голос. (: - person Connor Deckers; 09.07.2012
comment
Еще не очень стойкий. Вы полагаетесь на то, что полей ровно четыре - не очень хорошая идея. Более надежная версия может выглядеть как ^Style: [^,]+,[^,]+,.+$. - person Li-aung Yip; 09.07.2012