Игнорировать запятую после обратной косой черты в строке в текстовом файле, используя awk или sed - PullRequest
2 голосов
/ 01 апреля 2019

У меня есть текстовый файл, содержащий несколько строк следующего формата:

name,list_of_subjects,list_of_sports,school
Eg1: john,science\,social,football,florence_school
Eg2: james,painting,tennis\,ping_pong\,chess,highmount_school

Мне нужно проанализировать текстовый файл и распечатать вывод полей, игнорируя экранированные запятые.Здесь это будут поля 2 или 3, например:

science, social
tennis, ping_pong, chess

Я не знаю, как игнорировать экранированные символы.Как я могу сделать это с awk или sed в терминале?

Ответы [ 7 ]

3 голосов
/ 01 апреля 2019

Замените \, символом, который обычно не содержится в ваших записях (например, \n), и восстановите его перед печатью.Например:

$ awk -F',' 'NR>1{ if(gsub(/\\,/,"\n")) gsub(/\n/,",",$2); print $2 }' file
science,social
painting

Поскольку первый gsub выполняется для всей записи (т. Е. $0), awk вынужден пересчитывать поля.Но второе выполняется только во втором поле (т.е. $2), поэтому оно не повлияет на другие поля.См .: Изменение полей .

Чтобы иметь возможность извлекать несколько полей с правильно экранированными запятыми, вам нужно gsub \n s во всех полях с циклом for, как в следующем примере:

$ awk 'BEGIN{ FS=OFS="," } NR>1{ if(gsub(/\\,/,"\n")) for(i=1;i<=NF;++i) gsub(/\n/,"\\,",$i); print $2,$3 }' file
science\,social,football
painting,tennis\,ping_pong\,chess

См. Также: Какой самый надежный способ эффективного анализа CSV с использованием awk? .

2 голосов
/ 01 апреля 2019

Вы можете заменить последовательности \, другим символом, который не появится в вашем тексте, разбить текст вокруг оставшихся запятых и заменить выбранный символ запятыми:

sed $'s/\\\,/\31/g' input | awk -F, '{ printf "Name: %s\nSubjects : %s\nSports: %s\nSchool: %s\n\n", $1, $2, $3, $4 }' | tr $'\31' ','

В этом случае с помощью управляющего символа ASCII «Разделитель единиц» \ 31, который, я уверен, ваш ввод не содержит.

Вы можете попробовать здесь .

0 голосов
/ 01 апреля 2019

Это может сработать для вас (GNU sed):

sed -E 's/\\,/\n/g;y/,\n/\n,/;s/^[^,]*$//Mg;s/\n//g;/^$/d' file

Замените запятые в кавычках на новые строки, а затем верните новые строки в запятые и запятые в новые строки. Удалите все строки, которые не содержат запятую. Удалить пустые строки.

0 голосов
/ 01 апреля 2019

Использование Perl. Измените \, на какой-нибудь контрольный символ, скажем, \x01, а затем замените его снова на ,

$ cat laxman.txt
john,science\,social,football,florence_school
james,painting,tennis\,ping_pong\,chess,highmount_school
$ perl -ne ' s/\\,/\x01/g and print ' laxman.txt  | perl -F, -lane ' for(@F) { if( /\x01/ ) { s/\x01/,/g ; print } } '
science,social
tennis,ping_pong,chess
0 голосов
/ 01 апреля 2019

Используйте gawk's FPAT:

awk -v FPAT='(\\\\.|[^,\\\\]*)+' '{print $3}' file
#list_of_sports
#football
#tennis\,ping_pong\,chess

затем используйте gnusub для замены обратной косой черты:

awk -v FPAT='(\\\\.|[^,\\\\]*)+' '{print gensub("\\\\", "", "g", $3)}' file
#list_of_sports
#football
#tennis,ping_pong,chess
0 голосов
/ 01 апреля 2019

Возможно, вы можете объединить столбцы с помощью функции.

function joincol(col,    i) {
    $col=$col FS $(col+1)
    for (i=col+1; i<NF; i++) {
        $i=$(i+1)
    }
    NF--
}

Это может быть использовано таким образом:

{
    for (col=1; col<=NF; col++) {
        if ($col ~ /\\$/) {
            joincol(col)
        }
    }
}

Обратите внимание, что уменьшение NF является неопределенным поведением в POSIX.Он может удалить последнее поле, а может и нет, и при этом быть совместимым с POSIX.Это работает для меня в BSDawk и Gawk.YMMV.Может содержать орехи.

0 голосов
/ 01 апреля 2019

Почему awk и sed, когда bash с coreutils достаточно:

# Sorry my cat. Using `cat` as input pipe
cat <<EOF |
name,list_of_subjects,list_of_sports,school
Eg1: john,science\,social,football,florence_school
Eg2: james,painting,tennis\,ping_pong\,chess,highmount_school
EOF
# remove first line!
tail -n+2 |
# substitute `\,` by an unreadable character:
sed 's/\\\,/\xff/g' |
# read the comma separated list
while IFS=, read -r name list_of_subjects list_of_sports school; do
     # read the \xff separated list into an array
     IFS=$'\xff' read -r -d '' -a list_of_subjects < <(printf "%s" "$list_of_subjects")
     # read the \xff separated list into an array
     IFS=$'\xff' read -r -d '' -a list_of_sports < <(printf "%s" "$list_of_sports")

     echo "list_of_subjects : ${list_of_subjects[@]}"
     echo "list_of_sports   : ${list_of_sports[@]}"
done

выдаст:

list_of_subjects : science social
list_of_sports   : football
list_of_subjects : painting
list_of_sports   : tennis ping_pong chess

Обратите внимание, что это, скорее всего, будет медленнее, чем решениеиспользуя awk.

Обратите внимание, что принцип действия такой же, как и в других ответах - замените строку \, другим уникальным символом, а затем используйте этот символ для итерации по второму и третьему элементам поля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...