ubuntuusers.de

Zeichenkette mit Regex suchen außer bestimmtes Wort

Status: Ungelöst | Ubuntu-Version: Xubuntu 20.04 (Focal Fossa)
Antworten |

Fried-rich

Anmeldungsdatum:
2. Mai 2013

Beiträge: 1162

Hi,

wie kann ich mit Regex nach einer bestimmten Kombination suchen, z. B.

[0-9]\. [a-z]

mit Ausnahme eines bestimmtes Wortes? Das oben sucht nach einer Zahl, gefolgt von einem Punkt, einem Leerzeichen und eine Kleinbuchstaben. Ich will nun bestimmte Wörter ausschließen, z. B. "wort".

Gefunden wer soll:

7. blabla
5. laberlaber
4. wtf

aber nicht

4. wort

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11267

Wohnort: München

Welchen Regex-Dialekt nutzt du denn? Mit PCRE könnte man sowas machen (vgl. https://www.regular-expressions.info/lookaround.html):

$ grep -P '\d+\. (?!wort)[a-z]' << EOF
7. blabla
5. laberlaber
4. wtf
4. wort
EOF
7. blabla
5. laberlaber
4. wtf 

NORACSA

Anmeldungsdatum:
31. Januar 2010

Beiträge: 182

^\d+\. (?!wort$)\w+$

Sollte mit den meisten Dialekten funktionieren, allerdings mit einer Einschränkung. Komischerweise funktioniert die Regex nicht in der letzten Zeile des Texts, d.h. du brauchst am Ende noch einen Zeilenumbruch.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

NORACSA schrieb:

^\d+\. (?!wort$)\w+$

Ich würde das hintere Dollarzeichen weglassen. Man kann sogar die ganze Sequenz "\w+$" weglassen. Wenn das Wort irgendwo auftauchen soll, dann muss man ein bisschen tricksen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ grep -P '^\d+\. ((?!wort).)*$' << EOF
7. blabla
5. laberlaber
4. wtf
4. wort
5. noch ein wort irgendwo
EOF
7. blabla
5. laberlaber
4. wtf

Sollte mit den meisten Dialekten funktionieren,

Man muss aber schauen, ob die Engine Negativen Lookahead beherrscht.

allerdings mit einer Einschränkung. Komischerweise funktioniert die Regex nicht in der letzten Zeile des Texts, d.h. du brauchst am Ende noch einen Zeilenumbruch.

Bei mir funktioniert das einwandfrei. Bei dem Here-Dokument ist sowieso ein Zeilenumbruch am Ende der letzten Zeile:

1
2
3
4
5
6
7
$ od -t x1c <<EOF
> a
> b
> EOF
0000000  61  0a  62  0a
          a  \n   b  \n
0000004

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17625

Wohnort: Berlin

Mit sed geht es auch:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
echo "7. blabla
5. laberlaber
4. wtf

aber nicht

4. wort"  | sed -n "/[0-9]\. [a-z]/{/wort/d;p}"
7. blabla
5. laberlaber
4. wtf

Der Schalter -n sagt, dass nicht per default auf den Bildschirm ausgegeben werden soll.

Zwischen den ersten 2 Slashes hast Du Deinen regulären Ausdruck unverändert.

Wenn der matcht sollen mehrere Sachen passieren, dafür geschweifte Klammern, die das zusammenfassen. Wenn die Zeichenkette "wort" matcht, dann mit d deleten. P bewirkt dann ein print.

Fried-rich

(Themenstarter)

Anmeldungsdatum:
2. Mai 2013

Beiträge: 1162

Ich hab das nicht per sed sondern einfach in einem Texteditor (Geany) für eine sehr umfangreiche Textdatei gemacht.

Bei mir hat

([^0-9])\. ([a-z][^wort1|wort2])

funktioniert, obwohl ich nicht verstehe wieso. Scheinbar wird hier "[a-z][^wort1|wort2]" interpretiert mit "alles von a-z, außer die Wörter 'wort1' und 'wort2'. Spontan hätte ich eher gedacht das ganze wird interpretiert als "alles von a-z, danach alles außer wort1 oder wort2".

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

Fried-rich schrieb:

Ich hab das nicht per sed sondern einfach in einem Texteditor (Geany) für eine sehr umfangreiche Textdatei gemacht.

Bei mir hat

([^0-9])\. ([a-z][^wort1|wort2])

funktioniert, obwohl ich nicht verstehe wieso.

Weil "wort1" und "word2" aus eben diesen Buchstaben bestehen. Wenn Du "wor" verwendest, wird das davon ebenso erwischt, was aber falsch wäre. Um entscheiden zu können, ob ein Match wirklich funktioniert, braucht es klug ausgewählte Testfälle, damit man nicht in solche Fallen tappt.

Scheinbar wird hier "[a-z][^wort1|wort2]" interpretiert mit "alles von a-z, außer die Wörter 'wort1' und 'wort2'.

Nein, sicher nicht. [^wort1|word2] matched ein Zeichen, das keiner der erwähnten Buchstaben und nicht "1", "2" oder "|" ist.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ for c in {a..z} \|; do echo $c; done >alphabet
$ grep '[^wort1|word2]' alphabet | diff -U3 alphabet -
--- alphabet	2021-08-31 23:10:56.243783681 +0200
+++ -	2021-08-31 23:11:03.593125400 +0200
@@ -1,7 +1,6 @@
 a
 b
 c
-d
 e
 f
 g
@@ -12,16 +11,11 @@
 l
 m
 n
-o
 p
 q
-r
 s
-t
 u
 v
-w
 x
 y
 z
-|

Spontan hätte ich eher gedacht das ganze wird interpretiert als "alles von a-z, danach alles außer wort1 oder wort2".

Nein. Das Pattern oben matched ein Zeichen, das keine Ziffer ist, gefolgt von einem Punkt und einem Leerzeichen, gefolgt von einem Zeichen aus a bis z, gefolgt von einem Zeichen, das nicht w, o, r, t, d, 1, 2 oder | ist. Die Klammern sind völlig überflüssig, weil sie weder mit einem Quantifizierer versehen sind, noch innerhalb eine Alternative beinhalten - es sei denn, Du willst bestimmte Teile beim Matchen extrahieren.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11267

Wohnort: München

Fried-rich schrieb:

Ich hab das nicht per sed sondern einfach in einem Texteditor (Geany) für eine sehr umfangreiche Textdatei gemacht.

Dafür gilt dann https://www.geany.org/manual/gtk/glib/glib-regex-syntax.html.

Antworten |