Скрипт AWK для обработки информации, распределенной по нескольким строкам файла - PullRequest
0 голосов
/ 27 марта 2019

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

111 0 1000 other stuff        #<- here a new element begins
      some text &             #<- "&" or white spaces increment - 
      some more               #<- signal continue on next line
      last line 
221 1 1.22E22                 # new element $2!=0 must be followed by float
   text &
   contiuned text
c comment line in between 
   more text &
last line
2221 88 -12.123 &
line1 
   line2
c comment line 
last line
223 0 lll -111        $ element given by line 
22 22 -3.14           $ element given by new line

Я бы хотел получить

111 0 1000 other stuff        #<- here a new element begins
      some text &             #<- "&" or white spaces increment - 
      some more               #<- signal continue on next line
      last line &
             xyz=1 
221 1 1.22E22                 # new element $2!=0 must be followed by float
   text &
   contiuned text
c comment line in between 
   more text &
last line &
      xyz=1
2221 88 -12.123 &
line1 
   line2
c comment line 
last line &
      xyz=1 
223 0 lll -111 &     $ element given by line
      xyz=1 
22 22 -3.14 &          $ element given by new line
      xyz=1

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

function newelement(line) {
  split(line, s, " ")
  if (s[1] ~/^[0-9]+$/ && ((s[2] ~/^[0-9]+$/ && s[3] ~/\./) || (s[2] == 0 && s[3] !~/\./))) {
    return 1
  } else {
    return -1
  }
}

function contline(line) {
  if (line~/&/ || line~/^[cC]/ || line~/^\s{3,10}[^\s]./) {
    return 1
  } else {
    return -1
  }
}

BEGIN {
  subs = " xyz=1 "
} #increment to have the next line in store
FNR == 1 {
  getline nextline < FILENAME
} 
{ 
  # get the next line
  getline nextline < FILENAME
  if (newelement($0) == 1 && NR < 3673) {
    if (length(old) > 0 || $0~/^$/) {
      printf("%s &\n%20s\n", old, subs)
      print $0
    } 
    # to capture one line elements with no following continuation
    # i.e.
    # 221 91 0.5 33333
    # 22  0  11
    #look at the next line
    else if (($0!~/&/ && contline(nextline) == -1)) {
      printf("%s &\n%20s\n", $0, subs)
    }
  } 
  else {
  print "-" $0
  }
  # store last not - commented line
  if ($0!~/^\s{0,20}[cC]/) old = $0

}

Где строка комментария имеет c или c, за которым следует пустой пробел. Строки комментариев должны быть сохранены, но к ним нельзя добавлять строки.

Ответы [ 2 ]

2 голосов
/ 28 марта 2019

Пожалуйста, проверьте следующий код и дайте мне знать, если он работает для вас:

$ cat 3.1.awk
BEGIN{
    subs      = " xyz=1 "
    threshold = 3673
}

# return boolean if the current line is a new element
function is_new_element(){
    return ($1~/^[0-9]+$/) && (($2 ~ /^[0-9]+$/ && $3~/\./) || ($2 == 0 && $3 !~/\./))
}

# return boolean if the current line is a comment or empty line
function is_comment() {
    return /^\s*[cC] / || /^\s*$/
}

# function to append extra text to line
# and followed by comments if applicable
function get_extra_text(     extra_text) {
    extra_text = sprintf("%s &\n%20s", prev, subs)
    text = (text ? text ORS : "") extra_text
    if (prev_is_comment) {
        text = text ORS comment
        prev_is_comment = 0
        comment = ""
    }
    return text
}

NR < threshold {
# replace the above line with the following one if 
# you want to process up to the first EMPTY line
#NR==1,/^\s*$/ {
    # if the current line is a new element
    if (is_new_element()) {
        # save the last line and preceeding comments 
        # into the variable 'text', skip the first new element
        if (has_hit_first_new_element) text = get_extra_text()
        has_hit_first_new_element = 1
        prev_is_new = 1
    # before hitting the first new_element line, all lines 
    # should be printed as-is
    } else if (!has_hit_first_new_element) {
        print
        next
    # if current line is a comment
    } else if (is_comment()) {
        comment = (comment ? comment ORS : "") $0
        prev_is_comment = 1
        next
    # if the current line is neither new nor comment
    } else {
        # if previous line a new element
        if (prev_is_new) {
            print (text ? text ORS : "") prev
            text = ""
        # if previous line is comment
        } else if (prev_is_comment) {
            print prev ORS comment
            prev_is_comment = 0
            comment = ""
        } else {
            print prev
        }
        prev_is_new = 0
    }
    # prev saves the last non-comment line
    prev = $0
    next
}
# print the last block if NR >= threshold 
!is_last_block_printed {
    print get_extra_text()
    is_last_block_printed = 1;
}

# print lines when NR > threshold or after the first EMPTY line
{   print "-" $0 }

Где

Строки разделены на 3 категории и обрабатываются по-разному:

  1. is_new_element() для истины, когда текущая строка является новым элементом, флаг prev_is_new идентифицирует предыдущий новый элемент
  2. is_comment(), функция для истины, затем текущаястрока комментария, prev_is_comment для идентификации предыдущей строки комментария
  3. другие строки: все остальные строки, кроме двух предыдущих

Другие примечания:

  • Вы можете выбрать NR < threshold (что составляет 3673 в вашем коде) или шаблон диапазона NR==1,/^\s*$/ для обработки только диапазона строк.
  • is_last_block_printed флаг и связанные с нимкод должен гарантировать, что последний блок обработки будет напечатан либо в конце вышеуказанного диапазона, либо в блоке END{}
  • . Я не проверял конечную строку & для продолжения строки, если они выполняютсяс помощью комментария или нового элемента должна быть определена логика, т.е.d имеет приоритет
  • Если перед первой строкой is_new_element() есть другие строки, код будет работать некорректно.Это можно исправить, добавив другой флаг вместо использования if (NR > 1) для обновления text.

Образец тестирования:

$ cat 3.1.txt
111 0 1000 other stuff        #<- here a new element begins
      some text &             #<- "&" or white spaces increment -
      some more               #<- signal continue on next line
      last line
221 1 1.22E22                 # new element $2!=0 must be followed by float
    text &
   contiuned text
c comment line in between
   more text &
last line
2221 88 -12.123 &
line1
   line2
c comment line 1
last line
c comment line 2
c comment line 3
c comment line 4
c comment line 5
223 0 lll -111        
223 0 22 -111        
223 0 22 -111        
c comment line in between 1
c comment line in between 2
22 22 -3.14         
c comment line at the end

Вывод:

$ awk -f 3.1.awk 3.1.txt
111 0 1000 other stuff        #<- here a new element begins
      some text &             #<- "&" or white spaces increment - 
      some more               #<- signal continue on next line
      last line  &
              xyz=1 
221 1 1.22E22                 # new element $2!=0 must be followed by float
   text &
   contiuned text
c comment line in between 
   more text &
last line &
              xyz=1 
2221 88 -12.123 &
line1 
   line2
c comment line 1
last line &
              xyz=1 
c comment line 2
c comment line 3
c comment line 4
c comment line 5
223 0 lll -111  &
              xyz=1 
223 0 22 -111  &
              xyz=1 
223 0 22 -111  &
              xyz=1 
c comment line in between 1
c comment line in between 2
22 22 -3.14    &
              xyz=1 
c comment line at the end

Некоторые дополнительные пояснения:

  • Одной из проблем обработки текста является завершающий символ новой строки "\ n" при добавлении строки subs к prev.это особенно важно, когда происходят последовательные строки new_element.

  • Важно отметить, что переменная prev в коде определяется как предыдущая строка без комментариев (категория-1, 3 определена выше).между строкой prev и текущей строкой может быть ноль или несколько комментариев (категория-2).именно поэтому мы используем print prev ORS comment вместо print comment ORS prev при печати обычных комментариев (не тех, которые предшествуют строке new_element).

  • Блок из comment строк (1 или более последовательныхстроки комментариев) сохраняются в переменную comment.если это прямо перед строкой new_element, тогда добавьте блок к переменной text.Все остальные блоки комментариев будут напечатаны в строке print prev ORS comment, упомянутой выше

  • Функция get_extra_text() предназначена для обработки extra_text в следующем порядке: prev subs ORS comments, гдеcomments добавляется только тогда, когда флаг prev_is_comment равен 1.Обратите внимание, что одна и та же переменная text могла бы сохранить несколько блоков prev subs ORS comments, если имеются последовательные строки new_element.

  • Мы только print в строке категории 3, упомянутой выше (ни новый_элемент, ни комментарий).Это безопасное место, когда мы не беспокоимся о конце новой строки или extra_text:

    • , если prev_is_new, мы печатаем кэшированную text, а затем переменную prev (которая является новым_элементом)
    • если prev_is_comment, мы просто печатаем prev ORS comment.Обратите внимание, что переменная prev сохраняет последнюю строку без комментариев из текущей строки, она не должна быть строкой прямо над текущей строкой.
    • во всех остальных случаях просто напечатайте строку prev как есть
  • Поскольку мы объединяем строки в переменные text и comment,мы используем следующий синтаксис, чтобы избежать ведущего ORS (который по умолчанию равен «\ n»)

    text = (text ? text ORS : "") prev

    Если ведущий ORS не имеет значения, вы можете просто использовать следующее:

    text = text ORS prev

    и поскольку строки добавляются к этим переменным, нам нужно будет сбрасывать их (т. Е. text = "") каждый раз после их использования, в противном случае - объединенная переменнаябудет содержать все ранее обработанные строки.

Заключительные примечания

  1. добавлен флаг has_hit_first_new_element, в случае если перед первой строкой new_element есть строки, они будутпечатается как есть.В этом коде первая строка new_element должна обрабатываться по-другому, с использованием NR == 1 не является безопасным поясом.
  2. удалил код в блоке END{}, который является избыточным
1 голос
/ 27 марта 2019

Попробуйте это:

function newelement(line){
    split(line,s," ")
    if(s[1]~/^[0-9]+$/ && ((s[2]~/^[0-9]+$/ && s[3]~/\./)|| (s[2]==0 && s[3]!~/\./))){return 1}
    else{return -1}
}

BEGIN{
    subs=" xyz=1 "
} 
{
    if (length($0)==0) next   # Skip empty lines, remove or change it according to your needs.
    if (newelement($0)==1){
        if (length(last_data)>0) {
            printf("%s &\n%20s\n",last_data,subs)
            if (last_type=="c") {
                print comments
            }
        }
        last_data=$0
        last_type="i"
    } else if($0 ~/^\s*[cC] /) {
        if (last_type=="c") comments = comments ORS $0
        else comments = $0
        last_type="c"
    } else {
        if (last_type=="c") print comments
        else if(length(last_data)>0) print last_data
        last_data=$0
        last_type="d"
    }
}
END{
    printf("%s &\n%20s\n",last_data,subs)
    if (last_type=="c") print comments
}

Три переменные:

  • last_data для хранения последней строки данных.
  • last_type для хранения типа последней строки, i для индикатора, c для комментариев.
  • comments для хранения строк комментариев.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...