regular expression Anfang

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Hi

Ich bin ein absoluter Neuling was Python angeht und versuche derzeit in einem file alle Zeilen zu finden die mit einen ; enden. Es soll sowohl mit den Zeilenumbruch von Windows als auch Linux umgehen können. Habe folgende Zeile versucht aber es geht nicht. Wenn ich (\r)? weglasse funktioniert es für Linux. Vielleicht könnt ihr mir einen Tipp geben wo der Fehler ist. Danke im vorhinein.

Code: Alles auswählen

re.search(r"([;](\r)?)$", line)
MFG Harald
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Hi harald,

neben der Tatsache, dass die ganzen Klammern überflüssig sind, tut bei mir alles:

Code: Alles auswählen

>>> re.search(r";\r?$", "bla;\r\n")
<_sre.SRE_Match object at 0x1079cfd98>
>>> re.search(r";\r?$", "bla;\n")
<_sre.SRE_Match object at 0x1079cfe00>
Alternativ kannst Du auch nach ";\s*$" suchen, dann ist es egal, welche Art unsichtbarer Zeichen nach dem ";" kommen.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

OK danke. Und wenn ich nun Sätze finden will die nicht auf ; enden reicht es dann vorm ; ein ^ zu schreiben?

MFG Harald
BlackJack

Wobei sich das auch noch prima ohne kryptische reguläre Ausdrücke testen lässt:

Code: Alles auswählen

line.rstrip().endswith(';')
@harald: Nein, dazu muss das ';' auch noch mit dem '^' in eckigen Klammern stehen, denn nur dort hat es die Bedeutung die Du haben möchtest. Ohne regulären Ausdruck:

Code: Alles auswählen

not line.rstrip().endswith(';')
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Also abgesehen von den Klammer müsste der folgende code funktionieren oder? Bei mir funktioniert er leider nicht. Zu den alternativen muss ich sagen das ich re verwenden muss.

Code: Alles auswählen

re.search(r"([^;](\r)?)$", line)
MFG Harald
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Kannst Du ein Beispiel zeigen?
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Habe folgendes Testfile:

Code: Alles auswählen

int test();

int test(int i)
{

}

void main()
{

  int i = 0;
  char text[50];

  LOG();

}
Mit folgenden code möchte ich die richtigen Zeilen findet.

Code: Alles auswählen

        for line in findFile:
            if re.search(r"([^;](\r)?)$",line):
                print line
Er gibt mir immer die Zeile int test(); auch aus obwohl er diese weglassen sollte.

mfg Harald
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo harald,
so kannst Du Deinen regulären Ausdruck ja auch nicht negieren. Es wird natürlich immer ein Zeichen gefunden das nicht ";" ist (nämlich das Zeilenendezeichen). Wenn Du C-Code analysieren willst, sind reguläre Ausdrücke schwierig und das zeilenbasiert zu tun fehlerhaft, da C nicht zeilenbasiert ist.
BlackJack

@harald: Du hast da ja zum Beispiel ' LOG();\n'. Und da matched Dein regulärer Ausdruck natürlich. Das '[^;]' passt auf das Zeilenendezeichen und das '\r?' passt auf die leere Zeichenkette die danach kommt. Das ist gar nicht so einfach, insbesondere wenn in der letzten Zeile zum Beispiel kein Newline oder Carriage Return Zeichen am Ende steht. Schönes Beispiel wo reguläre Ausdrücke keine gute Idee sind, weil man das mit den anderen Methoden auf Zeichenketten viel einfacher und verständlicher ausdrücken kann.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

So bin jetzt gerade dabei einen fertigen C Parser zu suchen welcher mir es ermöglicht bestimmte Befehle im Code zu finden und dort dann auch sagen zu können in welcher Funktion die Funktion steht bzw in welcher Zeile im Source Code. Bin jetzt mal auf den pycparser gestoßen. Gibt es von euch nocht Vorschläge für andere Parser oder eine Idee wie ich meine Aufgabe sonst lösen könnte? Mit regular expressions ist es das Problem das ich nicht immer sagen kann das der c Code gleich ausschaut.

mfg Harald
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was sollen denn "bestimmte Befehle" genau sein? Geht es um Funktionsaufrufe, um Schlüsselwörter oder wie? Beschreibe doch mal dien eigentliches Ziel. Das bringt sicherlich mehr, als wenn du hier (gefühlt) mit jedem Beitrag deine Anforderungen änderst.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Ich muss bestimmte Funktionsaufrufe im Code erkennen und umschreiben. Meine derzeitige Idee wäre das ich mit Regular Expressions erkenne wo eine Funktion beginnt und wie diese heist. Danach suche ich innerhalb der Funktion wo ein von mir gesuchter Funktionsaufruf ist. Weitere Informationen über die zum Beispiel übergeben Variablen (welcher Typ?) an den Funktionsaufruf erhalte ich dann aus dem AST der vom Parser kommt.

mfg Harald
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Na, das hört sich doch tatsächlich so an, als wenn der bereits von dir erwähnte `pycparser` hier gut zum Einsatz kommen kann. Das würde dir schonmal den hässlichen Teil des Parsens ersparen. Du musst dich dann sicherlich noch ein bißchen durch den vom Parser erzeugten AST wuseln, um die von dir gewünschte Funktionalität zu erhalten, aber das müsstest du ja so oder so.

Du kannst halt nicht einfach einen regulären Ausdruck schreiben, bei dem du annimmst, dass eine Funktion immer nach dem Schema "Text, runde Klammer auf, ggf Text, runde Klammer zu, geschweifte Klammer auf, Text, geschweifte Klammer zu" oder so ähnlich aufgebaut ist. Das scheitert bereits daran, dass in C-Funktionen regelmäßig weitere geschweifte Klammernpaare geöffnet werden. Da würde ein naiv geschriebener regulärer Ausdruck beim Schließen eines inneren Klammernpaares sofort denken, es wäre das Funktionsende, weil er ja auf eine geschlossene geschweifte Klammer gestoßen ist. Du müsstest das daher im gesamten Kontext betrachten und quasi die Klammernpaare mitzählen. Allein dieser Umstand geht bereits über die Möglichkeiten von regulären Ausdrücken hinaus.

Wie gesagt: Lass dir diesen Teil der Arbeit von nem Tool abnehmen. Dann hast du wenigstens mit recht hoher Wahrscheinlichkeit einen korrekten AST und kannst dich auf dein eigentliches Ziel konzentrieren. ;)

EDIT: Ich empfehle übrigens einen Blick in die FAQs vom `pycparser`. Da steht zum Beispiel, dass es möglich ist, einen AST automatisch wieder in Code umwandeln zu lassen. Modifikationen und die anschließende Ausgabe des angepassten C-Codes dürften daher möglich sein. Einziges Manko: Der neu erzeugte Code wird höchstwahrscheinlich nicht mehr dieselbe Formatierung haben wie das Original (falls das eine Rolle spielt).

EDIT2: Und mit func_calls.py gibt es dort sogar ein Beispiel, wie man bestimmte Funktionsaufrufe nachverfolgen kann. Entspricht dies nicht in etwa deinem Vorhaben? Wäre zumindest ein Anfang. Achja, und in func_defs.py wird gezeigt, wie man Funktionsdefinitionen herausfischen kann.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Hi
Also der pycparser funktioniert super und ich kann auch schon meine gesuchten Informationen ablesen. Habe alles mal mit einen Testfile probiert und dabei habe ich aber schon folgendes Problem. Die Testdatei benötigt types.h. Ich verwende die Fake Headers welche im Ordner Utils drinnen sind. Dort sind alle Headers drinnen außer types.h welche im Unterordner sys ist. Wenn ich die Datei in den Hauptordner kopiere funktioniert alles aber wenn ich sie nur im Unterordner habe geht es nicht. Der Aufruf des Parsers ist im folgenden zu sehen. Ich bringe den cpp aber nicht dazu das er im Ordner fake_libc_include recursive sucht und den Unterordner sys mitnimmt. Vielleicht kennt ja jemand eine Lösung für das Problem? Das kopieren oder verschieben von types.h ist leider keine Lösung da ich dann im Projekt selber auch die Header Dateien in unterschiedlichen Ordnern habe.

Code: Alles auswählen

ast = parse_file(totalFilePath, use_cpp=True, cpp_args=r'-Ipycparser-master/utils/fake_libc_include/')
mfg Harald
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Hi,
C sucht nicht rekursiv nach Header-Dateien, also ist das Verhalten von pycparser hier gleich. Du mußt eben alle Pfade, die Du brauchst, mit -I angeben.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Hi

Ok habe nun herausgefunden wie man mehrere Pfade angeben kann (mit []). Ich lasse mir nun alls Pfade heraussuchen wo header dateien enthalten sind und stelle einen String zusammen der passen sollte. Wenn ich den String an die Parse Funktion übergebe funktioniert es nicht wenn ich den Inhalt des String ausgebe und diesen in die python Datei kopiere dann gehts.

Code: Alles auswählen

self.headers = "[r'-Ipycparser-master/utils/fake_libc_include/', r'-Ipycparser-master/utils/fake_libc_include/sys/']"

# Funktioniert
ast = parse_file(filePath, use_cpp=True, cpp_args=[r'-Ipycparser-master/utils/fake_libc_include/', r'-Ipycparser-master/utils/fake_libc_include/sys/'])

# Funktioniert nicht
ast = parse_file(filePath, use_cpp=True, cpp_args='%s' %self.headers)

# Funktioniert nicht
ast = parse_file(filePath, use_cpp=True, cpp_args=self.headers)
Fehlermeldung:

Code: Alles auswählen

Datei 1 gefunden (/home/harald/Arbeitsfläche/Prototyp/helloWorld.c)
cpp: Fehler: r'-Ipycparser-master/utils/fake_libc_include/', r'-Ipycparser-master/utils/fake_libc_include/sys/': Datei oder Verzeichnis nicht gefunden
cpp: Warnung: »-x c« hinter letzter Eingabedatei hat keine Wirkung
cpp: schwerwiegender Fehler: keine Eingabedateien
Kompilierung beendet.

mfg Harald
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

der Name des Parameters (»cmd_args«) suggeriert ja schon, dass eine Liste mit Argumenten erwartet wird. Weder Fall 2 noch Fall 3 übergeben allerdings eine Liste. Auch wäre mir keine Funktion bekannt, die mit der Repräsentation einer Liste etwas sinnvolles anfangen kann (Außer vielleicht »eval« :P ).
Um es deutlicher zu sagen. In »self.headers« steht Quatsch drin, schau Dir einfach mal den Typ dieser Variable an.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Hi
Also ich hätte eigentlich gedacht das wenn ich die zweite Variante nehme das er dann einfach den String der in self.headers drinnen steht dort hinschreibt und die Funktion ihn dann rictig interpretiert. Wenn ich nun aber eine Liste mache und jeden Pfad in diese Liste einfüge dann sollte es doch gehen oder?

mfg Harald
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

harald hat geschrieben:Also ich hätte eigentlich gedacht das wenn ich die zweite Variante nehme das er dann einfach den String der in self.headers drinnen steht dort hinschreibt und die Funktion ihn dann rictig interpretiert.
Das wäre ein unglaublich schlechtes Verhalten. Dann müsste der Interpreter irgendwie entscheiden, welche Variante er denn nun gerade in diesem konkreten Fall wählen sollte.
harald hat geschrieben:Wenn ich nun aber eine Liste mache und jeden Pfad in diese Liste einfüge dann sollte es doch gehen oder?
Das schöne ist ja, dass man solche Sachen einfach ausprobieren kann und daraus lernt ;-)
Das Leben ist wie ein Tennisball.
harald
User
Beiträge: 18
Registriert: Freitag 16. August 2013, 09:10

Hi
So ich bin nun schon relativ Weit mit den Parser und kann auch schon meine Funktionen erkennen. Nun dachte ich mir eigentlich das ich in einer Datei an eine bestimmte Zeile springen kann und dort die vorhandene Zeile mit einer neuen (die länger oder kürzer ist) ersetzen kann. Allerdings geht das anscheinend doch nicht und so habe ich gelesen das ich eine temporäre Datei erstellen muss in die ich den neuen Code reinschreibe und die dann umbennene zum Beispiel. Ich hoffe ich ihre mich nicht was das angeht den eigentlich würde ich lieber nur in der aktuellen Datei die Zeile ersetzen. Sollte das doch gehen bitte um Antwort.

Nun habe ich also zwei Ansätze:

1) Die alte Datei Zeilenweise in eine neue kopieren und die jeweiligen Zeilen einfach ersetzen. Danach die alte datei löschen und die neue umbenennen.

2) Die datei mit readlines() einlesen und in eine Liste Spiechern. In der Liste die jeweiligen Zeilen ändern und danach mit writelines() die liste wieder rausschreiben.

Nachdem vermutlich beide Varianten funktionieren habe ich mir überlegt das für mich die zweite Variante leichter zu implementieren wäre aber da ich dann eine Liste brauche die den kompletten Inhalt der Datei speichert ist die Frage welche der beiden Varianten besser ist in Bezug auf Performance?! Vielleicht habt ihr ja einen Rat dazu?

mfg Harald
Antworten