Regex kérdés

Fórumok

Üdv,

olyanra lenne szükségem, hogy adott karaktersorok ne következzenek. Pl. van olyan, hogy (foo|bar), ami vagy foo vagy bar, tehát a(foo|bar)-ra illeszkedik afoo vagy abar. Na nekem olyan kellene, hogy a után pont egyik se jöjjön, tehát a afo, aba, abaz jó, de ha már afoo vagy abar van, az nem kell.

Köszi.

Hozzászólások

báshban' a $-jel a sorvég "úgymond", mint ahogy a "^" a kezdet. nem remélem, hogy nem segítettem :D de egyáltalán nem biztos, hogy ez egyáltalán jó válasz :\

Ilyesmit próbáltam, de nem működött. Konkretizálva akkor a problémát, sed-hez akarok egy substitute patternt, ami relatív (tehát nem /-vel és nem http://-vel kezdődő) linkeket prefixel egy adott sztringgel. Úgy szeretném, hogy minden esetre működjön, még http.html nevű target-re is. Jelenleg mielőtt rámennék sed-del ilyennel próbálkozom, de csak nem akar illeszkedni:
echo href=\"http.html\" | grep 'href="[^(/|http://)]'

lehet rosszul értelmezem, de azt szeretnéd, hogy a link-et (vagy nem link-et) tartalmazó string elejéről lekapod a "http://" tag-et ha van, majd mindenképpen eléteszel egy másik tetszőlegest?

echo "egyeb szoveg http://hup.hu hello ez itt mar nem az url resze" | grep -iEo "http://[^ ]*" | sed -r s/"^http:\/\/"/""/

ezzel megvan csak a domain része az url-nek, hozzátenni meg akár echo-val pl.

Nem. Vannak relatív (alfanumerikus karakterrel kezdődik), félig abszolút (/-el kezdődik), illetve abszolút (http://-el kezdődik) linkjeim. Utolsóval nem kell csinálni semmit. Másodikat egy adott prefix-szel kell ellátni (ez könnyű, már megvan), elsőt pedig egy másik prefix-szel. Na itt akadtam le.

közben olvasgatom, és próbálom újra megérteni mit szeretnél :)

ha jól tévedek, akkor ezzel megkapod azokat a szavakat, amelyeket ki szeretnél venni. csak sajnos ismeretem szerint, a -o kapcsolónál nem működik a -v negáció.


echo "ki kell venni ezt: foo afoo de maradhat afo es fo is" | grep -ioE "[^ ]*foo[^ ]*"

tehát ez elvileg pont azokat a szavakat írja ki, amelyeket ki szeretnél venni. jól értem?

csak persze te fordítva szeretnéd, vagyis a többi szót meghagyni ha jól tévedek. ezen még elgondolkzok.. :)

szerk.: egy olyan eszembe jutott, hogy a fenti kimeneteit kivetetni külön sed-del egy while loop használatával:


TEXT="ki kell venni ezt: foo afoo vagy foob de maradhat afo es fo is"

echo "$TEXT" | grep -ioE "[^ ]*foo[^ ]*" | while read F
        do
                echo "$F"
                TEXT=$( echo "$TEXT" | sed -r s/"$F "/""/ )
                echo "$TEXT"
        done

szerk2.: talán még egyszerűbb lenne ez (mivel a '*' nem úgy működik sed-nél mint grep-nél, ezért 3 esetre bontva):


TEXT="ki kell venni ezt: foo aafoo vagy foobcd de maradhat afo es fo is"

echo "$TEXT" | sed -r s/"foo "/""/ | sed -r s/"[^ ]+foo "/""/ | sed -r s/"foo[^ ]+ "/""/

A másik posztom alapján ezt össze lehet kalapálni, csak egy kicsit trükkös, mert mindig meg kell nézni, hogy amikor kiléptünk az illeszkedő prefix-ből, akkor mivel léptünk ki -- idézőjellel vagy sem. Ha ui. nem idézőjellel, akkor a következő idézőjelig fel kell enni mindent. Az idézőjel vizsgálata egyúttal elintézi a szóvég-kérdést is. Ezért a rekurzívan alkalmazandó kifejezés így fog kinézni:

T()|U()|"|[^TU"][^"]*"

Magyarázat: T és U a normál következő karakterek, ekkor rekurzívan folytatjuk a vizsgálatot. Idézőjel esetén kiszállunk (megtaláltuk a végét illeszkedéssel, csere lesz). Ha egyik eset sem áll fenn, akkor szintén megtaláltuk a végét (idézőjeltől eltérő karakterrel), ugyanakkor fel kell nyalnunk idézőjellel bezárólag a hátralévő sztringet, hogy a cserében majd hivatkozhassunk rá.

Itt a fa:


    *
   / \
 '/' 'h'
      |
     't'
      |
     't'
      |
     'p'
      |
     ':'
      |
     '/'
      |
     '/'

Alkalmazva a képletet (és élve azzal, hogy a bevezető "/" alatt már nincs további vizsgálat):


h(
  t(
    t(
      p(
        :(
          /(
            "|[^/"][^"]*"
          )|"|[^/"][^"]*"
        )|"|[^:"][^"]*"
      )|"|[^p"][^"]*"
    )|"|[^t"][^"]*"
  )|"|[^t"][^"]*"
)|"|[^/h"][^"]*"

Ezzel a MINTA készen van. Ezt berakjuk egy pár zárójelbe. Ez a legkülső kifejezés (vagyis a

(MINTA)

) akkor fog illeszkedni, ha a tiltott prefixek nem szerepelnek, és ekkor a (MINTA) kifejezés által elkapott érték tartalmazni fogja a lezáró idézőjelet is. Ennek megfelelően az alábbi sed parancs ajánlott (case insensitive, extended regex, soronként bárhány csere, és persze a PREFIX-et ki kell tölteni):

sed -r 's,href *= *"(MINTA),href="PREFIX\1,gi'

Kifejtve az egészet (kivéve a PREFIX-et, de simán írj be a helyébe

http://

-t):

sed -r 's,href *= *"(h(t(t(p(:(/("|[^/"][^"]*")|"|[^/"][^"]*")|"|[^:"][^"]*")|"|[^p"][^"]*")|"|[^t"][^"]*")|"|[^t"][^"]*")|"|[^/h"][^"]*"),href="PREFIX\1,gi'

Valóban, sokkal olvashatóbb. Ez a ?! ugye a negative lookahead? (Ahol a lookahead a non-capturing match?) Perl-ben még nem használtam, vagy csak nagyon régen, viszont a NEdit, amely a kedvenc editorom, támogatja ezeket, és ott szoktam ilyet írni, ha nagyon kell.

Azért érdekelt a dolog, mert a kérdés megoldhatónak látszott sima regex-szel is (*valódi* regex-szel, amelyben csak konkatenáció, alternatíva, zárójel, és Kleene-csillag szerepel -- na jó, van benne negatív charclass is, de az egy triviális rövidítés (a|b|c|d)-re). Egyszóval a kérdés elméleti szépsége ragadott meg :)

A perl regex engine-je nem (sem) valódi regex, mert már az illesztésben lehet visszahivatkozásokat használni, amihez veremgép kell. Mindenesetre a fenti kifejezés (ha jól értelmezem) valószínűleg nem veszi igénybe a vermet, tehát valódi véges determinisztikus (vagy nemdeterminisztikust szimuláló) automatát hoz létre. Kérdésedre, hogy van-e értelme ilyennel erőlködni: gyakorlati szempontból nincs, csak gondoltam, nézzük meg, mi van egy ilyen negative lookahead "mögött" -- a regex parser valójában egy olyan automatát generál (legalábbis nagyon valószínűen) a negative lookahead-ből, mint amilyet a fent látható kibontott regex ír le "közelebbről". Egészen tömören kifejezve: érdekes elméleti feladatnak tűnt, és szemléltetni kívántam vele, hogy a negative match valójában micsodás.

lacos tudasa * ebbe belefektetett munka > Beanie tudasa * (Beanie yacc megtanulasaba fektetett ideje + yacc-ban megoldani a problemat ido)

vagyis gazdasagilag a jo megoldas az, ha Beanie megtanul yacc-ul es megoldja. Bar, azt hiszem, ha veletlenul Beanie 'bash internal commands' feltetelt adott volna, nem pedig 'sed regexp' feltetelt, lacosnak arra is lenne megoldasa.

mellesleg, mint rendszergazdanak, aki folyamatosan a programozok hibas vilagnezete szerint szenved, baromira nem tetszik, hogy nincs ertelmezve, hogy ez az <a> tag vagy sem. Es ennek ellenorzese nehezkes is, mert az A tagben lehet ujsor karakter (tobb is) 'href=' pedig mashol is fordulat elo esigytovabb.

mellesleg, mint rendszergazdanak, aki folyamatosan a programozok hibas vilagnezete szerint szenved, baromira nem tetszik, hogy nincs ertelmezve, hogy ez az <a> tag vagy sem

Tök igazad van :( A hibás világnézet már abban tetten érhető, hogy a böngészők XHTML-en kívül bármit is megesznek :(

Például ha XHTML-ben lennénk (amivel rögtön kielégítenénk azt az igényt, hogy kettes típusú (környezetfüggetlen grammatikával leírható) nyelvként ugorjunk a HTML torkának -- hiszen a yacc-ot pont ezért használnánk), akkor kb. a következő XPath kifejezéssel meg tudnánk ragadni az összes olyan href attribútumot, amely <a> tag-hez tartozik, és sem /-rel, sem http://-rel nem kezdődik:

//a/@href[not(starts-with(., "/") or starts-with(., "http://"))]

Eköré meg jó eséllyel tudnánk rittyenteni egy XSL(T)-t, de ahogy korábban írtam, az xmlstarlet-tel ezeket a node-okat kiválasztva (amely node-set a megfelelő HTML anchor href attribútumokat tartalmazza) a tartalmukat *helyben* tudnánk módosítani. Ha jól emlékszem.

(Szerk.: az xmlstarlet ugye itt annyit segít, hogy helyettünk kigenerálja az "in place update"-et szimuláló XSL(T)-t, aztán le is futtatja. De belül az is végig XSL-lel dolgozik.)

Igen, valóban hasznos lenne megtanulnom a yacc-t, de jelenleg el vagyok havazva eléggé és ez a munka nekem úgy lett kiadva, hogy amennyire lehet egyszerű scripteléssel, könnyítsem meg a webmesterek dolgát. Amikor meg feltettem a kérdést, nem gondoltam volna, hogy ilyen komplikált ezt regexben megfogalmazni. A href-ről meg annyit, hogy természetesen máshol is cserélni kell, az <a> tag csak egy példa volt.

jol tetted h konkretizaltad, mar ezt kellett volna az elejen.
regexnel a tagadas valoban kicsit szivas, de nem perl-ben
ha megteheted h nem bash-t hasznalsz hanem perl-t, akkor inkabb azzal csinald, perl-ben mar tudsz tagadni.
(a perl nagyon elterjedt, szoval talan nem olyan nagy baj h perlben irod meg)
nezz utana perlben a regexp-nek

Szia,

a konkret kerdesre valaszolva

Illeszkedjen minden /minta/ -ra, ami utan nem jon se /tiltott1/ se /tiltott2/

Reszeiben: mi az, ami illeszkedik midnenre, kiveve a /tiltott1/ -et es a /tiltott2/ -ot.
Bonyi ez, ezeknek a regexpes kifejtese irgalmatlan hosszu.
Pl "a" es "b" legyen a /tiltott1/ /tiltott2/, ekkor a kifejezesed:
'[^ab]'
De ha "aa" es "bb", akkor:
'(^.$|[^ab][^ab]|a[^a]|b[^b])'

Ez a regularis kifejezesek (nyelvek) generalo tipusu modszere miatt van igy, nagyon nehez legeneralni az osszes szot, kiveve nehanyat.
Sokkal konnyebb nehany szot generalni.
Pont emiatt van a grep -nek -v kapcsoloja. Meg talan van tagadas a perl regexpben is, de ez nem ment meg a mertektelen szivastol.

picit bovebben ertelmezve valaszolva

A html egy bonyolult nyelv.
Nem mindegy, hogy

<a href="http://foo/">bar</a>

Vagy

<img src="kep" alt="href=http://foo/">

esetleg

<!--  <a href="http://foo/">bar</a> -->

Az ilyenekre nagyon nehez regularis kifejezest irni. Sokkal konyebb dolgod van, ha egy olyan programmal dolgozol, aminek vannak belso allapotai, es tud regexpet illeszteni egyszerre. QAkkor megteheted, hogy egy valtozoban nyilvantartod, hogy milyen allapotban van az ertelmezes eppen (tagen belul, kommenten belul, idezojelen belul stb). Pont erre valo a bison/yacc. Bar varhatoan a sed elvileg kepes erre a feladatra, azonban azt hiszem, konnyebb dolgod lenne a bison-t nullarol tanulva megoldani a dolgot, mint seddel.

de varhatoan ez kell neked valojaban

Az kell, hogy egy leszedett html oldal lokalis gepen, vagy masig helyre feltoltve is hasznalhato legyen.
Pont erre valo a

wget -k

Ugyan ez pont azt csinalja, hogy az abszolult hivatkozasokbol is relativat csinal ahol lehet, te meg pont az ellenkezojet mondtad, de ennek megiscsak inkabb igy lenne az ertelme.

Köszi a bőséges választ. Az nem baj, ha kommentekben is cserél, de az <a> tagek a fontosak most, utána meg esetleg az img kell még. A megoldás tényleg bonyi, és amúgy nekem is hasonló dolog jutott eszembe, ezért is nyitottam ezt a topicot remélve, hogy van jobb. A bison/yacc túl nagy falat lenne ehhez, annyit nem ér a dolog. A wget meg azért nem jön szóba, mert nem erre kell, hanem HTML részleteket kell egy Drupal rendszerbe migrálnom, és ami anno lokális link (vagy /-el kezdődő félig lokális) volt, azt meg kéne posztprocesszálni a feltöltő scriptemből, hogy utána a webmestereknek kevesebb dolga maradjon a linkek kézi javítgatásával.

Nahát, nahát, eddig három ismerős ebben a topic-ban! :)

A tiltott szavakat szervezzük papíron prefix-fába (trie-ba). A trie-on annyit reszelnék, hogy a node-okba nem az addig leírt szavakat tenném, hanem a betűket (tehát nem az élekre írnám a karaktereket, hanem a node-okba, és egy szó összeolvasásához a gyökérből le kell olvasni az adott levélig.) Ezután a fát járjuk be az alábbi bejárással:

Tetszőleges node-on állva, melynek gyermekei T, U, V, az alábbit generáljuk:

[^TUV]|\>|T()|U()|V()

A zárójelekbe pedig belelépünk, és ott a kijelölt részfát bejárva rekurzívan ismételjük az algoritmust. Az algoritmust egy ál-gyökérből indítjuk.

A fát annyival trükközzük még meg, hogy a tiltott szavak végére mindenhova odaírjuk a szóhatárt (

/\>/

). Ezek lesznek ugye a fa levelei, ahonnan már nincs hova továbbhaladni. Ilyenkor a fenti alternatívából csak az első ág értelmes (a tagadó osztály), a szóhatárnak a negálását pedig (legalábbis GNU alatt)

/\B/

-nek írjuk.

Legyenek például a tiltott szavak: /alma/, /alfa/, /alga/. Ebből az alábbi fa készül:


   *
   |
   a
   |
   l
  /|\
 m f g
 | | |
 a a a
 | | |
 > > >

A fenti algoritmussal:

[^a]|\>|a(
  [^l]|\>|l(
    [^mfg]|\>|m(
      [^a]|\>|a(
        \B
      )
    )|f(
      [^a]|\>|a(
        \B
      )
    )|g(
      [^a]|\>|a(
        \B
      )
    )
  )
)

Az egészet beletesszük egy külső zárójelbe, és az elejére kirakunk egy bal szóhatárt. Összeolvasva:

\<([^a]|\>|a([^l]|\>|l([^mfg]|\>|m([^a]|\>|a(\B))|f([^a]|\>|a(\B))|g([^a]|\>|a(\B)))))

Yacc/bison/sed/awk téma: a sed és főleg az awk is erősen programozható, tehát nem biztos, hogy csak emiatt rendes HTML parser-t kell írni bison-ban. A HTML (mivel nem XHTML) amúgy is rosszul formázott lesz. Ha véletlenül XHTML-lel van dolgunk, akkor az xmlstarlet-tel lehet a legjobb nekiugrani. A yacc-ot megtanulni persze amúgy is érdemes.

Köszönöm :)

Gondolkoztam régebben azon, tanítanék-e, de elvetettem, mert nincsenek megfelelő tanári kvalitásaim :) Például akit nem érdekel a téma, az inkább menjen haza, minek gyötörjük egymást. Ez nem venné ki jól magát :)

Viccet félretéve, munkahelyen gyakran szoktam táblánál prezentálni, de a hivatásszerű tanításhoz ennél sokkal több kell: a tárgy ismeretén túl tudni kell és szeretni kell tanítani. Egy tanárnak a legnagyobb szakmai beteljesülést okozza már az, ha a tanítványai fejlődnek! Engem pedig végső soron nem ez motivál.

Sziasztok,

Regex segítségre lenne szükségem, konkrétan az alábbiról lenne szó:
Példa1: "Béla Géza! Sanyi"
Elvárt eredmény1: " Sanyi"
Példa2: "Béla Géza Sanyi"
Elvárt eredmény2: "Béla Géza Sanyi"

A feladat az lenne, hogy amennyiben a szöveg amiben keresek tartalmazza az következő karakterek bármelyikét !?. úgy a szövegnek az adott karakter utolsó előfordulása utáni részét adja vissza. Ha nem tartalmaz semmit, akkor a teljes szöveg kell.

Az én illesztésem abban esetben működik, ha a keresett karakterek bármelyikének legalább egy előfordulása van, ^.*[!?.](.*) viszont itt elakadtam, nem tudom, hogy mondjam meg neki, hogy ha nincsenek benne a keresett karakterek, akkor adja a teljes szöveget

--

kincza

Ha extended regex, akkor lehet vagy kapcsolat:

(.*[.?!]\(.*\))|(\(.*\))

Ennek *szerintem* azt kellene adnia. A vagy-jel előtti rész ugye illeszkedik (az alapértelmezett mohóság miatt) a legutolsó . vagy kérdő/felkiáltójelig, majd megjegyzi a maradékot. A második fele meg értelemszerűen mindenre - azaz a teljes szövegre - illeszkedik.

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?