.cpp finden, auslesen, ändern, speichern

Code-Stücke können hier veröffentlicht werden.
Antworten
invi86
User
Beiträge: 7
Registriert: Mittwoch 26. Oktober 2016, 06:14

hi,

bin beginner und habe folgende aufgabe.

- ein verzeichnis (ordner) soll nach .cpp dateien (text) durchsucht werden "erledigt"
- in die dateien sollen geöffnet und ein gewisser string gesucht + gefunden werden "erledigt"
- danach soll der gefundene wert, durch einen anderen (append) ersetzt werden "auch zum teil"
- und schlussendlich abgespeichert und geschlossen werden "funktioniert auch"

problem ist, dass mein skript den entsprechenden eintrag findet, ihn auch abändert "jedoch alles was davor und danach kommt "LÖSCHT""
was muss ich ändern?

Vielen Dank.

Code: Alles auswählen

import re
with open("test.cpp", "r+") as f:
    match = re.split("^(?P<Text1>.*)COMPILE_TIME_ASSERT\(\s*(?P<Text2>.+),(?P<Text3>\s*)(?P<Text4>\S*)(?P<Text5>\s*\)\s*;s*)",
                     "COMPILE_TIME_ASSERT(TABLE_LENGTH(PopupType2ActionID) == EPopupType::ARRAY_SIZE , PopupType2ActionID_table_needs_revision);")
    result = '"' + match.group('Text4') + '"'
    if match:
           print("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
    f.write("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
    f.close()
Zuletzt geändert von Anonymous am Mittwoch 26. Oktober 2016, 07:53, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@invi86: das mit dem regulären Ausdruck macht das Verstehen sehr schwierig. zumal er höchstwahrscheinlich nicht das macht, was Du möchtest. Die Text3-Gruppe, die ja sowieso nur aus Leerzeichen besteht, wird nicht verwendet. Wenn Du die Gruppen sowieso nur durchnummerierst, dann brauchst Du ihnen erst gar keinen Namen geben. match wird schon vor dem `if match:` verwendet. match ist auch eine Liste und kein Match-Objekt, hat also kein group-Attribut. Wenn man Dateien mit with öffnet, braucht man sie nicht zu schließen. f wird auch nie gelesen, weshalb wohl auch das, was drinsteht, überschrieben wird.
BlackJack

@invi86: Warum verwendest Du `re.split()` wenn Du etwas *ersetzen* möchtest? Dafür gibt es `re.sub()`. Der Funktion kann man zum ersetzen auch eine Funktion übergeben, die ein Match-Objekt als Argument erwartet und den Ersetzungstext dafür als Rückgabewert liefert.
invi86
User
Beiträge: 7
Registriert: Mittwoch 26. Oktober 2016, 06:14

danke schonmal.

stimmt "re.split" war nur ein versuch zum rantasten, was passiert. wurde durch re.search ersetzt. werde nun mal mit re.sub probieren und sehen was passiert.

re.sub wurde nicht angenommen :(
wähle ich zu beginn statt "r+" für read+write -> "a" fügt er zwar den zu ändernden text am ende hinzu entfernt den ursprünglichen jedoch nicht.
hab keinen anhaltspunkt was ich abändern könnte, damit es funktioniert.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@invi86: nee, meine Kristallkugel hat auch im Moment kein klares Bild, was Du denn nun tatsächlich versucht hast :D . "a" steht für anfügen. Warum glaubst Du also, dass irgendetwas entfernt wird?
invi86
User
Beiträge: 7
Registriert: Mittwoch 26. Oktober 2016, 06:14

weil ich es in meiner test datei sehe :P

mit r+ ändert er in der datei zwar den vorhandenen wert in den gewünschten ab, behält aber nur diesen und entfernt den weiteren inhalt
mit a fügt er nur den zu ändernden text am ende ein, ohne jedoch den vorhandenen zu ändern.

ich will also zB. "hallo" (was vorhanden ist) durch "bye" (ersetzen), der rest darf nicht verändert werden.
BlackJack

@invi86: Was heisst `re.sub()` wurde nicht „angenommen”? Das ist *die* Funktion zum ersetzen. Alles andere wäre bloss die Funktionalität von der Funktion umständlicher nachbasteln. Was wenig Sinn macht.

Du musst die Datei lesen. Die Daten verändern. Und dann die Datei mit den veränderten Daten neu speichern. Das sind drei Schritte und zweimal `open()` mit verschiedenen Modi. Man kann in einer Textdatei ”mitten drin” nichts verändern, man muss die immer komplett neu schreiben. (Stimmt nicht ganz, aber es ist einfacher und sicherer das so zu tun.)

Wobei reguläre Ausdrücke hier auch nur funktionieren wenn man ein paar Annahmen machen kann die von C++-Quelltext im allgemeinen nicht garantiert werden. Zum Beispiel kann man das erste und das zweite Argument nur erfassen wenn im ersten niemals Kommas vorkommen können, oder wenn die `COMPILE_TIME_ASSERT`\s nicht auf mehrere Zeilen umgebrochen vorkommen und danach kein weiterer Aufruf kommt der Kommas enthält. Kommentare oder Zeichenketten werden von dem regulären Ausdruck auch nicht berücksichtigt. Was Du da versuchst ist alles andere als robust.
invi86
User
Beiträge: 7
Registriert: Mittwoch 26. Oktober 2016, 06:14

@invi86: Was heisst `re.sub()` wurde nicht „angenommen”? Das ist *die* Funktion zum ersetzen. Alles andere wäre bloss die Funktionalität von der Funktion umständlicher nachbasteln. Was wenig Sinn macht.

##### If you want to locate a match anywhere in string, use search() // deshalb verwende ich dieses #####

Du musst die Datei lesen. Die Daten verändern. Und dann die Datei mit den veränderten Daten neu speichern. Das sind drei Schritte und zweimal `open()` mit verschiedenen Modi. Man kann in einer Textdatei ”mitten drin” nichts verändern, man muss die immer komplett neu schreiben. (Stimmt nicht ganz, aber es ist einfacher und sicherer das so zu tun.)

##### ich soll aber leider keine neue datei erstellen lassen, sondern die vorhandene ändern mist #####
##### habe jetzt das erste open() im read modus und der zweite durchlauf mit write, er tut es jedoch aber überschreibt er immer noch alles.

Wobei reguläre Ausdrücke hier auch nur funktionieren wenn man ein paar Annahmen machen kann die von C++-Quelltext im allgemeinen nicht garantiert werden. Zum Beispiel kann man das erste und das zweite Argument nur erfassen wenn im ersten niemals Kommas vorkommen können, oder wenn die `COMPILE_TIME_ASSERT`\s nicht auf mehrere Zeilen umgebrochen vorkommen und danach kein weiterer Aufruf kommt der Kommas enthält. Kommentare oder Zeichenketten werden von dem regulären Ausdruck auch nicht berücksichtigt. Was Du da versuchst ist alles andere als robust.

###### es ist mehr zu übungszwecken gedacht, als robust zu sein hab keinen plan#######

Code: Alles auswählen

import re
with open("test.cpp", "r+") as f:
    match = re.search("^(?P<Text1>.*)COMPILE_TIME_ASSERT\(\s*(?P<Text2>.+),(?P<Text3>\s*)(?P<Text4>\S*)(?P<Text5>\s*\)\s*;s*)","COMPILE_TIME_ASSERT(TABLE_LENGTH(PopupType2ActionID) == EPopupType::ARRAY_SIZE , PopupType2ActionID_table_needs_revision);")
    result = '"' + match.group('Text4') + '"'
    if match:
           print("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
    f.write("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
    f.close()

Code: Alles auswählen

import re    
with open("test.cpp", "w") as f:
    match = re.search("^(?P<Text1>.*)COMPILE_TIME_ASSERT\(\s*(?P<Text2>.+),(?P<Text3>\s*)(?P<Text4>\S*)(?P<Text5>\s*\)\s*;s*)","COMPILE_TIME_ASSERT(TABLE_LENGTH(PopupType2ActionID) == EPopupType::ARRAY_SIZE , PopupType2ActionID_table_needs_revision);")
    result = '"' + match.group('Text4') + '"'
    if match:
           print("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
    f.write("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
    f.close()
Zuletzt geändert von Anonymous am Mittwoch 26. Oktober 2016, 10:15, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@invi86: der normale Vorgang ist, die Datei zu lesen, eine neue Datei mit dem neuen Inhalt zu schreiben und danach die neue Datei in den alten Dateinamen umbenennen. Hat den Vorteil, dass Dateien nicht kaputt gehen, falls es beim Schreiben Probleme gibt. Meine Frage vom ersten Post bleibt aber: wo liest Du denn die Datei überhaupt ein?

PS: hier im Forum gibt es den "Quote"-Knopf, dann kann man wenigstens sehen, was von Dir kommt, und auf was Du Dich von jemand anderem beziehst.
BlackJack

@invi86: Du möchtest doch aber nicht nur suchen (`search()`) sondern suchen und ersetzen, und dafür ist `sub()` da.

Deine Quelltexte werden nicht besser. Das sieht nach wildem herum raten aus, so funktioniert Programmieren aber nicht. Und Du zeigst nicht den tatsächlichen Quelltext, denn der erste lief nicht mal und die die Du jetzt zeigst entsprechen nicht dem was Du im Beitragstext beschreibst und bisher war noch keiner dabei der die Datei auch tatsächliche *liest*. Das wäre bei einlesen, ändern, schreiben, ja der erste Schritt. Solange Du den nicht vollziehst, kann auch der Rest nicht funktionieren, denn der hängt davon ja ab.

Das Problem kann man in die drei Teilprobleme Eingabe, Verarbeitung, Ausgabe aufteilen, was eine übliche Abfolge bei vielen Programmen/Teilprogrammen ist. Hier bietet es sich an erst einmal nur die Eingabe und die Ausgabe zu programmieren wenn beide die gleiche Struktur haben. Was sie in diesem Fall haben: Sowohl Ein- als auch Ausgabe ist der komplette Inhalt einer Datei.

Auch wenn Du eine vorhandene Datei ändern sollst, wäre es sinnvoll erst einmal eine neue Datei anzulegen, dann lässt sich das leichter testen ohne das man vorher immer die Eingabedatei erneut von woanders kopieren muss weil sie vom Programm verändert wurde.

Also fang mal an mit Lesen der Datei und dann schreiben in eine neue wobei zwishcen den beiden Vorgängen der Dateiinhalt an einen Namen gebunden werden sollte. Beispielsweise `content` oder `source`.

Wenn das funktioniert, dann mach Dich mit `re.sub()` vertraut und versuch damit mal eine einfache Ersetzung wobei als Ersetzung beim Aufruf keine feste Zeichenkette sondern eine Funktion verwendet werden sollte. Die musst Du schreiben. Einfach Ersetzung kann auch erst einmal sein, dass die gar nichts ersetzt, sondern nur den Suchtreffer zurück gibt.

Wenn das funktioniert, dann kannst Du einen regulären Ausdruck entwickeln die `COMPILE_TIME_ASSERT`\s sucht und die Funktion so anpassen, das diese die gewünschten Änderungen durchführt.

Insgesamt solltest Du dabei mehr auf Funktionen setzen, also nicht alles auf Modulebene schreiben, sondern die Teillösungen für die Teilprobleme als Funktionen schreiben. Und auch keinen Code wiederholen. Der Code der die 'PCC_STATIC_ASSERT'-Zeichenkette erstellt, steht in beiden Quelltexten von Dir *zweimal* da. Das ist schlecht. Du musst dann immer beide vorkommen ändern, wobei die Gefahr besteht, dass dabei Fehler gemacht werden und am Ende beide Ausdrücke nicht mehr zum gleichen Ergebnis führen.

Ausserdem könntest Du mal auf Hinweise eingehen wie das der `close()`-Aufruf wegen dem ``with`` unnötig ist und das Du auf `match.group()` zugreifst *bevor* Du überhaupt getestet hast ob es einen Treffer gab und auch wenn es keinen gab greifst Du nach dem Test wieder auf `match.group()` zu. Das führt zu eine Ausnahme falls es keinen Treffer gab, ist also ein Programmierfehler.
invi86
User
Beiträge: 7
Registriert: Mittwoch 26. Oktober 2016, 06:14

Vielen Dank auf jede Kritik.

Es ist eben so, dass ich mich sehr schwer tue mit der Thematik.

Eine Testdatei habe ich erstellt, welche meine test.cpp ist! Und wirklich Öffnen, Auslesen und Ändern funktioniert. Jedoch der Abschließende Teil nicht.

Und nochmal danke, ja ich habe überhaupt nirgends mein file eingelesen, dass habe ich jetzt auch erkannt.
Ich weiß nun, dass ich f.read() benötige jedoch noch nicht weiß an welche Stelle es bei der zweiten Initiailierung kommt
invi86
User
Beiträge: 7
Registriert: Mittwoch 26. Oktober 2016, 06:14

so habe nochmal geändert. wo liegt nun mein fehler?

Code: Alles auswählen

import re

array = []

with open("test.cpp", "r") as f:
       for line in f:
              pattern = re.compile("^(?P<Text1>.*)COMPILE_TIME_ASSERT\(\s*(?P<Text2>.+),(?P<Text3>\s*)(?P<Text4>\S*)(?P<Text5>\s*\)\s*;s*)")
              result = '"' + match.group('Text4') + '"'
              if match:
                     array.append("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
              else:
                     array.append(line)
              



###datei öffnen (schreibend)
###zeile für zeile schreiben
###datei schliessen

array = []

with open("test.cpp", "r+") as f:
       for line in f:
              pattern = re.compile("^(?P<Text1>.*)COMPILE_TIME_ASSERT\(\s*(?P<Text2>.+),(?P<Text3>\s*)(?P<Text4>\S*)(?P<Text5>\s*\)\s*;s*)")
              result = '"' + match.group('Text4') + '"'
              if match:
                     array.append("%sPCC_STATIC_ASSERT(%s,%s%s" % (match.group('Text1'), match.group('Text2'), result, match.group('Text5')))
              else:
                     array.append(line)
Zuletzt geändert von Anonymous am Mittwoch 26. Oktober 2016, 14:46, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@invi86: Dein Fehler liegt daran, dass Du wild herumrätst, statt Dir zu überlegen, was man Schritt für Schritt tun müßte, um das Problem erst in kleinere Teilprobleme zu zerlegen und diese kleinen Probleme dann in Programmcode umzusetzen. Wenn Du mal, meinetwegen in natürlicher Sprache aufschreibst, was konkret zu tun ist, dann können wir hier auch Tipps geben, wie man jeden einzelnen Schritt mit Python formuliert. Wie der Ansatz aussieht, hat Dir ja BlackJack schon gesagt.
invi86
User
Beiträge: 7
Registriert: Mittwoch 26. Oktober 2016, 06:14

ok.
- datei öffnen (lesend)
- datei lesen
- wert filtern/finden /ersetzen
- datei schließen

- datei öffen (schreibend)
- zeile für zeile schreiben
- datei schließen
BlackJack

Wie schon gesagt, schreib erst einmal Code der die Daten aus der Datei einliest, an einen Namen bindet und die Daten in eine neue Datei schreibt. Du behauptest zwar dass das funkioniert, wenn ich mir den Code anschaue den Du zeigst, dann funktioniert das *nicht*, denn wenn man dort das ändern entfernt, hat man immer noch kein funktionieren des Programm das einliest und schreibt. Zum einen weil Du alle Daten wieder löscht, zum zweiten weil Dein schreiben gar kein Schreiben ist sondern auch wieder ein lesen. Da wird nirgends was geschrieben. Du versuchst zu viel auf einmal zu lösen.

Schreib am besten mal drei Funktionen. Eine Funktion die den Inhalt der Datei einliest und zurück gibt.

Dann eine Funktion die Daten als Argument entgegen nimmt und in eine Datei schreibt.

Dann eine Funktion die Daten als Argument bekommt und veränderte Daten als Rückgabewert hat.

Das sind drei einzelne, unabhängige Probleme die man auch unabhängig voneinander lösen und testen kann.

Grundgerüst:

Code: Alles auswählen

def read(filename):
    # TODO Hier die Datei einlesen und als Zeichenkette zurückgeben.
    return '... content of file ...'


def process(content):
    # TODO Aus der Zeichenkette `content` eine mit den veränderten
    #   Daten erstellen und zurückgeben.
    return content


def write(filename, content):
    # TODO Hier die Zeichenkette `conten` in eine Datei schreiben.
    pass


def main():
    filename = 'test.cpp'
    temporary_filename = filename + '.bak'
    write(temporary_filename, process(read(filename)))
    # TODO Hier die temporäre Datei in die ursprüngliche umbenennen.


if __name__ == '__main__':
    main()
BlackJack

In CoffeeScript könnte das ganze (Verzeichnisbaum nach C++-Quelltextdateien absuchen und in diesen die Ersetzung vornehmen) so aussehen:
[codebox=coffeescript file=Unbenannt.coffee]#!/usr/bin/env coffee
'use strict'

fs = require 'fs'
path = require 'path'

ENCODING = 'utf8'
ASSERT_RE = /COMPILE_TIME_ASSERT\s*\(\s*(.+),\s*([^)]+)\)\s*;/g


changeFileContent = (filename, encoding=ENCODING) ->
fs.readFile filename, encoding, (error, data) ->
if error then throw error

data = data.replace ASSERT_RE, (_, argument1, argument2) ->
"PCC_STATIC_ASSERT(#{argument1}, \"#{argument2}\")"

temporaryFilename = filename + '~'
fs.writeFile temporaryFilename, data, encoding, (error, data) ->
if error then throw error
fs.renameSync temporaryFilename, filename


processFiles = (basePath) ->
fs.readdir basePath, (error, names) ->
if error
console.log error
else
for name in names
fullName = path.join basePath, name
try
stats = fs.statSync fullName
catch error
console.log error
stats = null

if stats?
if stats.isDirectory()
processFiles fullName
else if path.extname(fullName) is '.cpp'
console.log 'process:', fullName
try
changeFileContent fullName
catch error
console.log error
null


main = -> processFiles './tmp/forum'


main() if require.main == module[/code]
Antworten