Разделить список через запятую номером строки, используя awk и bash - PullRequest
1 голос
/ 04 февраля 2020

У меня есть (очень большой) CSV-файл в следующем формате:

id;surname;firstname;aliases
1;Simpson;Homer;Homer Jay Simpson,Homer J. Simpson
2;Simpson;Bart;Bartholomew JoJo Simpson,Bartholomew Simpson
3;Krusty the Clown;;Herschel Shmoikel Pinchas Yerucham Krustofsky
4;Simpson;Lisa;

Теперь я хочу преобразовать его в следующий формат:

id;name
1;Homer Simpson
1_1;Homer Jay Simpson
1_2;Homer J. Simpson
2;Bart Simpson
2_1;Bartholomew JoJo Simpson
2_2;Bartholomew Simpson
3;Krusty the Clown
3_1;Herschel Shmoikel Pinchas Yerucham Krustofsky
4;Lisa Simpson

По соображениям производительности я бы как сделать это с помощью awk или другого UNIX -командного инструмента строки.

С помощью awk -F ';' '{print $1, $3, $2}' я могу отделить точку с запятой через точку. Но как мне использовать awk в awk для разделения разделенной запятой записи?

Ответы [ 4 ]

4 голосов
/ 04 февраля 2020
$ cat tst.awk
BEGIN { FS=OFS=";" }
NR==1 {
    print $1, "name"
    next
}
{
    name = $3 " " $2
    gsub(/^ +| +$/,"",name)
    print $1, name
    n = split($NF,aliases,/,/)
    for (i=1; i<=n; i++) {
        print $1 "_" i, aliases[i]
    }
}

$ awk -f tst.awk file
id;name
1;Homer Simpson
1_1;Homer Jay Simpson
1_2;Homer J. Simpson
2;Bart Simpson
2_1;Bartholomew JoJo Simpson
2_2;Bartholomew Simpson
3;Krusty the Clown
3_1;Herschel Shmoikel Pinchas Yerucham Krustofsky
4;Lisa Simpson
2 голосов
/ 04 февраля 2020

Не могли бы вы попробовать следующее (написано и протестировано с показанными образцами).

awk '
BEGIN{
  FS="[;,]"
  OFS=";"
  print "id;name"
}
FNR>1{
  j=$2~/ /?2:3
  for(i=j;i<=NF;i++){
    if($i==""){
      continue
    }
    if(i==j){
      print $1,$3" "$2
    }
    else{
      print $1"_"++c,$i
    }
  }
  c=""
}' Input_file

Вывод будет следующим.

id;name
1;Homer Simpson
1_1;Homer Jay Simpson
1_2;Homer J. Simpson
2;Bart Simpson
2_1;Bartholomew JoJo Simpson
2_2;Bartholomew Simpson
3; Krusty the Clown
3_1;Herschel Shmoikel Pinchas Yerucham Krustofsky
4;Lisa Simpson

Объяснение: Добавление подробного объяснения приведенного выше кода здесь.

awk '                        ##Starting awk program from here.
BEGIN{                       ##Starting BEGIN section from here.
  FS="[;,]"                  ##Setting field as either semi-colon OR comma for all lines.
  OFS=";"                    ##Setting output field separator semi-colon.
  print "id;name"            ##Printing id;name string before reading Input_file.
}                            ##Closing BLOCK for BEGIN block of this awk program here.
FNR>1{                       ##Checking condition if FNR>1 then do following.
  j=$2~/ /?2:3
  for(i=j;i<=NF;i++){        ##Running a for loop from i=j to till number of fields of line.
    if($i==""){              ##Checking condition if current field value is NULL then do following.
      continue               ##Using continue to take cursor to for loop again here.
    }
    if(i==j){                ##Checking condition if i==3 then do following.
      print $1,$3" "$2       ##Printing first, 3rd,space and 2nd field of line here.
    }
    else{                    ##If above if condition is false then come to this else here.
      print $1"_"++c,$i      ##Printing first field underscore variable c value, value of current field here.
    }
  }
  c=""                       ##Nullifying variable c here.
}
'  Input_file                ##Mentioning Input_file name here.
0 голосов
/ 04 февраля 2020

Это будет сделано так, как вы намеревались в Python 3. Обратите внимание, что я набрал его быстро, поэтому можно сделать много улучшений. Я считаю, что это может быть быстрее, чем awk, но я могу ошибаться. Вы можете проверить это, используя команду time в Linux и Ma c.

#!/usr/local/bin/python3

import csv
csvr = csv.reader(open('simpsons.csv'), delimiter = ";")

index=0
for row in csvr:
    if index == 0:
        index = index +1
        continue
    print("{};{} {}".format(index,row[2],row[1]))
    sindex=0
    for sitem in row[3].split(','):
        if sitem != "" :
            sindex = sindex + 1
            print("{};{}".format(row[0] + "_" + str(sindex),sitem))
    index = index +1

Надеюсь, это поможет!

Редактировать:

Я сгенерировал фиктивный список из 500 тыс. Строк и проверил некоторые ответы, данные здесь пользователями, и, похоже, между Python 3 и awk нет существенной разницы. (По крайней мере, в моей плохой реализации в Python 3).

 $ time awk -f tst.awk fivehundredthousand.txt &> /dev/null

real    0m2.141s
user    0m2.118s
sys     0m0.020s

 $ time ./handle_csv.py >/dev/null

real    0m1.750s
user    0m1.722s
sys     0m0.021s

$ time awk -f ravinder.awk fivehundredthousand.txt &> /dev/null

real    0m1.736s
user    0m1.718s
sys     0m0.017s
0 голосов
/ 04 февраля 2020

В Awk есть функция split, которая позволяет разбивать строки на массивы.

awk -F ';' 'BEGIN { OFS=FS }
  { print $1, $3 " " $2
    n = split($4, alias, /,/)
    for(i=1; i<=n; i++)
      print $1 "_" i, alias[i] }' file.csv

Возвращаемое значение из split сообщает, сколько членов содержится в массиве результатов.

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