Переменные GAWK concat в FOR

Мои текущие сценарии GAWK берут файл фраз и создают массив шаблонов регулярных выражений, затем разбивают каждую строку на символ \t и зацикливают первые 10 столбцов каждой строки, затем он проверяет, содержит ли он хотя бы одну фразу из массива шаблонов, если это так, он пропускает строку и не печатает ее в документе.

Проблема:

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

(700 шаблонов x 10 столбцов (разделенных табуляцией \t)) x 1000 строк.

Решение:

Чтобы повысить скорость, я хотел бы объединить первые 10 столбцов и проверить, содержит ли вся строка хотя бы один шаблон. Я не могу понять, как объединить строки в цикле FOR.

Рабочий пример:

gawk 'BEGIN{
FS=" *\t *";
IGNORECASE=1;

while(getline a < "'$phpath'") PATS["^.*"a".*$"]
}

{
    ok=1;
    for(i=1;i<=10;i++){
        for(p in PATS){
            if($i ~ p){
            ok=0
            }
        }
    }

} 
ok {print}' "$f" > "$newPath$filename" 

Моя попытка:

gawk 'BEGIN{
    FS=" *\t *";
    IGNORECASE=1;

    while(getline a < "'$phpath'") PATS["^.*"a".*$"]
    }

    {
        phrase="";
        space=" ";
        ok=1;

        for(i=1;i<=10;i++){
            phrase = $space $phrase $i
        }

        for(p in PATS){
            if($phrase ~ p){
                ok=0
            }
        }

    } ok {print}' "$f" > "$newPath$filename"

person Sam Axe    schedule 15.11.2016    source источник


Ответы (3)


В awk вы используете $ как оператор разыменования, где $x означает "дайте мне значение столбца, номер которого находится в переменной x"

Чтобы получить первые 10 столбцов в строку:

    for (i=1; i<=10; i++) {
        # not this => phrase = $space $phrase $i
        phrase = space phrase $i
    }

а также

    for (p in PATS) {
        if (phrase ~ p) {   # <= no $
            ok = 0
            break           # no match, so break the loop early
        }
    }

awk использует такие переменные, как C, а не shell или perl


Вы также можете попробовать это:

gawk -v patternfile="$phpath" '
    BEGIN {
        FS = " *\t *"
        IGNORECASE = 1
        while ((getline a < patternfile) > 0)
            PATS["^.*"a".*$"]
    }
    {
        line = $0
        NF = 10         # this truncates the current record to 10 fields
        ok = 1
        for (p in PATS) 
            if ($0 ~ p) {
                ok = 0
                break
            }
        if (ok) 
            print line
    }
' "$f" > "$newPath$filename"
person glenn jackman    schedule 15.11.2016
comment
спасибо за очень четкое объяснение и предложенное решение. Ваш код выглядит намного легче. Нужен ли мне перерыв при зацикливании шаблонов в предложенном вами решении? - person Sam Axe; 16.11.2016
comment
Это войдет в бесконечный цикл, если он обнаружит ошибку чтения файла шаблона, поскольку getline вернет -1. Вам нужно while ( (getline a < patternfile) > 0 ), см. awk.freeshell.org/AllAboutGetline. Вы можете выполнить сравнение без цикла, создав строку, разделенную |, из файла шаблона вместо заполнения массива. - person Ed Morton; 16.11.2016
comment
Я чувствую, что зацикливание со многими маленькими регулярными выражениями может быть быстрее, чем одно гигантское регулярное выражение. Хотя нужно будет сравнить. - person glenn jackman; 16.11.2016

Это не ответ на ваш вопрос, но, возможно, на вашу проблему.

Я понимаю, что ваша проблема связана с производительностью.

Насколько я понимаю, одна из основных проблем, с которыми вы сталкиваетесь, заключается в том, что вы используете RegEx. Позвольте мне объяснить мою точку зрения. В AWK, когда вы работаете с регулярным выражением, подобным этому: /MyRegExp/, вы используете скомпилированную версию RegEx, поэтому каждый раз, когда вам нужно проверить соответствие, вы только проверяете его, но поскольку вы используете RegEx следующим образом: «MyRegExp», это компилируется каждый раз, когда вы хотите проверить, соответствует ли строка.

Вы действительно проверяете RegEx? Может быть, это не так, и функция «индекс» для вас достаточно хороша.

Почему бы вам не попробовать создать скрипт и запустить его? Вместо того, чтобы проверять каждую строку во втором файле на соответствие загруженным шаблонам, вы создаете такой скрипт:

/pattern1/{
    print
    next
}
/pattern2/{
    print
    next
}
/pattern3/{
    print
    next
}
...
...

А затем запустить его со вторым файлом. В любом случае, я надеюсь, что это может быть полезно.

person kcoder24    schedule 15.11.2016

while(getline a < "'$phpath'") PATS["^.*"a".*$"]

RE ^.*"a".*$ эквивалентно a. И вместо того, чтобы повторять шаблоны, вы можете указать условие ИЛИ напрямую с помощью |.

Если ваш входной файл

every
good
boy
does
fine

ваш RE становится every|good|boy|does|fine, а ваш код сокращается до

$0 ~ pattern { 
    for (i=1; i<=10; i++) {
        if( $i ~ pattern ) { 
           print "$f" > "$newPath$filename" # what's $f?  
           break
        }
    }
}

То есть сначала просканируйте всю строку. Если он что-то находит, перебираем первые 10 столбцов. Бьюсь об заклад, это быстрее, чем безоговорочно перебирать их.

person James K. Lowden    schedule 15.11.2016
comment
Мне нужны шаблоны, потому что столбцы не содержат точных фраз. например: test my phrase test, test, test, test, test, test, test для захвата my phrase мне нужно регулярное выражение. - person Sam Axe; 16.11.2016