Mam grupę łańcuchów, które wyglądają:

foo<xyz><123>
bar
pizza<oregano><tomato><mozzarella>

Jest więc gotuje się do prefiksu (foo, bar, pizza, pizza, ...), a następnie dowolną liczbę nazw atrybutów włączonych do wsporników kątowych. Zarówno prefiks, jak i atrybuty mogą składać się z jakiegokolwiek znaku z wyjątkiem wsporników kątowych (które są używane tylko dla nazwy atrybutów) Ani prefiks, ani nazwy atrybutów muszą być puste.

Teraz chciałbym mieć regex w mojej aplikacji TCL, która daje mi zarówno prefiks, jak i wszystkie atrybuty (w porządku, jeśli zachowują swoje wsporniki, choć ja, muszę podzielić je w listę ).

TRIVIAL podejścia ^(.+)(<.+>)*$ nie działa, ponieważ końcowa .+ jest zbyt chciała i zjada wszystkie mecze dla nazw atrybutów.

Próbowałem więc wykluczyć zakazujących nawiasów kątowych ^(\[^<>\]+)(<.+>)*$, który działa OK na pierwszy rzut oka - ale potem odkryłem, że to pasuje do fnork<<>><x<>> naruszyła regułę, że nazwy atrybutów nie mogą zawierać żadnych nawiasów kątowych (oprócz ograniczania jeden).

Po trzecie, rozszerzyłem zakazanych znaków do nazw atrybutów ^(\[^<>\]+)(<\[^<>\]>)*$, ale teraz rzeczy otrzymują nieco zacienione: podczas gdy REGEX pasuje tylko do prawidłowych ciągów (dzięki temu zarówno prefiks, jak i nazwy atrybutów nie mogą zawierać żadnych wsporników), już nie Uzyskaj nazwy atrybutów jako część meczu:

% regexp -all -inline "^(\[^<>\]+)(<\[^<>\]+>)*" "A<xyz><123>"
A<xyz><123> A <123>

Z jakiegoś powodu nie jest powrócił <xyz>!

Jakiś pomysł jak to naprawić?

Dygresja

Rzeczywisty ciągiem, który próbuję analizować używa wsporników kwadratowych i nawiasów jako ograniczniki. Coś w rodzaju: pizza[large](tomato)(olives)(cheese) gdzie jest [term] może pojawić się 0 lub 1, podczas gdy (term) s może pojawić się 0 lub więcej razy. Ale ze względu na charakter nawiasów kwadratowych i nawiasów wymaga to uczciwej ilości cytowania, co prawdopodobnie jest zbyt wiele rozproszenia, aby była przydatna tutaj)

-1
umläute 20 luty 2018, 00:15

3 odpowiedzi

Najlepsza odpowiedź

W tym przypadku sztuczka polega na użyciu dość prostego ponownego procesu Wyniki:

% regexp -all -inline {^([^<>]+)((?:<[^<>]+>)*)$} foo<xyz><123>
foo<xyz><123> foo <xyz><123>
% regexp -all -inline {[^<>]+} <xyz><123>
xyz 123

Byłeś tam prawie, ale zmagałem się z użyciem (<[^<>]+>)*, który nie będzie działać, ponieważ to tylko przechwytuje grupę jeden z czasów pasuje do. (nie byłem świadomy, że to uchwyciło Ostatni mecz, ale ponieważ rzadko chcę, albo najpierw lub ostatni, ale raczej wszyscy używam innego podejścia.)

Umieszczenie tego razem i zakładając, że masz jeden duży ciąg wielofunkcyjny, który ma wszystkie elementy, na które chcesz spojrzeć na nim (np. Ponieważ przeczytałeś go z pliku)

set str "foo<xyz><123>
bar
pizza<oregano><tomato><mozzarella>"

# Find the matching lines and do the first-level extract on them
foreach {- prefix attribs} [regexp -all -line -inline {^([^<>]+)((?:<[^<>]+>)*)$} $str] {
    # Split the attribute names
    set attributes [regexp -all -inline {[^<>]+} $attribs]
    # Show that we've matched them for real
    puts "prefix='$prefix', attributes=[join $attributes ,]"
}

Który generuje ten wynik:

prefix='foo', attributes=xyz,123
prefix='bar', attributes=
prefix='pizza', attributes=oregano,tomato,mozzarella
3
Donal Fellows 19 luty 2018, 21:50

Zwitajmy to.

package require string::token

set lex {[[] LB []] RB [(] LP [)] RP [^][()]+ t}
set str {pizza[large](tomato)(olives)(cheese)}

% set tokens [::string::token text $lex $str]
{t 0 4} {LB 5 5} {t 6 10} {RB 11 11} {LP 12 12} {t 13 18} {RP 19" 19} {LP 20 20} {t 21 26} {RP 27 27} {LP 28 28} {t 29 34} {RP 35 35}

Mając tokenizowane, możemy analizować lub oceniać żetony jako oświadczenia w małym języku:

% set terms [lassign $tokens prefix]

proc t {str beg end} {
    string range $str $beg $end
}
proc LB {str beg end} {
    return "Optional term is: "
}
proc RB args {
    return \n
}
proc LP {str beg end} {
    rename LP {}
    proc LP args {
        return ", "
    }
    return "Arguments are: "
}
proc RP args {}

% puts "Prefix is: [eval [linsert $prefix 1 $str]]"
Prefix is: pizza
% % join [lmap term $terms {eval [linsert $term 1 $str]}] {}
Optional term is: large
Arguments are: tomato, olives, cheese

Dokumentacja: eval, Dołącz do, Lassign, Lintert, LMAP (dla TCl 8.5), LMap , Pakiet, PROC, Puts, Zmień nazwę, powrót , Zestaw, Ciąg :: Token (pakiet)

1
Peter Lewerin 19 luty 2018, 22:37

Mogłabym źle odczytać wymagania, ale biorąc pod uwagę, że już "zakodowałeś" wszystkie szczegóły strukturalne w notacji ad hoc ad hoc, dlaczego nie ma maszyn listy TCl?

set str {foo(xyz)(123)
bar
pizza[large](oregano)(tomato)(mozzarella)}

foreach line [split $str \n] {
    set line [string map {"[" " " "]" " " ")(" " " "(" " {" ")" "} "} $line]
    set suffix [lassign $line prefix]
    lassign $suffix a b
    if {[llength $suffix] == 2} {
      set optional $a
      set attributes $b
    } else {
      set optional ""
      set attributes $a
    }
    puts "prefix='$prefix', optional='$optional', attributes='[join $attributes ,]'"
}

Przepraszam, ściśle mówiąc, moja odpowiedź nie odnosi się do pytania Regexa. I jest mniej czarodzieja niż w innych odpowiedziach;)

0
mrcalvin 20 luty 2018, 00:28