Cum se utilizează sed pentru a înlocui caracterele în anumite poziții de linie? (Programare, Bash, Awk, Sed)

Robin Wang a intrebat.
a intrebat.

Încerc să procesez niște fișiere și vreau să transform aceste fișiere în fișiere .csv, așa că am nevoie să înlocuiesc unele caractere specifice (spațiu în cazul meu) cu virgulă (‘,’).Am crezut că acest lucru se poate face cu sed sau awk, dar nu am reușit să scriu comanda sed corectă.

De exemplu, un fișier de intrare arată ca mai jos (doar două linii de exemplu)

 112 322432 434543    4555 3223
 adg gdasgg dagdag    gdag gdsg
 ...

Rețineți că datele dintr-un fișier de intrare nu sunt neapărat separate de exact un spațiu, dar este garantat că fișierul de intrare este valid pentru înlocuirea caracterelor. și trebuie să înlocuiesc fiecare caracter de la poziția coloanei 3, 10, 17, 25 în fiecare linie. fișierul de ieșire corespunzător ar trebui să arate astfel

 112,322432,434543,   4555,3223
 adg,gdasgg,dagdag,   gdag,gdsg
 ...

Apropo, este posibil să scriem un script sed (în loc de hard code) care să putem defini un array care să conțină pozițiile pe care trebuie să înlocuim spațiul cu virgulă.

===================

Greșeala mea, înlocuirea spațiilor continue nu funcționează pentru cazul meu.


abcde abcde abcde abcde abcde
abcde abcde de bcde

Rândurile de mai sus arată problema cu care mă confrunt, unele câmpuri de date ar putea fi goale, dar nu pot fi ignorate. Din fericire, fișierul de intrare a garantat că toate câmpurile de date sunt plasate în poziția corectă conform documentului de proiect (lungimea fiecărui câmp este dată și sunt separate de un singur spațiu, deși în fișierul de intrare pot exista spații continue din cauza diferenței dintre lungimea cerută în document și lungimea reală a datelor).

6 răspunsuri
jijinp
sed -r 's/([^ ]) /1,/g' File

Găsiți șirul care se potrivește cu un caracter fără spațiu urmat de un spațiu apoi înlocuiți-l cu caracterul + ,

Ed Morton

WIth GNU awk pentru gensub():

$ awk '{print gensub(/([^ ]) /,"\1,","g")}' file
 112,322432,434543,   4555,3223
 adg,gdasgg,dagdag,   gdag,gdsg

$ awk -v pos='5 12 19 27' 'BEGIN{split(pos,a)} {for (i in a) $0=gensub(/./,",",a[i])} 1' file
 112,322432,434543,   4555,3223
 adg,gdasgg,dagdag,   gdag,gdsg

Numărătoarea dvs. a fost greșită cu 2 atunci când ați spus că doriți să înlocuiți caracterele de la pozițiile 3, 10, 17 și 25:

$ awk -v pos='3 10 17 25' 'BEGIN{split(pos,a)} {for (i in a) $0=gensub(/./,",",a[i])} 1' file
 1,2 3224,2 4345,3    45,5 3223
 a,g gdas,g dagd,g    gd,g gdsg

Adam

Cea mai simplă metodă este să folosiți awk handy FIELDWIDTH pentru a specifica lățimea coloanei, utilizând -F pentru a elimina separatorul de spațiu și -v OFS=, pentru a-l înlocui cu o comă:

 awk -v FIELDWIDTHS="3 7 7 8 4" -F" " -v OFS=, '{print $1,$2,$3,$4,$5,$6}' file

Aceasta returnează:

 112,322432,434543,4555,3223,
 adg,gdasgg,dagdag,gdag,gdsg,

John Bollinger

Și am nevoie să înlocuiesc fiecare caracter de la poziția coloanei 3, 10, 17, 25 în fiecare linie.

Presupun că acest lucru înseamnă că pot exista caractere de spațiu semnificative fie înainte, fie după delimitatorii de spațiu, astfel încât poziția pe linie este singura modalitate fiabilă de a identifica caracterele de înlocuit. Înțeleg, de asemenea, că nu vă interesează ce caracter se află în aceste poziții în fișierul original. Dacă într-adevăr trebuie să folosiți numere de caractere pentru a identifica locațiile pentru substituiri, atunci puteți proceda astfel:

sed -e 's/(.{3})./1,/'  
    -e 's/(.{10})./1,/' 
    -e 's/(.{17})./1,/' 
    -e 's/(.{25})./1,/' 
    input > output

Fiecare fragment efectuează o substituție în locația desemnată prin potrivirea tuturor caracterelor până la poziția de substituție inclusiv, prin capturarea celor care preced poziția de substituție și prin înlocuirea lor cu caracterele capturate plus o virgulă.

Alternativ, acest lucru este echivalent:

sed -e 's/(.{3}).(.{6}).(.{6}).(.{7})./1,2,3,4,/' 
    input > output

Comentarii

  • Îmi pare rău. Vă rugăm să ignorați primul spațiu din fiecare linie, iar eu am numărat indicele începând de la 0. –  > Por Robin Wang.
  • @RobinWang, da, mi-am dat seama. Indicii din codul de mai sus încorporează deja această înțelegere, dar am greșit limitele grupurilor de captură. Am actualizat codul pentru a remedia acest lucru și am furnizat, de asemenea, o versiune oarecum condensată. Versiunea condensată este mai greu de corelat cu cerințele dumneavoastră, dar ar putea rula ceva mai repede dacă aveți nevoie să procesați fișiere lungi. –  > Por John Bollinger.
Arjun Mathew Dan

Înlocuiți pur și simplu sequence of spaces cu ,

Exemplu cu sed:

sed -r 's/ +/,/g' File

Acest lucru vă va da CSV ieșire. Dar presupunem aici că datele în sine nu conțin niciun spațiu.

Comentarii

  • Acest lucru întrerupe formatarea. Puteți vedea în mod clar în exemplu că se dorește păstrarea mai multor spații. –  > Por 123.
  • Presupun că formatarea nu este prioritară aici, ci mai degrabă obiectivul este de a genera un fișier csv. Formatarea a venit ca parte a ideii sale de rezolvare a problemei. –  > Por Arjun Mathew Dan.
  • Greșeala mea, nu am clarificat clar că formatarea este de fapt importantă, deoarece câmpul de date din fișierul original este plasat în anumite poziții cu o anumită lungime. Fiecare câmp de date are lungimea sa proprie, iar spațiul este folosit pentru a umple lungimea rămasă (unele date pot fi chiar goale, dar nu le putem omite). De aceea, datele nu sunt neapărat separate de un singur spațiu. Înlocuirea spațiilor continue nu funcționează în cazul meu. –  > Por Robin Wang.
Michael Vehrs

Puteți face așa:

sed -r 's/(.{3})./1,/; s/(.{10})./1,/; ...'

Cu alte cuvinte, înlocuiți n caracterele urmate de un alt caracter, cu caracterul original n caracterele originale urmate de o virgulă. Aveți nevoie de o astfel de declarație pentru fiecare index, ceea ce este incomod. Cu toate acestea, puteți automatiza această traducere, utilizând, de asemenea, funcția sed:

 echo 3 10 17 25 | sed 's/ /
/g' | sed -r 's#(.*)#s/(.{1})./\1,/;#;' | sed -rf- input

Puteți elimina primul apel al sed cu prețul unui program puțin mai complex:

echo 3 10 17 25 | sed -r 's#([^ ]+)( |$)#s/(.{1})./\1,/;
#;P;D'  | sed -rf- input

Tags:, ,