In einer Text Datei nach "=" einen wert ändern

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.
Antworten
BlueDogi
User
Beiträge: 30
Registriert: Mittwoch 29. April 2015, 22:25

Guten Tag zusammen,

ich würde gerne den Wert hinter einem "=" Zeichen ändern. Ein Auszug aus der Datei und mein bisherigen Programm:

Code: Alles auswählen

'''
Beispiel der zu durchsuchenden Datei:

CHANDATA(1)
N30110 $MA_CTRLOUT_MODULE_NR[0,AX1]=3
N30120 $MA_CTRLOUT_NR[0,AX1]=1
N30130 $MA_CTRLOUT_TYPE[0,AX1]=1
N30132 $MA_IS_VIRTUAL_AX[0,AX1]=0
N30134 $MA_IS_UNIPOLAR_OUTPUT[0,AX1]=0
N30200 $MA_NUM_ENCS[AX1]=2
N30220 $MA_ENC_MODULE_NR[0,AX1]=3
N30220 $MA_ENC_MODULE_NR[1,AX1]=3
N30230 $MA_ENC_INPUT_NR[0,AX1]=1
N30230 $MA_ENC_INPUT_NR[1,AX1]=2
N30240 $MA_ENC_TYPE[0,AX1]=4
N30240 $MA_ENC_TYPE[1,AX1]=4
N30242 $MA_ENC_IS_INDEPENDENT[0,AX1]=1
'''

import sys
maschinendatum = ["N30130","N30240"]
neuer_wert     = ["0"     ,"0"     ]

pfad_orginal_datei = "D:\NC-DatenAnpassen\NC_TESTNTP.arc"  #sys.argv[1]
pfad_neue_datei = "D:\NC-DatenAnpassen\NC_TESTNTP.arc_NEU" #sys.argv[1] +"_NEU"

orginal_datei = open(pfad_orginal_datei)
neue_datei = open(pfad_neue_datei,"w")

for Zeile in orginal_datei:
    for s in maschinendatum:
        if s in Zeile:
            print(Zeile)
            # Schreibe nun den Neunen Wert in neue Datei.
        else:
            neue_datei.write(Zeile)
            # Kopiere Zeile in neue Datei.

orginal_datei.close()
neue_datei.close()
Es gibt die N Parameter bis zu 31 mal. Aber alle sollen hinterher den selben Wert erhalten. Das extrahieren der für mich interesanten Zeilen habe ich erledigt. Aber ich bekomme grade keine Idee wie ich den String ändern kann das hinterher alles passt. Die Werte hinter dem = können auch mehrstellig sein.

Ich stehe hier bei grade auf dem Schlauch... Hoffe jemand hat einen Tipp für mich.

Viele Grüße
BlueDogi
BlackJack

@BlueDogi: Statt des ``in``-Operators solltest Du besser die `startswith()`-Methode auf Zeichenketten verwenden, denn Du willst ja wahrscheinlich nur Zeilen ändern wo das am Anfang steht und nicht irgendwo drin vorkommt. Und man kann der Methode auch mehr als einen Wert zum Testen geben, damit sparst Du eine Schleife und damit eine Einrückebene.

Wenn nach dem '=' grundsätzlich nur der Wert kommen kann, kannst Du doch einfach den Index vom '=' ermitteln (`index()`-Methode auf Zeichenketten) und dann mittels slicing alles vor dem Wert inklusive '=' kopieren und da dann den neuen Wert und ein Zeilenendezeichen anhängen.

Dateien sollte man zusammen mit der ``with``-Anweisung öffnen, dann braucht man sich keine Gedanken mehr ums explizite Schliessen machen.

`s` ist kein guter Name weil der nichts über die Bedeutung des Werte aussagt.

Funktionen sind eine tolle Sache. Man kann beispielsweise eine Funktion schreiben die den Test und das ersetzen macht, und die dann mit `itertools.imap()` (in Python 2) oder `map()` (in Python 3) auf die Eingabedatei anwenden und das Ergebnis dann direkt an die `writelines()`-Methode der Ausgabedatei übergeben.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst und durch folgendes Idiom aufgerufen wird:

Code: Alles auswählen

if __name__ == '__main__':
    main()
BlueDogi
User
Beiträge: 30
Registriert: Mittwoch 29. April 2015, 22:25

Vielen Dank,

Dass war sehr hilfreich. ich habe alles wie folgt geändert:

Code: Alles auswählen

import sys
maschinendatum = ("N30130","N30240")
neuer_wert     = ["0"     ,"0"     ]

pfad_orginal_datei = "D:\NC-DatenAnpassen\NC_TESTNTP.arc"  #sys.argv[1]
pfad_neue_datei = "D:\NC-DatenAnpassen\NC_TESTNTP.arc_NEU" #sys.argv[1] +"_NEU"



def main():
    with open(pfad_orginal_datei,"r") as orginal_datei:
        with open(pfad_neue_datei,"w") as neue_datei:
            for Zeile in orginal_datei:
                if Zeile.startswith(maschinendatum):
                    position_gleisch = Zeile.index("=")
                    neue_zeile = Zeile[:position_gleisch+1]
                    neue_zeile = neue_zeile + neuer_wert[0]
                    print(neue_zeile)
                    neue_datei.write(neue_zeile)

                else:
                    neue_datei.write(Zeile)



if __name__ == '__main__':
    main()
Jetzt habe ich noch 2 Probleme.
1. In meiner neu erstellten datei fehelen jetzt zeilenumbrüche:

Code: Alles auswählen

CHANDATA(1)
N30110 $MA_CTRLOUT_MODULE_NR[0,AX1]=3
N30120 $MA_CTRLOUT_NR[0,AX1]=1
N30130 $MA_CTRLOUT_TYPE[0,AX1]=0N30132 $MA_IS_VIRTUAL_AX[0,AX1]=0
N30134 $MA_IS_UNIPOLAR_OUTPUT[0,AX1]=0
N30200 $MA_NUM_ENCS[AX1]=2
N30220 $MA_ENC_MODULE_NR[0,AX1]=3
N30220 $MA_ENC_MODULE_NR[1,AX1]=3
N30230 $MA_ENC_INPUT_NR[0,AX1]=1
N30230 $MA_ENC_INPUT_NR[1,AX1]=2
N30240 $MA_ENC_TYPE[0,AX1]=0N30240 $MA_ENC_TYPE[1,AX1]=0N30242 $MA_ENC_IS_INDEPENDENT[0,AX1]=1
N30242 $MA_ENC_IS_INDEPENDENT[1,AX1]=1
N30244 $MA_ENC_MEAS_TYPE[0,AX1]=1
2. Wie bekomme ich den Index für den Neuen Wert heraus? BeiMaschinendatum N30130 ist index 0 richtig aber wie komme ich daran?
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

1. Häng an das Ende der Zeile einen Zeilenumbruch ( \n )

2. Wenn du auf der einen Seite Namen hast, und auf der anderen Seite Werte (also auf der einen Seite das Maschinendatum und auf der anderen Seite einen neuen Wert), solltest du über die Verwendung eines Dictionaries nachdenken.
BlackJack

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function


def main():
    machine_names_and_new_values = [('N30130', 0), ('N30240', 0)]
         
    pfad_orginal_datei = r'D:\NC-DatenAnpassen\NC_TESTNTP.arc'  #sys.argv[1]
    pfad_neue_datei = pfad_orginal_datei + '_NEU' #sys.argv[1] +"_NEU"
     
    with open(pfad_orginal_datei) as orginal_datei:
        with open(pfad_neue_datei, 'w') as neue_datei:
            for zeile in orginal_datei:
                for machine_name, new_value in machine_names_and_new_values:
                    if zeile.startswith(machine_name):
                        zeile = '{0}={1}\n'.format(
                            zeile[:zeile.index('=')], new_value
                        )
                        break
                neue_datei.write(zeile)


if __name__ == '__main__':
    main()
BlueDogi
User
Beiträge: 30
Registriert: Mittwoch 29. April 2015, 22:25

Vielen Dank! soweit läuft es...

Das mit str.format() habe ich erst nicht so recht verstanden aber dank Python Doku jetzt schon. Ich musste noch eine änderung mit reinbringen und zwar soll hinterher eine Datei in der die Änderungen vermerkt werden gespeichert werden.

Meine bisherige lösung sieht so aus:

Code: Alles auswählen

#!/usr/bin/env python

from __future__ import absolute_import, division, print_function

import sys

def main():
    maschinendatum_und_neune_werte = [('N30130', 0), ('N30240', 0)]
    pfad_orginal_datei = sys.argv[1]

    pfad_neue_datei = pfad_orginal_datei[:pfad_orginal_datei.index('.arc')]
    pfad_neue_datei = pfad_neue_datei + "_NEU.arc"
	
    pfand_aenderungs_datei = pfad_orginal_datei[:pfad_orginal_datei.index('.arc')]
    pfand_aenderungs_datei = pfand_aenderungs_datei + "_AENDERUNG.log"
	
    with open(pfad_orginal_datei) as orginal_datei:
        with open(pfad_neue_datei, 'w') as neue_datei:
            with open(pfand_aenderungs_datei, 'w') as aenderungs_datei:
                for alte_zeile in orginal_datei:
                    neue_zeile =""
                    for machine_name, neuer_wert in maschinendatum_und_neune_werte:
                        if alte_zeile.startswith(machine_name):
                            neue_zeile = '{0}={1}\n'.format(
                                alte_zeile[:alte_zeile.index('=')], neuer_wert
                            )
                            break
                    if alte_zeile != neue_zeile:
                        if neue_zeile != "":
                            geaenderte_zeile = '{0} -> {1}\n'.format(
                                alte_zeile, neuer_wert
                            )
                            aenderungs_datei.write(geaenderte_zeile)
                            neue_datei.write(neue_zeile)
                        else:
                            neue_datei.write(alte_zeile)
     
     
if __name__ == '__main__':
    main()
Habe aber das Problem das in der Änderungs Datei nun ein Zeilen umbruch zu viel ist. Die ausgabe:

Code: Alles auswählen

N30130 $MA_CTRLOUT_TYPE[0,AX1]=1
 -> 0
N30240 $MA_ENC_TYPE[0,AX1]=4
 -> 0
N30240 $MA_ENC_TYPE[1,AX1]=4
 -> 0
gibt es sowas wie replace? als nach "/n" suchen und durch "" ersetzen?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, es gibt soetwas wie replace, hier kann man aber auch ein rstrip('\n') benutzen. Da aber alle Zeilen regelmäßig aufgebaut sind, würde ich hier eher die Zeilen komplett in Teile aufspalten und ein Wörterbuch benutzen:

Code: Alles auswählen

import sys
import os

def main():
    maschinendatum_und_neun_werte = dict([('N30130', 0), ('N30240', 0)])
    pfad_orginal_datei = sys.argv[1]
    pfad_basis, _ = os.path.splitext(pfad_orginal_datei)

    pfad_neue_datei = pfad_basis + "_NEU.arc"
    pfad_aenderungs_datei = pfad_basis + "_AENDERUNG.log"

    with open(pfad_orginal_datei) as orginal_datei:
        with open(pfad_neue_datei, 'w') as neue_datei:
            with open(pfad_aenderungs_datei, 'w') as aenderungs_datei:
                for zeile in orginal_datei:
                    maschinendatum, rest = zeile.split(' ', 1)
                    if maschinendatum in maschinendatum_und_neun_werte:
                        neuer_wert = maschinendatum_und_neun_werte[maschinendatum]
                        maschinen_schluessel, alter_wert = rest.split('=', 1)
                        aenderungs_datei.write('{0} {1}={2} -> {3}\n'.format(maschinendatum, maschinen_schluessel, alter_wert, neuer_wert))
                        zeile = '{0} {1}={2}\n'.format(maschinendatum, maschinen_schluessel, neuer_wert)
                    neue_datei.write(zeile)

if __name__ == '__main__':
    main()
BlueDogi
User
Beiträge: 30
Registriert: Mittwoch 29. April 2015, 22:25

Ich habe mal deinen Vorschlag übernommen musste aber noch was ändern da meine Datei auch zeilen Ohne Lerzeichen oder mit anderen Informationen als Maschinen Daten hat. Dardurch kamm es in der Zeile 16 zum Fehler. Ich fange jetzt diesen Fehler ab.

In meiner Änderungs Datei werden aber alle Maschinen Daten angezeigt auch wenn die Werte sich nicht geändert haben. Das wollte ich durch eine abfrage auf ungleichheit des alten und neunen Wertes erschlagen. Leider Ohne erfolg siehe Zeile 24. Die wete die bei Print rauskommen sind auch gleich.

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function

import sys
import os

def main():
    maschinendatum_und_neun_werte = dict([('N30130', '0'), ('N30240', '0')])
    pfad_orginal_datei = sys.argv[1]
    pfad_basis, _ = os.path.splitext(pfad_orginal_datei)
    
    pfad_neue_datei = pfad_basis + "_NEU.arc"
    pfad_aenderungs_datei = pfad_basis + "_AENDERUNG.log"
     
    with open(pfad_orginal_datei) as orginal_datei:
        with open(pfad_neue_datei, 'w') as neue_datei:
            with open(pfad_aenderungs_datei, 'w') as aenderungs_datei:
                for zeile in orginal_datei:
                    try:
                        maschinendatum, rest = zeile.split(' ', 1)
                        if maschinendatum in maschinendatum_und_neun_werte:
                            neuer_wert = maschinendatum_und_neun_werte[maschinendatum]
                            maschinen_schluessel, alter_wert = rest.split('=', 1)
                            if alter_wert != neuer_wert:
                                print (alter_wert)
                                print (neuer_wert)
                                aenderungs_datei.write('{0} {1}={2} -> {3}\n'.format(maschinendatum, maschinen_schluessel, alter_wert, neuer_wert))
                            zeile = '{0} {1}={2}\n'.format(maschinendatum, maschinen_schluessel, neuer_wert)
                        neue_datei.write(zeile)
                    except:
                        neue_datei.write(zeile)

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

@BlueDogi: Ein nackstes ``except:`` ohne mindestens eine konkrete Ausnahme anzugeben die damit behandelt werden soll ist gar keine gute Idee. Und das dann auch noch über einen so langen Codeblock. Das behandelt *jede* Ausnahme so, auch welche mit denen Du gar nicht rechnest. Da wird die Fehlersuche dann extrem schwer und es kann auch eine Weile dauern bis man überhaupt merkt wenn man einen Fehler hat.

Also eine konkrete Ausnahme angeben und nur möglichst wenig Code damit testen. Dran denken das es zu ``try``/``except`` auch einen ``else``-Zweig geben kann! Oder man schreibt das `split()` mit `partition()` um.

Der Fehler ist übrigens das `alter_wert` noch ein '\n' hintendran hat und `neuer_wert` nicht, weshalb die immer verschieden sind.
BlueDogi
User
Beiträge: 30
Registriert: Mittwoch 29. April 2015, 22:25

Ok ich habe es noch mal überarbeitet. Die Funktion ist jetzt so wie ich sie mir vorstelle aber eine Frage hätte ich noch:
Der Fehler den ich abfangen möchte ist ja ein Werte Fehler und hat diesen Text "need more then 1 value to unpack" reich es wenn ich den Fehler so auswete 'except ValueError:' (Zeile 20) oder muss/kann ich Genau diesen Fehler "need more then 1 value to unpack" abfangen? Ich kann ja den Fehler als string in eine variable schreiben ausweten und wenn nicht der Fehler Text ansteht dan breche skript ab? könnte das so gehen?

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function

import sys, os

def main():
    maschinendatum_und_neun_werte = dict([('N30130', '0'), ('N30240', '0')])
    pfad_orginal_datei = sys.argv[1]
    pfad_basis, _ = os.path.splitext(pfad_orginal_datei)
    
    pfad_neue_datei = pfad_basis + "_NEU.arc"
    pfad_aenderungs_datei = pfad_basis + "_AENDERUNG.log"
     
    with open(pfad_orginal_datei) as orginal_datei:
        with open(pfad_neue_datei, 'w') as neue_datei:
            with open(pfad_aenderungs_datei, 'w') as aenderungs_datei:
                for zeile in orginal_datei:
                    try:
                        maschinendatum, rest = zeile.split(' ', 1)
                    except ValueError:
                        neue_datei.write(zeile)
                    else:
                        if maschinendatum in maschinendatum_und_neun_werte:
                            neuer_wert = maschinendatum_und_neun_werte[maschinendatum]
                            maschinen_schluessel, alter_wert = rest.split('=', 1)
                            if alter_wert.rstrip('\n') != neuer_wert:
                                aenderungs_datei.write('{0} {1}={2} -> {3}\n'.format(
                                    maschinendatum, maschinen_schluessel,
                                    alter_wert.rstrip('\n'), neuer_wert
                                ))
                            zeile = '{0} {1}={2}\n'.format(
                                maschinendatum, maschinen_schluessel, neuer_wert
                            )
                        neue_datei.write(zeile)
                    

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

@BlueDogi: Solange es keine Möglichkeit gibt das die Zeile einen anderen `ValueError` auslöst (der nicht genau so behandelt werden soll), reicht IMHO ein ``except ValueError:``. Ansonsten müsste man die Zeile mehr aufteilen und nur das „unpacking“ auf die Ausnahme prüfen. Den Text von Ausnahmen würde ich nicht auswerten. Der kann sich auch mal ändern.

Edit: In der Ausnahmebehandlung würde ich gar nichts machen (``pass``) und das schreiben der Zeile in die neue Datei auch aus dem ``else``-Zweig nehmen und *danach* machen. Denn diese Zeile muss am Ende ja in jedem Fall gemacht werden.
BlackJack

@BlueDogi: Ich finde das für eine Funktion ja mittlerweile auch schon ein bisschen zu umfangreich. Hier mal der Versuch die Verarbeitung der Zeilen als eigene Funktion heraus zu ziehen (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function

import os
import sys
from itertools import izip, tee


def verarbeite_zeilen(zeilen, maschinendatum2neuer_wert):
    for zeile in zeilen:
        maschinendatum, leerzeichen, rest = zeile.partition(' ')
        if leerzeichen and maschinendatum in maschinendatum2neuer_wert:
            zeile = '{0} {1}={2}\n'.format(
                maschinendatum,
                rest[:rest.index('=')],
                maschinendatum2neuer_wert[maschinendatum],
            )
        yield zeile


def main():
    maschinendatum2neuer_wert = {'N30130': '0', 'N30240': '0'}
    pfad_orginal_datei = sys.argv[1]
    pfad_basis, extension = os.path.splitext(pfad_orginal_datei)

    pfad_neue_datei = '{0}_NEU{1}'.format(pfad_basis, extension)
    pfad_aenderungs_datei = pfad_basis + '_AENDERUNG.log'

    with open(pfad_orginal_datei) as orginal_datei:
        with open(pfad_neue_datei, 'w') as neue_datei:
            with open(pfad_aenderungs_datei, 'w') as aenderungs_datei:
                alte_zeilen, zeilen = tee(orginal_datei)
                for alte_zeile, neue_zeile in izip(
                    alte_zeilen,
                    verarbeite_zeilen(zeilen, maschinendatum2neuer_wert)
                ):
                    if alte_zeile != neue_zeile:
                        aenderungs_datei.write(
                            '{0} -> {1}'.format(
                                alte_zeile.rstrip('\n'), neue_zeile
                            )
                        )
                    neue_datei.write(neue_zeile)
                

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