WedX - журнал о программировании и компьютерных науках

awk с многострочным регулярным выражением; выходное имя файла на основе соответствия awk

В настоящее время я пытаюсь извлечь 300 с лишним функций и подпрограмм из файла 22kLoC и решил попробовать сделать это программно (я сделал это вручную для «самых больших» кусков).

Рассмотрим файл вида

declare sub DoStatsTab12( byval shortlga as string)
declare sub DoStatsTab13( byval shortlga as string)
declare sub ZOMFGAnotherSub

Other lines that start with something other than "/^sub \w+/" or "/^end sub/"

sub main

    This is the first sub: it should be in the output file mainFunc.txt

end sub

sub test

    This is a second sub

    it has more lines than the first.

    It is supposed to go to testFunc.txt

end sub

Function ConvertFileName(ByVal sTheName As String) As String

    This is a function so I should not see it if I am awking subs

    But when I alter the awk to chunk out functions, it will go to ConvertFileNameFunc.txt    

End Function

sub InitialiseVars(a, b, c)

    This sub has some arguments - next step is to parse out its arguments
    Code code code;
    more code;
    ' maybe a comment, even? 


  and some code which is badly indented (original code was written by a guy who didn't believe in structure or documentation)

    and


  with an arbitrary number of newlines between bits of code because why not? 


    So anyhow - the output of awk should be everything from sub InitialiseVars to end sub, and should go into InitialiseVarsFunc.txt

end sub

Суть: найти наборы строк, которые начинаются с ^sub [subName](subArgs) и заканчиваются на ^end sub

А затем (и вот что ускользает от меня): сохраните извлеченную подпрограмму в файл с именем [subName]Func.txt

awk предложил себя в качестве кандидата (в прошлом я писал запросы регулярных выражений для извлечения текста на PHP, используя preg_match(), но я не хочу рассчитывать на доступность WAMP/LAMP).

Моя отправная точка — восхитительно экономный (двойные кавычки, потому что Windows)

awk "/^sub/,/^end sub/" fName

Это находит соответствующие фрагменты (и печатает их на стандартный вывод).

Шаг по помещению вывода в файл и присвоение имени файлу после $2 захвата awk мне не по силам.

Более ранний этап этого процесса включал awk создание имен подпрограмм и их сохранение: это было легко, так как каждая подпрограмма объявляется однострочной строкой формы

declare sub [subName](subArgs)

Так что это делает это, и делает это отлично -

awk "match($0, /declare sub (\w+)/)
{print substr($3, RSTART, index($3, \"(\")>0 ? index($3, \"(\")-1: RLENGTH)
     > substr($3, RSTART, index($3, \"(\")>0 ? index($3, \"(\")-1: RLENGTH)\".txt\"}"
fName

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

Мне кажется, что если вывод

awk '/^sub/,/^end sub/' fName

был объединен в один массив, тогда $2 (соответственно усеченный в '(' ) будет работать. Но это не сработало.

Я просмотрел различные потоки SO (и других семейств SE), которые имеют дело с многострочным awk, например, этот и этот, но ни один из них не дал мне достаточно информации о моей проблеме (они помогают получить само совпадение, но не передать его в файл, названный в его честь).

У меня RTFD для awkgrep), тоже безрезультатно.

01.01.2015

  • Каков ваш ожидаемый результат? 01.01.2015
  • Никогда не использовал синтаксис '/start/,/end/', так как он делает тривиальные задания немного короче, но тогда даже чуть более сложные задания требуют полной перезаписи. Вместо этого всегда используйте /start/{f=1} f; /end/{f=0}. 02.01.2015

Ответы:


1

Я предлагаю

awk -F '[ (]*' '            # Field separator is space or open paren (for
                            # parameter lists). * because there may be multiple
                            # spaces, and parens only appear after the stuff we
                            # want to extract.
  BEGIN { IGNORECASE = 1 }  # case-insensitive pattern matching is probably
                            # a good idea because Basic is case-insensitive.
  /^sub/ {                  # if the current line begins with "sub"
    outfile = $2 "Func.bas" # set the output file name
    flag = 1                # and the flag to know that output should happen
  }
  flag == 1 {               # if the flag is set
    print > outfile         # print the line to the outfile
  }
  /^end sub/ {              # when the sub ends, 
    flag = 0                # unset the flag
  }
' foo.bas

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

Если, например, где-то в коде объявление подпрограммы разбито на две строки (это возможно с помощью _, я полагаю, хотя Basic - это не то, чем я занимаюсь каждый день), пытаясь извлечь имя подпрограммы из первой линия его определения бесполезна. Форматирование также может внести небольшие коррективы в необходимые шаблоны; такие вещи, как лишние пробелы в начале строки, потребуют обработки. Используйте этот материал только для одноразовых преобразований кода и убедитесь, что он дал желаемый результат, не поддавайтесь искушению сделать его частью обычного рабочего процесса.

01.01.2015
  • Это правильный подход, и, поскольку OP находится в Windows, поместите скрипт в файл и выполните его как awk -f script ... вместо того, чтобы пытаться иметь дело с правилами цитирования кошмаров Windows. 02.01.2015
  • Как вы догадываетесь, это определенно единичный случай (ранее я приводил в порядок код для начальных пробелов в объявлениях подпрограмм/функций). Интересно, что я не смог заставить это работать из командной строки (после того, как ковырялся в том, что Windoze не может «делать» одинарные кавычки - путем (1) экранирования двойных кавычек; (2) замены одинарных кавычек двойными): awk выдает синтаксическая ошибка (я смотрел на свою версию и не вижу ее). Альтернатива Джиддера (ниже) работала (опять же, после адаптации для Windoze). 05.01.2015
  • Я снова... проблема в командной строке заключалась в том, что мне нужна точка с запятой между outfile = $2\"Func.txt\" и flag = 1; когда это было сделано, это работало именно так, как хотелось. 05.01.2015

  • 2

    Еще один нестандартный способ

    awk -F'[ (]' 'x+=(/^sub/&&file=$2"Func.txt"){print > file}/^end sub/{x=file=""}' file
    

    Объяснение

    awk -F'[ (]'                   - Set field separator to space or brackets
    
    x+=(/^sub/&&file=$2"Func.txt") - Sets x to 1 if line begins with sub and sets file 
                                     to the second field + func.txt. As this is a 
                                     condition that is checking if x is true then the 
                                     next block will repeatedly be executed until x 
                                     is unset.
    
    {print > file}                 - Whilst x is true print the line into the set filename
    
    
    /^end sub/{x=file=""}          - If line begins with end sub then set both x and file 
                                     to nothing.
    
    02.01.2015
    Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

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

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]