Újabb regex kérdés

Az alábbi sorokból szeretném kiszedni az alábbi dolgokat:


Szállítási költség 1.025,00 x 0,8500 871,25 EUR
Valamilyen díj 1,00 x 17,4900 17,49 EUR
További szöveg 40 szám és egyéb 1,00 x 100,1900 100,19 EUR

Tehát szeretném kikapni az alábbi dolgokat belőle:

1. Szállítási költség
2. 1.025,00
3. 0,8500
4. 871,25

És a következő sorokat is ugyanúgy szétkapni.
Tehát az elején a szöveg mindig változik.

Ha a szöveg mindig ugyanaz lenne sikerülne is, de így lövésem sincs hogy szedjem szét.
Biztos van valami egyszerű módszer, de nem látom a fától az erdőt…

Hozzászólások

es hol tartasz most? vagy hol akadtal el?

t

^(.*?)([0-9,.]+) x ([0-9,.]+) ([0-9,.]+) EUR$
nem teszteltem, de elsore valami hasonloval probalkoznek.

Tyrael

igen, en is igy indultam neki.

php-ban az alabbi egysoros nalam a kovetkezo eredmenyt dobta a nyito hozzaszolasban levo tesztadatokra:


php -r '$matches = array();preg_match_all("/^(.*?)([0-9,.]+) x ([0-9,.]+) ([0-9,.]+) EUR$/m", file_get_contents("foo.txt"), $matches, PREG_SET_ORDER);var_dump($matches);'

array(3) {
  [0]=>
  array(5) {
    [0]=>
    string(52) "Szállítási költség 1.025,00 x 0,8500 871,25 EUR"
    [1]=>
    string(24) "Szállítási költség "
    [2]=>
    string(8) "1.025,00"
    [3]=>
    string(6) "0,8500"
    [4]=>
    string(6) "871,25"
  }
  [1]=>
  array(5) {
    [0]=>
    string(40) "Valamilyen díj 1,00 x 17,4900 17,49 EUR"
    [1]=>
    string(16) "Valamilyen díj "
    [2]=>
    string(4) "1,00"
    [3]=>
    string(7) "17,4900"
    [4]=>
    string(5) "17,49"
  }
  [2]=>
  array(5) {
    [0]=>
    string(63) "További szöveg 40 szám és egyéb 1,00 x 100,1900 100,19 EUR"
    [1]=>
    string(37) "További szöveg 40 szám és egyéb "
    [2]=>
    string(4) "1,00"
    [3]=>
    string(8) "100,1900"
    [4]=>
    string(6) "100,19"
  }
}

Tyrael

Éppenséggel (némi, a mintából adódó feltételezéssel) lehet elölről is haladni:


awk '{ match( $0, /[[:blank:]]+[0-9.,]+[[:blank:]]+x/); print substr( $0, 1, RSTART-1); $0 = substr( $0, RSTART); print $1 "\n" $3 "\n" $4 }'

(A cat mindig, és a -F most (alapértelmezett lévén a mezőszeparátor) megspórolható.)

Viszont kétségtelen, hogy ez nem tiszta re.

cat regex | awk -F ' ' '{for (i=1; i<=NF-6; i++) printf("%s ", $i); print $(NF-1) ";" $NF}'

Ez kiírja az első részt és az utolsó kettőt.

sed -r "s@^(.*) ([0-9.,]+) x ([0-9.,]+) ([0-9.,]+) EUR\$@1. \1\n2. \2\n3. \3\n4. \4\n@"

Ezen a "-r" opcion kicsit elcsodalkoztam, sose hallottam rola. man sed:


     -r      Same as -E for compatibility with GNU sed.

OK, akkor mi a -E, mert az se ismeros:


     -E      Interpret regular expressions as extended (modern) regular
             expressions rather than basic regular expressions (BRE's).  The
             re_format(7) manual page fully describes both formats.

Tehat ugyanaz, mint a grep-nel - extended regexp. Vegul tovabbolvasva:


STANDARDS
     The sed utility is expected to be a superset of the IEEE Std 1003.2
     (“POSIX.2”) specification.

     The -E, -I, -a and -i options, the prefixing “+” in the second member of
     an address range, as well as the “I” flag to the address regular expres‐
     sion and substitution command are non-standard FreeBSD extensions and may
     not be available on other operating systems.

Azaz jol gondoltam, valami linuxizm - OK, jelen esetben GNU-izm. (Amit speciel atvettek FreeBSD-ek is, de attol meg nem standard.) Es kozben kicsit jobban megnezve azt a RE-t, az is latszik, hogy az adott esetben osszesen annyit nyertunk a nem standard opcioval, hogy (valami) format irtal a \(valami\) forma helyett (hogy meglegyen a backreference-hez szukseges "mentese" a megtalalt sztringnek). Ha megnezem, hogy osszesen 4 backreference van a kodban, ez 4*2 hanyattortvonal sporolasa, csereben kiirtad az opciot (es egy plusz szokozt), azaz 5 karaktert sporoltal - igaz egy hangyanyival olvashatobb a kod. Ennyit az inkompatibilitas szerintem nem er. (Termeszetesen amikor mar a vilag minden UNIX rendszere kihal, es csak Linux fog futni a gepeken GNU-s cuccokkal; amikor mar nem lesz elerheto az UWIN, csak a Cygwin; nem lesznek alternativ unixos utility-k, csak a GNU-s cuccok - vagy amikor megvaltozik az eppen uralkodo POSIX-szabvany, akkor mar ez lesz a kovetendo eljaras. Addig - talan - erre is erdemes figyelni.)

NOT GOOD. (But not too bad - de ezt a regexre mondom, mert vegul is az volt a feladat.)

(Megjegyzem, ha " (idezojel) helyett ' (azaz aposztrof) lenne a parancssorban, akkor raadasul nem kene meg azzal is foglalkozni, hogy mi az a karakter, amit a shell meg idezojelen belul is ertelmez - mint pl. maga a \ bizonyos kombinaciokban. De nem ekezlek tovabb, te legalabb kitalaltad a regexpet, en csak raneztem es azt mondtam magaman, "igen,valszeg backreference segitsegevel valoszinuleg meg lehet oldani" - es hagytam a fenebe.)

Szerk: most latom, mivel idezojelbe tetted a sima sorvegjel $ helyett \$-t kellett irnod. Aposztroffal a hanyattortvonal megsporolhato :-)

Hogy neked mennyi bajod van? Én is mindig sed -r, grep -E az, amit írok. Jobban olvasható a kód, és a parancssori kapcsoló nyilván azért van, hogy használjuk. Továbbá előszeretettel használom bash-ben a dupla zárójelet, írok C-style for ciklust, és így tovább. Most mit mondjak erre? Szégyelljem magam amiatt is, hogy kihasználom az UTF-8 adta lehetőségeket, s nem maradok az ASCII karakterhalmazon belül egy kiírandó szöveg esetén?

tr [:lower:] [:upper:] <<<locsemege
LOCSEMEGE

Nem baja van, csak szereti a hordozható/szabványos dolgokat. A hanyattper megspórolása tényleg nem ér annyit, hogy platformfüggő megoldást adjon valaki rá.
Olyan jókat lehet egyébként derülni a gnuonly/linuxonly embereken, ha unixot kapnak a kezük alá... Semmi sem megy, semmi sem jó, minden máshogy van rinya jön, miközben "csak" szabványkövetőbb az adott környezet. Pontosabban unix. merthogy a GNU az nem az :)

A bash-only megoldás sem portábilis, mint ahogy a többi utility GNU-s kiegészítése sem az. Ezt szem előtt tartva lehet használni, de jó tudni, hogy más környezetben esetleg mitől és miért nem fog működni a megírt script, és hogyan lehet(ett volna) hordozhatóbban elkövetni azt.

Értem én ezt, de adott környezetben minek ezen rugózni? Ugyanis nyilván azért felülről kompatibilis a bash a POSIX shell-re, hogy a plusz feature-öket kihasználja az ember. Nem azért van egy csomó dolog implementálva, hogy csak annyit tudjon, ami a szabvány, de legalább lassabb legyen.

Nyilván, aki Linuxhoz szokott, bénázik Unixon, ugyanakkor a hibaüzenet után azért összeszedném magam, úgy érzem. Írhatok C-style for helyett seq-val felsorolt listát is, de akár beérhetem a while-lal, kell a fenének a for.

tr [:lower:] [:upper:] <<<locsemege
LOCSEMEGE

Nezd, en nem rugozok ezen, csak - nem-kotozkodeskent - jeleztem, hogy hogyan lehetne ugy megirni a jo megoldast, ami esetleg tobb helyen is mukodokepes. Fentebb emlitetted (meg en is) a "grep -E" hasznalatot. Azzal semmi bajom, szemely szerint az egrep format jobb szeretem ugyan, de mivel a POSIX ezt preferalja (ha jol tudom), kezdek hozzaszokni. Mivel a sed-nek ilyen opcioja a POSIX-ban nincs, csak par elvetemult megvalositasban (egyelore amirol tudok, a GNU-sed, es a FreeBSD-fele), ellenben az adott esetben *minimalis* modositassal elerheto a szabvanyos (tehat valoszinuleg tobb helyen mukodo) javitas, odairtam ezt is.

Sokadszor probalom magyarazni azt, hogy mindeki jobban jar azzal, ha ilyesmire is felhivja valaki a figyelmet. (Van itt ember, aki az egyelore - egyenlore hibakra szokott reagalni, mas a musza*ly*-ra harap, en ha eszreveszem, akkor a portabilisabb megoldast szoktam jelezni.) Mast ne mondjak, a kerdezo *valoszinuleg* (meg szerintem is) Linuxon akarja a feladatot megoldani, ellenben mivel ez sehol nem szerepelt, ezert ugy gondoltam, hogy ha abban az el (nem) hanyagolhatoan csekely helyzetben van, hogy megis valami mas *X rendszerben probalna megcsinalni, akkor hol segithet magan. (Termeszetesen hihettem volna Windows-t is - bar ugye h*U*p az oldal -, es pont mivel U es nem L, ezert segitettem.)

ez mar kotozkodes: A C-szeru for ciklus bash-izm. for ciklusban seq-et irni meg plane sajnos tipikus Linuxizm, en ha lehet, mindenkit figyelmeztetek arra, hogy ha a legcsekelyebb eselye van annak, hogy a kodja mashol is futni fog, mint Linux, akkor irja at (nagy esellyel while-ra), mert seq se nagyon van mas rendszeren. Nyilvan a sajat magad - es csak sajat magad - altal Linuxon - es csak Linuxon - futo szkript eseteben tok mindegy, de mondjuk ha szembejon egy alkalmazas, amit esetleg terjeszt a fejlesztoje, netan utal is arra, hogy portolas, miegyeb, akkor megirom neki, hogy mit tehet a jobb portolhatosag kedveert. (Volt mar ilyen. Van aki elfogadja, es van aki szarik ra. Pl. a Fotoxx fejlesztoje kedvesen azt valaszolta az en FreeBSD-portolasi levelem kapcsan, hogy o konkretan leszarja, minek barmi egyeb az Ubuntun kivul? Anyam!)

ez-meg-vegkepp-kotozkodes: az alairasod - noha azota ertem, amiota lattam, sose szoltam miatta. De akkor most kotozkodeskent jelzem, tobb sebbol verzik:

a harom csibecsor - bashizm

ellenben az, hogy nem teszed aposztrofok (rosszabb esetben idezojelek) koze (netan nem takargatod \-sel a zarojeleket), az azt jelenti, hogy amikor tesztelted, szerencsed volt:


$ bash
$ mkdir X
$ cd X
$ tr [:lower:] [:upper:] <<< locsemege
LOCSEMEGE
$ touch l L o O
$ tr [:lower:] [:upper:] <<< locsemege
usage: tr [-Ccsu] string1 string2
       tr [-Ccu] -d string1
       tr [-Ccu] -s string1
       tr [-Ccu] -ds string1 string2
$ rm o O
$ tr [:lower:] [:upper:] <<< locsemege
Aocsemege
$ 

Kulonosen az utolsot javaslom a figyelmedbe. Aki megerti, hogy miert az az eredmeny, az mar kezdi megsejteni, hogy hogyan mukodik a shell, es a tr :-)

Jogos, globbing. Még szerencse, hogy ki szoktam javítani, ha valaki ezt írja:

rpm -qa kernel*

erre:

rpm -qa kernel\*

A here string-ről nem vagyok hajlandó lemondani, s nem fogok echo után pipe-olni. :) A bash képességei azért vannak, hogy használjuk. Ugyanakkor a globbingra valóban nem figyeltem, köszönöm, hogy szóltál, s szégyellem is magam miatta.

Az Aocsemege hogy jött ki? Nekem egy

tr: misaligned [:upper:] and/or [:lower:] construct

üzenetre futotta. Az aláírás egyébként azért van, mert olvastak már Iocsemegének is. Gondoltam, ezt feloldanám így.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Ezen a ponton speciel a gnu tr-je intelligensebb, mint akár az AIX-é, akár a Zahy által használt.

Az Aocsemege onnan jön, hogy - mivel az [:upper:] nem illeszkedik fájlra - az megmarad feloldásre a tr-nek, ami legyártja az A..Z halmazt, és át is mappel is... legalábbis Zahynál.
A gnu tr-je kiszúrja, hogy az 1. halmaz rövidebb, és beint.
Az AIX-é teljesen barom, ami a hibaüzenetet illeti:

> set -x
> tr [:lower:] [:upper:] <<< locsemege
+ tr l '[:upper:]'
tr: 0653-715 String2 contains an invalid character class.

Nem teljesen ertek egyet azzal, hogy kiabal, ha a hatso sztring hosszabb. Leginkabb azert, mert az definicio szerint engedelyezett, hogy az elso hosszabb legyen; akkor meg mar minek nyafogni a miatt, hogy megadtunk nehany olyan karaktert is, amire az eg adta vilagon semmi nem fog konvertalodni.

Azért bármilyen dicséretes a szabálkövetés, Eichmann óta tudjuk, hogy van az úgy, hogy maga a szabál az, ami életidegen, és itt ilyesmit érzek. A mapping pont az a dolog, ahol konkrét valamik tartanak konkrét valamik felé, és ha a konkretizálás mégsincs meg, akkor jó eséllyel valami el van bökve (az AIX példájából látszik, még stallmanistának se kell lenni az ilyen világnézethez) - a kiinduló példa pont egy ilyen elbökés korai kiszúrása.

Hej, ha 1-2 évvel korábban lett volna a közelemben egy Zahy, hogy aggályoskodjon egy kicsit! Feleannyit nem szívtam volna, amikor az AIX megvillantotta, milyen az, ha egy lépéssel közelebb áll valami a sztenderdhez (és persze jól félre is sasszézik egyet, hogy még kalandosabb legyen az élet).

^([:alpha:]{1,})[:blank:]{1,}([0-9.,]{1,})[:blank:]{1,}x[:blank:]{1,}([0-9.,]{1,})[:blank:]{1,}([0-9.,]{1,})[:blank:]{1,}EUR[:blank:]*$

Próbáltam minél általánosabbra csinálni.

Azért annyiban hadd korrigáljam, hogy a karakterosztályok csak karakterlistában használhatók, vagyis mindenhol, ahol [:alpha:] szerepel, ott annak kéne állnia, hogy [[:alpha:]] (különben olyan karakterlistát jelent a kód, amely : a l p és h karaktereket sorol fel.

A [:blank:] dettó.