2009-05-11 7 views
11

Ich bin ein Neuling für Python und ich benutze es, um etwas haarige EDI-Zeug zu schreiben, das unser Lieferant benötigt.Schreiben/Analysieren einer Datei mit fester Breite mit Python

Im Grunde brauchen sie eine 80-stellige Textdatei mit fester Breite, mit bestimmten "Chunks" des Feldes mit Daten und anderen leer gelassen. Ich habe die Dokumentation, damit ich weiß, wie lang jeder "Brocken" ist. Die Antwort, die ich zurückbekomme, ist leichter zu parsen, da sie bereits Daten enthält und ich Pythons "Slices" verwenden kann, um das zu extrahieren, was ich brauche, aber ich kann kein Slice zuweisen - das habe ich schon probiert, weil es gut klingt Lösung, und es hat nicht funktioniert, da Python-Strings unveränderlich sind :)

Wie ich schon sagte, ich bin wirklich ein Neuling für Python, aber ich freue mich darauf, es zu lernen :) Wie würde ich das tun? Idealerweise möchte ich sagen können, dass der Bereich 10-20 gleich "Foo" ist und dass es die Zeichenkette "Foo" mit 7 zusätzlichen Leerzeichen ist (vorausgesetzt, dass das Feld eine Länge von 10 hat) und dass es a ist Teil des größeren 80-stelligen Feldes, aber ich bin mir nicht sicher, wie ich das mache, was ich denke.

+0

Verarbeiten Sie X12-EDI-Nachrichten? Das Layout ist nicht wirklich festgelegt. Verarbeiten Sie ein anderes Format? Wenn ja, ist es nicht wirklich [EDI] ist es? Es ist nur ein festes Dateilayout. –

+0

Ich habe keine Ahnung, wirklich. Sie bezeichnen sie in ihrer gesamten Dokumentation als "EDI". Alles, was ich weiß, ist, dass ich ihnen einen Eintrag senden muss (sie nennen es einen "H0" -Datensatz und sie senden mir eine Datei zum Parsen.) –

+0

Der ISA-Header von X12 ist eine feste Breite (die erste Zeile) als Trennzeichen werden nicht bis zum Ende der Zeile deklariert – charlesbridge

Antwort

16

Sie müssen keine Slices zuweisen, erstellen Sie einfach die Zeichenfolge mit % formatting.

Ein Beispiel mit einem festen Format für 3 Datenelemente:

>>> fmt="%4s%10s%10s" 
>>> fmt % (1,"ONE",2) 
' 1  ONE   2' 
>>> 

Gleiche, Feldbreite mit den Daten geliefert:

>>> fmt2 = "%*s%*s%*s" 
>>> fmt2 % (4,1, 10,"ONE", 10,2) 
' 1  ONE   2' 
>>> 

Datentrenn und Feldbreiten und unter Verwendung von zip() und str.join() tricks:

>>> widths=(4,10,10) 
>>> items=(1,"ONE",2) 
>>> "".join("%*s" % i for i in zip(widths, items)) 
' 1  ONE   2' 
>>> 
0

Es ist ein wenig schwierig, Ihre Frage zu analysieren, aber ich sammle, dass Sie eine Datei oder ein dateiähnliches Objekt erhalten, es lesen und einige der Werte durch einige Geschäftslogik-Ergebnisse ersetzen. Ist das richtig?

Der einfachste Weg Zeichenfolge Unveränderlichkeit zu überwinden, ist eine neue Zeichenfolge zu schreiben:

# Won't work: 
test_string[3:6] = "foo" 

# Will work: 
test_string = test_string[:3] + "foo" + test_string[6:] 

gesagt hat, dass es klingt wie es Ihnen wichtig ist, dass Sie etwas mit dieser Zeichenfolge zu tun, aber ich bin nicht sicher, genau das ist es. Schreiben Sie es zurück in eine Ausgabedatei und versuchen Sie, eine Datei an Ort und Stelle oder etwas anderes zu bearbeiten? Ich erwähne das, weil der Vorgang des Erzeugens eines neuen Strings (der zufällig denselben Variablennamen wie der alte String hat) die Notwendigkeit hervorheben sollte, nach der Transformation einen expliziten Schreibvorgang auszuführen.

7

Hoffentlich verstehe ich was du bist e Suchen nach: Eine Möglichkeit, jeden Teil der Linie bequem mit einer einfachen Variablen zu identifizieren, sie aber auf die richtige Breite auszugeben?

Das Snippet unten können Sie geben, was Sie

wollen
class FixWidthFieldLine(object): 

    fields = (('foo', 10), 
       ('bar', 30), 
       ('ooga', 30), 
       ('booga', 10)) 

    def __init__(self): 
     self.foo = '' 
     self.bar = '' 
     self.ooga = '' 
     self.booga = '' 

    def __str__(self): 
     return ''.join([getattr(self, field_name).ljust(width) 
         for field_name, width in self.fields]) 

f = FixWidthFieldLine() 
f.foo = 'hi' 
f.bar = 'joe' 
f.ooga = 'howya' 
f.booga = 'doin?' 

print f 

Dies ergibt:

hi  joe       howya       doing  

Es funktioniert durch eine Variable auf Klassenebene zu speichern, fields die die Reihenfolge jedes Feld sollte in dem aufzeichnet erscheinen in der Ausgabe zusammen mit der Anzahl der Spalten, die das Feld haben sollte. In __init__ gibt es entsprechend benannte Instanzvariablen, die anfänglich auf eine leere Zeichenfolge gesetzt werden.

Die Methode __str__ gibt diese Werte als Zeichenfolge aus. Es verwendet ein Listenverständnis über das Attribut fields auf Klassenebene, sucht den Instanzwert für jedes Feld nach seinem Namen und korrigiert dann seine Ausgabe gemäß den Spalten. Die resultierende Liste von Feldern wird dann durch eine leere Zeichenfolge zusammengefügt.

Beachten Sie, dass die Eingabe nicht analysiert wird, obwohl Sie den Konstruktor leicht überschreiben könnten, um eine Zeichenfolge zu verwenden und die Spalten gemäß den Feld- und Feldbreiten in fields zu analysieren. Es werden auch keine Instanzwerte überprüft, die länger als die zugewiesene Breite sind.

0

Sie können die Zeichenfolge in eine Liste konvertieren und die Slice-Manipulation durchführen.

>>> text = list("some text") 
>>> text[0:4] = list("fine") 
>>> text 
['f', 'i', 'n', 'e', ' ', 't', 'e', 'x', 't'] 
>>> text[0:4] = list("all") 
>>> text 
['a', 'l', 'l', ' ', 't', 'e', 'x', 't'] 
>>> import string 
>>> string.join(text, "") 
'all text' 
+0

Intersting. Sie müssen nicht in eine Liste konvertieren, um zu extrahieren. Das ist albern. Aber eine Liste erstellen und dann zu einer Zeichenfolge zusammenfalten ... es gibt Ihnen, was ein wenig aussieht Bit wie eine "veränderbare Zeichenkette" - nur wenn Sie genügend Speicherplatz vormerken. –

+0

Eigentlich müssen Sie nichts vorallokieren, wenn Sie sich nicht allzu sehr um die Leistung kümmern. Der Listentyp wird automatisch mehr Platz zuweisen, wenn der Bereich sliced ​​ist einem größeren Bereich zugeordnet – Skurmedel

+0

Auch die Listenumwandlung ist der Übersichtlichkeit halber besser geeignet.Es könnte natürlich besser sein, wenn er Daten von Anfang an direkt in eine Liste einliest, aber das wollte ich nicht zeigen. – Skurmedel

0

Es ist einfach zu schreiben Funktion zu "ändern" Zeichenfolge.

def change(string, start, end, what): 
    length = end - start 
    if len(what)<length: what = what + " "*(length-len(what)) 
    return string[0:start]+what[0:length]+string[end:] 

Verbrauch:

test_string = 'This is test string' 

print test_string[5:7] 
# is 
test_string = change(test_string, 5, 7, 'IS') 
# This IS test string 
test_string = change(test_string, 8, 12, 'X') 
# This IS X string 
test_string = change(test_string, 8, 12, 'XXXXXXXXXXXX') 
# This IS XXXX string 
7

Sie justify Funktionen verwenden können, um linksbündig, rechtsbündig und in der Mitte eine Zeichenfolge in einem Feld gegebener Breite.

'hi'.ljust(10) -> 'hi  ' 
0

Ich benutzte Jarret Hardies Beispiel und modifizierte es leicht. Dies ermöglicht die Auswahl der Art der Textausrichtung (links, rechts oder zentriert.)

class FixedWidthFieldLine(object): 
    def __init__(self, fields, justify = 'L'): 
     """ Returns line from list containing tuples of field values and lengths. Accepts 
      justification parameter. 
      FixedWidthFieldLine(fields[, justify]) 

      fields = [(value, fieldLenght)[, ...]] 
     """ 
     self.fields = fields 

     if (justify in ('L','C','R')): 
      self.justify = justify 
     else: 
      self.justify = 'L' 

    def __str__(self): 
     if(self.justify == 'L'): 
      return ''.join([field[0].ljust(field[1]) for field in self.fields]) 
     elif(self.justify == 'R'): 
      return ''.join([field[0].rjust(field[1]) for field in self.fields]) 
     elif(self.justify == 'C'): 
      return ''.join([field[0].center(field[1]) for field in self.fields]) 

fieldTest = [('Alex', 10), 
     ('Programmer', 20), 
     ('Salem, OR', 15)] 

f = FixedWidthFieldLine(fieldTest) 
print f 
f = FixedWidthFieldLine(fieldTest,'R') 
print f 

Returns:

Alex  Programmer   Salem, OR  
     Alex   Programmer  Salem, OR 
1

Ich weiß, dieser Thread ist ziemlich alt, aber wir verwenden eine Bibliothek namens django-copybook. Es hat nichts mit Django (mehr) zu tun. Wir benutzen es, um zwischen cobol-Dateien mit fester Breite und Python zu wechseln. Sie erstellen eine Klasse Ihre feste Breite Satzaufbau zu definieren und können leicht bewegen sich zwischen typisierten Python-Objekte und feste Breite Dateien:

USAGE: 
class Person(Record): 
    first_name = fields.StringField(length=20) 
    last_name = fields.StringField(length=30) 
    siblings = fields.IntegerField(length=2) 
    birth_date = fields.DateField(length=10, format="%Y-%m-%d") 

>>> fixedwidth_record = 'Joe     Smith       031982-09-11' 
>>> person = Person.from_record(fixedwidth_record) 
>>> person.first_name 
'Joe' 
>>> person.last_name 
'Smith' 
>>> person.siblings 
3 
>>> person.birth_date 
datetime.date(1982, 9, 11) 

Es kann auch Situationen umgehen ähnlich wie Cobol der AUFTRITT Funktionalität wie wenn ein bestimmter Abschnitt X-mal wiederholt

Verwandte Themen