Kirjoittaja Aihe: BASH:in säännöllinen lauseke (regex), atomic-group  (Luettu 4302 kertaa)

petteriIII

  • Käyttäjä
  • Viestejä: 601
    • Profiili
Minun mielestäni esimerkki on jonkinverran käyttökelpoinen yksinäänkin, mutta teoria on vahingollinen ilman esimerkkiä eli sovellusohjetta.

Atomic-group:ista ei esitetä missään kunnollista esimerkkiä mutta kelvottomia esimerkkejä runsaasti. Kunnon esimerkillä tarkoitan sellaista esimerkkiä josta selviää se jokapaikassa väitetty nopeutus edes jotenkin samoinkuin katastrofeilta suojaava ominaisuus. Yhden hölmön esimerkin löysin regex-info sivuilta - tosin kokonaisuuteen piti hakea palasia monesta paikasta. Lisäksi tämä selvittää vasta katastrofeilta suojaamista:
Koodia: [Valitse]
echo '< kakkularatsis ja hilipata hoi >'  | grep -Po '(\D+|<\d+>)*[!?]'     # tämä kestää minun koneessani noin 60-100ms ja päätyy virheeseen.
echo '< kakkularatsis ja hilipata hoi >'  | grep -Po '((?>\D+)|<\d+>)*[!?]' # atomic-group:illa varustettuna sama kestää noin 4ms, ei pääty virheeseen muttei tulostakaan mitään
echo '< kakkularatsis ja hilipata hoi >?' | grep -Po '((?>\D+)|<\d+>)*[!?]' # tämä kestää saman noin 4ms ja tulostaa:  ? 
- atomic-group on tuo kahdessa viimeisessä esimerkissä oleva: (?>\D+) .
**
atomic-group:in nopeutuksesta osaan esittää vain yhden täysin mielipuolisen esimerkin:
Koodia: [Valitse]
echo 'kymmenentuhatta a-kirjainta peräkkäin' | grep -P '(?>a*)' > /dev/null      # tämän pitäisi olla se nopeampi ja kyllähän se hieman onkin
echo 'kymmenentuhatta a-kirjainta peräkkäin' | grep -P 'a*' > /dev/null
- muissa esimerkeissä pilasin yhden kovalevyn miljoonilla toistoilla (esimerkiksi suoritusajan keskiarvon saamiseksi) ja tuloksena oli vain epäilys että atomic-group yleensä päinvastoin kuluttaa aikaa.
**
Atomic-group:in tarkoituksena on, että valitaan käyttöön joko yksinkertainen- tai monimutkainen regex siten, että monimutkainen ei saa alkaa samallatavoin kuin yksinkertainen -> "hienommin" sanottuna estetään backtracking.

Mikäli regex:ät muodostetaan käsin koodaamalla  on atomic-group:in käyttäminen hölmöä, joten seuraavissa esimerkeissä ei sinällään ole järkeä. Atomic-group:in käyttökelpoisuus perustuukin siihen että pätevämmissä hyötyohjelmissa regex:ät muodostetaan muuttujilla.
- ehkä tuo väite että regex:ät voidaan muodostaa muuttujilla kaipaa sekin osoitusta: regex="[0-9]"; echo "9 aaa b" | grep -o "$regex" # tulostaa: 9

Seuraavat esimerkit kertovat atomic-group:in toiminnan koodina:
Koodia: [Valitse]
echo accc  | grep -Po 'a(?>b|cc)c'   # tulostaa: accc -> siis ?>b:llä ei ole mitään funktiota koska "echo:tussa" ei b:tä ole.           
echo abcc  | grep -Po 'a(?>b|bc)c'   # tulostaa: abc  -> siis ?>b määrää ettei muita vaihtoehtoja koeteta kun b löytyy
echo abccc | grep -Po 'a(?>b|cc)c'   # tulostaa: abc  -> siis ?>b määrää ettei muita vaihtoehtoja koeteta kun b löytyy (varmistus)
     
echo abac  | grep -Po 'a(?>bb|ba)c'  # tulostaa: abac -> siis ?>bb määrää, että bb-alkuiset on lukittu mutta kaikki muut toimii kyllä, vaikka ba:kin toimii.
echo abc   | grep -Po 'a(?>bb|b)c'   # tulostaa: abc  -> siis ?>bb määrää, että bb-alkuiset on lukittu mutta kaikki muut toimii kyllä, vaikka b:kin toimii.
echo abbac | grep -Po 'a(?>bb|bba)c' # ei tulosta mitään, koska ?>bb määrää, ettei bb-alkuisten regex:ät eivät voi saada muita arvoja.
**
Atomic tarkoittaa: jakamaton eikä sitä voi muuttaa kun se on kerran tehty.

Kun BASH:issa atomic liitetään ryhmään ei koko ryhmässä voi muuttaa mitään niin kauan kuin atomic on joukossa. 
 
« Viimeksi muokattu: 09.01.16 - klo:07.11 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 601
    • Profiili
Vs: BASH:in säännöllinen lauseke (regex), atomic-group
« Vastaus #1 : 01.04.16 - klo:09.42 »
Vihdoinkin sain atomic-group:ille normaalissa toiminnassa todella  merkitsevää käyttöä heti vuoden ihmettelyn jälkeen - atomic-groupin toimintaa "selvennetään" parillakin korkea-tasoisella tiiliskiven kokoisella sivustolla. Mutta taas kertaalleen tämä sai ihmettelemään onko noiden "virtuoosi-sivustojen" tarkoitus neuvoa vai kätkeä toiminta niin ettei siitä saa selvää, sillä niiden esittämät esimerkit eivät ole tästä maailmasta.

Seuraava pätee kaikkiin regex:iin ja poistaa niiltä joitain pahoja puutteita:

Esimerkiksi kun muodostetaan päiväykselle regex niin jos odotetaan päiväystä muodossa vvvv.kk.pp niin mikäli kohdassa pp on jotain muuta kuin luku 01-31 niin kysymys ei voi olla päiväyksestä lukipa muualla mitätahansa, tai jos kohdassa kk on jotain muuta kuin luku 01-12 kysymys ei voi olla päiväyksestä lukipa muualla mitätahansa. Tämä esitettynä koodina jolla toimintaa voi tutkia:
Koodia: [Valitse]
echo 2001.02.12 | grep -P '[0-9][0-9][0-9][0-9][.-](?>[0-2][0-9]|30|31)[.-](?>[0][0-9]|10|12)'
- tottakai tämä on liian rajoittavaa eikä salli että hommissa mukana pyörii virheitä tekevä ihminen joka lisäksi saattaa jättää merkityksettömiä nollia pois elikä kirjoittaa päivän 09 tilalle 9. Mutta sen voi ottaa huomioon koodimuutoksin tai käyttämällä toisia työkaluja.
- samanaikaisesti tämä on liian lepsua - muutta esimerkiksi ankkurit auttavat jo paljon ( esimerkiksi määrätään että päiväys voi olla vain rivin alussa ).
- huomioi että koodissa on kaksi atomic-group:ia.
- tuo [.-] sallii että välimerkkinä voi olla joko . tai -




nm

  • Käyttäjä
  • Viestejä: 13761
    • Profiili
Vs: BASH:in säännöllinen lauseke (regex), atomic-group
« Vastaus #2 : 02.04.16 - klo:13.34 »
Vihdoinkin sain atomic-group:ille normaalissa toiminnassa todella  merkitsevää käyttöä heti vuoden ihmettelyn jälkeen - atomic-groupin toimintaa "selvennetään" parillakin korkea-tasoisella tiiliskiven kokoisella sivustolla. Mutta taas kertaalleen tämä sai ihmettelemään onko noiden "virtuoosi-sivustojen" tarkoitus neuvoa vai kätkeä toiminta niin ettei siitä saa selvää, sillä niiden esittämät esimerkit eivät ole tästä maailmasta.

Seuraava pätee kaikkiin regex:iin ja poistaa niiltä joitain pahoja puutteita:

Esimerkiksi kun muodostetaan päiväykselle regex niin jos odotetaan päiväystä muodossa vvvv.kk.pp niin mikäli kohdassa pp on jotain muuta kuin luku 01-31 niin kysymys ei voi olla päiväyksestä lukipa muualla mitätahansa, tai jos kohdassa kk on jotain muuta kuin luku 01-12 kysymys ei voi olla päiväyksestä lukipa muualla mitätahansa. Tämä esitettynä koodina jolla toimintaa voi tutkia:
Koodia: [Valitse]
echo 2001.02.12 | grep -P '[0-9][0-9][0-9][0-9][.-](?>[0-2][0-9]|30|31)[.-](?>[0][0-9]|10|12)'

Sama tulos saadaan nähdäkseni tavallisilla ryhmillä: '[0-9][0-9][0-9][0-9][.-](?:[0-2][0-9]|30|31)[.-](?:[0][0-9]|10|12)'. Esimerkissäsi atomisuudesta on hyötyä suorituskyvyn optimoinnissa (johon sitä useimmiten käytetäänkin), eli virheellinen arvo viimeisessä kentässä ei aiheuta edellisen ryhmän uudelleenyrittämistä. Eli esimerkiksi syötteellä "2001.02.13" viimeinen numero 3 ei täsmää säännölliseen lausekkeeseen. Atomisella lausekkeella regex-moottori ei backtrackaa vaan päätyy saman tien hylkäävään tulokseen. Tavallisilla ei-atomisilla ryhmillä palataan lausekkeessa taaksepäin ja kokeillaan täsmätä syötteen 02:ta 30:een ja sen epäonnistuessa vielä 31:een.
« Viimeksi muokattu: 02.04.16 - klo:13.36 kirjoittanut nm »

petteriIII

  • Käyttäjä
  • Viestejä: 601
    • Profiili
Vs: BASH:in säännöllinen lauseke (regex), atomic-group
« Vastaus #3 : 13.04.16 - klo:13.19 »
Olet ihan oikeassa; esimerkkini ei toimi nopeammin kuin normaalikonstit - yritin vain väkisin saada atomic-group:lle käyttöä - tai kyllähän tuo toimii mutta atomic-group on jo grepin koodissa joten on turhaa määrätä sitä erikseen.

Edellisessä esimerkissä esittämäni käsky voidaan siis kirjoittaa jättäen atomic-group pois:
Koodia: [Valitse]
echo 2001.02.12 | grep -P '[0-9][0-9][0-9][0-9][.-][0-2][0-9]|30|31[-.][0][0-9]|10|12'
ja tulos on ihan sama. Itseasiassa voi käyttää grep:ille myös kytkintä -E joka on "kolmekertaa nopeampi".

Mutta toisaalta kannatti etsiä - BASH:in perusteistakin löytyi uutta ja lisäksi tuli sellainen huomio että regex:issä on paljon sellaista atomic-group:in kaltaista mitä ei juurikaan näe:

- rekursio. Aluksi vain hyödyttömiä esimerkkejä:
Koodia: [Valitse]
echo aaazzz | grep -P 'a(?R)?z'  #  tunnistaa onko lopussa yhtämonta z:aa kuin alussa on a:ta.

- funktiokutsut:
Koodia: [Valitse]
echo habckaaajbabgbbbpcbakabt | grep -P '(?P<f_nimi>[abc])(?1)(?P>f_nimi)'  # tunnistaa tekstistä ryhmät joissa on kolme merkkiä ryhmästä abc.

- monta eri hommaa:
Koodia: [Valitse]
echo 'aaaabxyz aaaad' | grep -P "(?+1)(?'ryhman_nimi'[abc])(?1)(?-1)(?&ryhman_nimi)"

(?+1)       viittaa ryhmän seuraavaan jäseneen
(?-1)        viittaa ryhmän edelliseen jäseneen
(?1)         viittaa koko ryhmään. Ilmeisesti ryhmiä voi olla useampiakin, täytynee joskus kokeilla.
(?'ryhman_nimi'[abc])  määrittelee ryhmän jäsenet (tässä: abc). Kyse on merkeistä eikä sanoista. Jos haluat määritellä sanojakin niin ne on kirjoitettava kaarisulkuihin.
(?&ryhman_nimi)        kertoo että "tässähän tämä oli"
lopputuloksena tarkistetaan onko tulevassa tekstissä viisikirjaimisia ryhmiä joissa on vain tuon määrätyn ryhmän jäseniä - siis yksi "vihollinen" ryhmässä turmelee koko ryhmän. Kun halutaan määrätä hyväksyttyjen merkkien luku niin näin se käy ( määräys on tuo {3} ):
Koodia: [Valitse]
echo 'aaaaaacc aaaad' | grep -P "(?+1)(?'kakkularatsis'[abcdefghij])(?1){3}(?-1)(?&kakkularatsis)"
- tuo kolme määrää että 4+3 elikä 7. Rekursioita pitää olla vähintään yksi.
« Viimeksi muokattu: 13.04.16 - klo:13.22 kirjoittanut petteriIII »

nm

  • Käyttäjä
  • Viestejä: 13761
    • Profiili
Vs: BASH:in säännöllinen lauseke (regex), atomic-group
« Vastaus #4 : 13.04.16 - klo:14.01 »
Olet ihan oikeassa; esimerkkini ei toimi nopeammin kuin normaalikonstit - yritin vain väkisin saada atomic-group:lle käyttöä - tai kyllähän tuo toimii mutta atomic-group on jo grepin koodissa joten on turhaa määrätä sitä erikseen.

Edellisessä esimerkissä esittämäni käsky voidaan siis kirjoittaa jättäen atomic-group pois:
Koodia: [Valitse]
echo 2001.02.12 | grep -P '[0-9][0-9][0-9][0-9][.-][0-2][0-9]|30|31[-.][0][0-9]|10|12'
ja tulos on ihan sama. Itseasiassa voi käyttää grep:ille myös kytkintä -E joka on "kolmekertaa nopeampi".

Tuohon tarvitaan kyllä ryhmät. Ilman niitä  | -vaihtoehdot kattavat koko lausekkeen, eli lauseke hyväksyy merkkijonot, jotka täsmäävät joko osaan "[0-9][0-9][0-9][0-9][.-][0-2][0-9]" tai "30" tai "31[-.][0][0-9]" tai "10" tai "12". Esimerkki:

Koodia: [Valitse]
$ echo 31.00 | grep -P '[0-9][0-9][0-9][0-9][.-][0-2][0-9]|30|31[-.][0][0-9]|10|12'
31.00

Mutta kuten esitin, atomisten  ryhmien (?>...) sijaan tähän kuitenkin käy tavalliset ryhmät (...) tai non capturing -ryhmät (?: ...), jotka ovat muuten samoja kuin tavalliset ryhmät, mutta niihin ei voi viitata myöhemmin numeroiduilla viittauksilla \1 \2 jne, ja ovat aavistuksen nopeampia, jos viittauksia ei tarvita.


Mutta toisaalta kannatti etsiä - BASH:in perusteistakin löytyi uutta ja lisäksi tuli sellainen huomio että regex:issä on paljon sellaista atomic-group:in kaltaista mitä ei juurikaan näe:

Perliähän nämä ovat. Bash ei ymmärrä näitä, koska se ei tue Perlin säännöllisten lausekkeiden syntaksia. Siksi joudut käyttämään greppiä, joka taas kutsuu libpcre:tä. ;)
« Viimeksi muokattu: 13.04.16 - klo:14.05 kirjoittanut nm »

petteriIII

  • Käyttäjä
  • Viestejä: 601
    • Profiili
Vs: BASH:in säännöllinen lauseke (regex), atomic-group
« Vastaus #5 : 15.04.16 - klo:13.59 »
Tattista vaan, tulipahan ryhmityksen tarve selville ja näin kantapään kautta tulleena sen muistaakin; tunnen itseni idiootiksi. Mutta pähkäillessäni noiden erilaisten regex:ien kanssa osoittautuikin että päivämäärä onkin parasta tarkistaa kuten sivulla: http://forum.ubuntu-fi.org/index.php?topic=303.msg386774#msg386774 kerrotaan.

- muuten vaikuttaakohan näihin kaikkiinkin se että skripti-kielenä käytän aina bash:ia mutta päätehän kai toteuttaa dash:ia?
« Viimeksi muokattu: 15.04.16 - klo:14.24 kirjoittanut petteriIII »

Tomin

  • Palvelimen ylläpitäjä
  • Käyttäjä / moderaattori+
  • Viestejä: 11051
    • Profiili
    • Tomin kotisivut
Vs: BASH:in säännöllinen lauseke (regex), atomic-group
« Vastaus #6 : 15.04.16 - klo:15.43 »
- muuten vaikuttaakohan näihin kaikkiinkin se että skripti-kielenä käytän aina bash:ia mutta päätehän kai toteuttaa dash:ia?

Kannattaa muistaa, että komentotulkilla skriptatessa jotkin asiat suorittaa komentotulkki (siis sh, bash, dash, ksh, zsh, tcsh, fish, tjms.) ja osan sitten nuo kutsuttavat ohjelmat kuten vaikkapa grep, echo, bc, sed ja awk. Tässäkin nuo säännölliset lausekkeet suorittaa grep (ja, niin kuin nm sanoi, se taas käyttää libpcre-kirjastoa) eikä siis komentotulkki eli komentotulkin vaihtaminen ei periaatteessa vaikuta siihen osaan mitenkään.
Automaattinen allekirjoitus:
Lisäisitkö [RATKAISTU] ketjun ensimmäisen viestin aiheeseen ongelman ratkettua, kiitos.

nm

  • Käyttäjä
  • Viestejä: 13761
    • Profiili
Vs: BASH:in säännöllinen lauseke (regex), atomic-group
« Vastaus #7 : 15.04.16 - klo:15.54 »
- muuten vaikuttaakohan näihin kaikkiinkin se että skripti-kielenä käytän aina bash:ia mutta päätehän kai toteuttaa dash:ia?

Ubuntussa ja useimmissa jakeluissa interaktiivinen komentotulkki on Bash. Skriptit suoritetaan Dashilla, jos tiedoston alussa on shebang #!/bin/sh koska se on symbolinen linkki dashiin:

Koodia: [Valitse]
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 helmi  4  2015 /bin/sh -> dash

Voit silti suorittaa skriptin Bashilla komentamalla

Koodia: [Valitse]
bash skripti.sh
Suoritukseen käytetään automaattisesti Bashia, jos tiedoston alussa on määritelty #!/bin/bash tai #!/usr/bin/env bash (https://en.wikipedia.org/wiki/Shebang_%28Unix%29#Portability)