Frage Text aus einer Datei mit Terminal extrahieren?


Ich möchte den Textkörper verarbeiten und eine Ganzzahl aus einer bestimmten Position im Text extrahieren, aber ich bin mir nicht sicher, wie ich diese "bestimmte Position" beschreiben soll. Reguläre Ausdrücke verwirren mich wirklich. Ich verbrachte (vergeudete) ein paar Stunden damit, Tutorials zu lesen, und ich fühle mich einer Antwort nicht näher :(

Es gibt eine Menge Text, die ganze Zahlen enthalten kann oder auch nicht (die ich nicht will) und dann gibt es eine Zeile, die immer enthält

id_ad=1929170&action

und dann folgt ein Haufen Müll, der mir egal ist, wieder kann es eine oder mehrere ganze Zahlen enthalten oder auch nicht.

So intuitiv weiß ich, ich möchte einfach alles ignorieren (und inklusive) id_ad= und ignoriere alles nach (und inklusive) &action und ich werde mit der ganzen Zahl verlassen, die ich will. Und ich weiß, dass ich reguläre Ausdrücke verwenden kann, um dies zu erreichen. Aber ich kann es nicht herausfinden.

Ich würde das gerne als einen Liner vom Terminal machen, wenn möglich.


1
2017-11-06 19:54


Ursprung


Das Ergebnis sollte 1929170 sein, oder? kommt es nur einmal im Körper vor? - Jacob Vlijm
Nun, in diesem Beispiel ist das ja das Ergebnis. Und es kann (oder auch nicht) anderswo vorkommen. Ich möchte irgendwelche Zahlen in dieser Position ziehen - bcsteeve


Antworten:


Nicht so ein One Liner (obwohl der Befehl, es auszuführen ist ein ein Liner :)), aber hier ist eine Python-Option:

#!/usr/bin/env python3
import sys
file = sys.argv[1]

with open(file) as src:
    text = src.read()

starters = [(i+6, text[i:].find("&action")+i) for i in range(len(text)) if text[i:i+6] == "id_ad="]
if len (starters) > 0:
    for item in starters:
        print(text[item[0]:item[1]])

Das Skript listet zuerst alle Vorkommen (Indizes) der (Start-) Zeichenfolge "id_ad =" in Kombination mit der (Ende-) Zeichenfolge "& action" auf. Dann druckt es alles, was zwischen diesen "Markern" liegt.

Aus einer vorbereiteten Datei extrahiert:

"Ich möchte den Textkörper verarbeiten und eine Ganzzahl aus einer bestimmten Position im Text extrahieren, aber ich bin mir nicht sicher, wie ich diese" bestimmte Position "beschreiben soll. Reguläre Ausdrücke verwirren mich wirklich. Ich habe ein paar Stunden damit verbracht Lesen Tutorials und ich fühle mich nicht näher zu einer Antwort :( Es gibt eine Menge Text, die ganze Zahlen enthalten kann oder auch nicht (die ich nicht will) und dann gibt es eine Zeile, die immer enthält id_ad = 1929170 & Aktion Es gibt eine Menge Text, die ganze Zahlen enthalten kann oder auch nicht (die ich nicht will) und dann gibt es eine Zeile, die immer enthält id_ad = 1889170 & action und dann gefolgt von einem Haufen Müll, der mir egal ist, wieder kann es eine oder mehrere ganze Zahlen enthalten oder auch nicht. Es gibt eine Menge Text, die ganze Zahlen enthalten kann oder auch nicht (die ich nicht will) und dann gibt es eine Zeile, die immer id_ad = 1889170 & action enthält und dann gefolgt von einem Haufen Müll, der mir egal ist, wieder kann es oder darf keine oder mehrere ganze Zahlen enthalten. Es gibt eine Menge Text, die ganze Zahlen enthalten kann oder auch nicht (die ich nicht will) und dann gibt es eine Zeile, die immer id_ad = 1929990 & action "enthält

Das Ergebnis ist:

1929170
1889170
1889170
1929990

Wie benutzt man

Fügen Sie das Skript in eine leere Datei ein und speichern Sie es unter extract.py führe es mit dem Befehl aus:

python3 <script> <file>

Hinweis

Wenn nur ein Vorkommen in der Textdatei vorhanden ist, kann das Skript viel kürzer sein:

#!/usr/bin/env python3
import sys
file = sys.argv[1]

with open(file) as src:
    text = src.read()
print(text[text.find("id_ad=")+6:text.find("&action")])

2
2017-11-06 20:46



Ich wähle diese Antwort, weil sie mich wirklich auf den richtigen Weg geschickt hat! Die anderen Antworten funktionieren natürlich auch. Aber das lieferte genug Informationen, Kontext und Beispiel, um mir zu helfen, viel zu verstehen. Vielen Dank. - bcsteeve
@bcsteeve perfekt! Ich bin froh, dass es geholfen hat. - Jacob Vlijm
@AvinashRaj Ich fürchte ich verstehe nicht was du meinst: mit open (datei) als src? - Jacob Vlijm
Das ist der übliche Weg, es zu tun. auch: schau dir das Ergebnis an time <script> <file> `es ist schneller. - Jacob Vlijm


Beispielsweise:

 egrep "id_ad=[[:digit:]]+&action" file.txt |  tr "=&" "  " | cut -d " " -f2 

... aber ich bin mir sicher, dass es elegantere Wege gibt ;-).

Schritt für Schritt:

egrep "id_ad=[[:digit:]]+&action" file.txt 

Scan file.txt für das Muster (regulärer Ausdruck), das aus einem Literal besteht id_ad=, gefolgt von 1 oder mehr Ziffern (die Bedeutung von [[:digit:]]+, gefolgt von einem Literal &action. Senden Sie die Ausgabe an die Standardausgabe.

tr "=&" "  " 

wandelt die Zeichen "=" und "&" in zwei Leerzeichen um.

cut -d " " -f2

Drucken Sie das zweite Feld (Leerzeichen getrennt) der Standardeingabe.


2
2017-11-06 20:03



Vielen Dank! Kannst du erklären warum: digit: ist innerhalb doppelter eckiger Klammern bitte ?? Ich nehme an, die interne Klammer ist einfach Teil der angegebenen Struktur, während die äußere ist, was sagt, dass wir das zusammenpassen (während die linken und rechten Literale nicht in Klammern sind und daher nicht übereinstimmen). Bin ich in der Nähe? - bcsteeve
Nein, es ist nur die Syntax des regulären Ausdrucks, der von verwendet wird egrep. Sehen man egrep, scrollen Sie nach unten zu "Zeichenklassen und Klammerausdrücke". - Rmano
ergep wird abgeschrieben, verwenden grep -E stattdessen. - Avinash Raj


Mit sed:

sed 's/id_ad=\(.*\)&action/\1/' filename

Erläuterung:

Der obige Befehl gibt beliebige Zeichenfolgen zurück (.*) zwischen zwei START-Wörtern (id_ad=) und ENDE (&action) im Dateinamen.
\(...\) Wird zum Erfassen von Gruppen verwendet. \( ist der Anfang einer einfangenden Gruppe und endet mit \). Und mit \1 wir drucken den Gruppenindex (wir haben eine Erfassungsgruppe)

Besser sed Befehl für obige Lösung kann so sein:

sed 's/^id_ad=\([0-9]*\)&action/\1/' filename

^ Beginn der Linie.
[0-9]*: Eine beliebige Zahl mit 0 oder mehr Vorkommen.
Siehe mehr für den Befehl sed

Mit grep:

Erläuterung:

grep -Po '(?<=id_ad=)[0-9]*(?=&action)' filename

Von Mann grep:

-o, --only-matching
      Print only the matched (non-empty) parts of a matching line,
      with each such part on a separate output line.
-P, --perl-regexp
      Interpret PATTERN as a Perl compatible regular expression (PCRE)

Gibt eine Zahl mit 0 oder mehr Vorkommen zurück ([0-9]*) zwischen zwei START-Wörtern (id_ad=) und ENDE (&action) im Dateinamen.

(?<=pattern): Positives Lookbehind. Ein Paar Klammern mit der öffnenden Klammer, gefolgt von einem Fragezeichen, einem "weniger als" -Symbol und einem Gleichheitszeichen.

(?<=id_ad=)[0-9]* (positiver Lookbehind) entspricht der 0 oder mehr Vorkommen von Zahlen, die danach folgten id_ad= im Dateinamen.

(?=pattern): Positiver Lookahead: Das positive Lookahead-Konstrukt ist ein Paar Klammern, mit der öffnenden Klammer gefolgt von einem Fragezeichen und einem Gleichheitszeichen.

[0-9]*(?=&action): (positive Lookahead) entspricht 0 oder mehr Vorkommen von Zahlen, die von Muster folgt (&action), ohne das Muster zu machen (&action) Teil des Spiels.
Lesen Sie mehr über Lookahead und Lookbehind

Zusätzliche Links:
Fortgeschrittene Grep-Themen
GREP für Designer


2
2017-11-06 20:32



Es wäre gut, eine Erklärung zu geben, besonders wenn die Person, die die Frage gestellt hat, stundenlang danach gesucht hat. - Alaa Ali
Mit sed müssten Sie zumindest den Schalter -n verwenden und einfach die Ersetzung drucken, denke ich, d. H. sed -n 's/id_ad=\(.*\)&action/\1/p'(Ansonsten druckt sed standardmäßig alle Zeilen), obwohl ich persönlich die Übereinstimmung etwas spezifischer machen würde, z. `sed -n 's / ^ id_ad = ([0-9] *) & aktion / \ 1 / p' - steeldriver
@Kasiya als Stahlfahrer sagte, Ihre Sed-Lösung wird nicht funktionieren, wenn zwei oder mehr id_ad=00&action  in derselben Zeile. - Avinash Raj
Und Sie brauchen nicht hinterher zu schauen. Dies grep -Po 'id_ad=\K[0-9]*(?=&action)' filename wäre genug. - Avinash Raj


Eine weitere Python-Antwort durch re Modul. Beispiel gestohlen von Jacobs Post.

script.py

#!/usr/bin/python3
import sys
import re
file = sys.argv[1]
L = []                                                  # Declare an empty list
with open(file) as src:
    for j in src:                                       # iterate through all the lines
        for i in re.findall(r'id_ad=(\d+)&action', j):  # extracts the digits which was present in-between `id_ad=` and `&action` strings.
            L.append(i)                                 # Append the extracted digits to the already declared empty list L. 
    for f in L:                                         # Iterate through all the elements in the list L
        print(f)                                        # Print each element from the list L in a separate new line.

Führen Sie das obige Skript als

python3 script.py /path/to/the/file

Beispiel:

$ cat fi
I want to process the body of text and extract an integer from a specific position in the text, but I'm not sure how to describe that 'particular position'. Regular expressions really confuse me. I spent (wasted) a couple hours reading tutorials and I feel no closer to an answer :( There's a bunch of text which may or may not include integers (that I don't want) and then there's a line that always contains

 id_ad=1929170&action There's a bunch of text which may or may not include integers (that I don't want) and then there's a line that always contains id_ad=1889170&action and then followed by a bunch of garbage I don't care about, again it may or may not include one or more integers. There's a bunch of text which may or may not include integers (that I don't want) and then there's a line that always contains

 id_ad=1889170&action and then followed by a bunch of garbage I don't care about, again it may or may not include one or more integers. There's a bunch of text which may or may not include integers (that I don't want) and then there's a line that always contains id_ad=1929990&action

$ python3 script.py ~/file
1929170
1889170
1889170
1929990

1
2017-11-08 02:38



Ich habe es getestet, auf großen Dateien, das wird schneller, danke für die Erwähnung. - Jacob Vlijm