String mit fester Satzlänge in ASCII umwandel

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
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen,

stehe vor dem Problem, eine ASCII-Datei mit fester Satzlänge zu erstellen, die versendet wird.
Dabei muß ich zuvor Text (UTF-8) in ASCII umzuwandeln, damit die Satzlänge stimmt.

Beispiel:
Satzlänge_1 = 30
String_1 = 'Ösil Müller-Määr'
Satzlänge_2 = 10
String_2 = 'Müllallee'

Ergebnis UTF-8, Satzlänge == 40:

Code: Alles auswählen

'Herr Ösil Müller-Määr         Müllallee '
Das Ergebnis beim Empfänger, könnte dann so aussehen.
Ergebnis ASCII, Satzlänge > 40:

Code: Alles auswählen

'Herr \xd6sil M\xfcller-M\xe4\xe4r         M\xfcllallee '
Da es auch um Adressen (Namen, Straße, Ort) geht, scheidet das Ersetzen von z.B. Ö in Oe, ü in ue usw. aus, da eine Verwechslungsgefahr möglich sein könnte.

Der Empfänger schreibt dazu:
Zeichensatz:
PC-DOS
zum Beispiel deutsche Sonderzeichen:
Ä = ASC(142)
Ö = ASC(153)
Ü = ASC(154)
ä = ASC(132)
ö = ASC(148)
ü = ASC(129)
ß = ASC(225)
Wie kann ich dieses Problem umsetzen, so daß die vorgegebene Satzlänge eingehalten wird?

Grüße Nobuddy
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Nobuddy hat geschrieben: Wie kann ich dieses Problem umsetzen, so daß die vorgegebene Satzlänge eingehalten wird?
Gar nicht!

Du hast ja nicht den Text in ASCII umgewandelt (denn das geht gar nicht), sondern ihn nur in eine repräsentierbare Form gebracht. Das ist ein Unterschied!

Das Zielsystem müsste diese (ggf. Python spezifische) Form wieder decodieren o.ä.; wobei sich dann die Frage stellt, ob sie das können... denn wenn ja, würden sie wohl kaum ASCII vorschreiben ;-)

Zudem müsstest Du beim Prüfen auf die Länge die Escape-Sequenzen berücksichtigen! Sprich: Ein UTF-8 codierter Eingabestring mit Länge 30 ist nicht notwendiger Weise überführbar. Dann müssten Zeichen abgeschnitten werden. Auch dafür bräuchtest Du Regeln.

Aber um auf Deine Frage eine kleine Antwort zu geben: Auch jetzt scheinst Du ja einen Mechanismus zu verwenden, der die Strings mit Leerzeichen auffüllt und verbindet. Dazu muss dieser Mechnismus eigentlich die Länge der Strings ermitteln können. Das sollte mit ASCII-Texten ebenso funktionieren. Der Zwischenraum sollte eben kleiner werden...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Nobuddy: Du schreibst, der Empfänger-Zeichensatz soll PC-DOS sein, also wahrscheinlich Code page cp437. Das kannst Du direkt in Python auch benutzen. Was soll aber mit Zeichen passieren, die nicht in diesem Encoding darstellbar sind? Die Länge kannst Du mit Stringformatierung festlegen:

Code: Alles auswählen

'%-30s%-10s'%(name.encode('cp437')[:30],ort.encode('cp437')[:10])
Wer ist der Empfänger? Wenn es irgendwie geht, sollte der auf ein sinnvolles Format umsteigen!
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Gar nicht!
Das habe ich befürchtet... :(
Du hast ja nicht den Text in ASCII umgewandelt (denn das geht gar nicht), sondern ihn nur in eine repräsentierbare Form gebracht. Das ist ein Unterschied!
Die repräsentierbare Form, war nur zur Veranschaulichung gedacht.
Das Zielsystem müsste diese (ggf. Python spezifische) Form wieder decodieren o.ä.; wobei sich dann die Frage stellt, ob sie das können... denn wenn ja, würden sie wohl kaum ASCII vorschreiben ;-)
Das ist klar, das Problem mit den Selbstlauten, ist dadurch aber nicht gelöst, denn gerade bei Adressen kommen leider Selbstlaute vor, die beim Ersetzen von z.B. ü in ue, zu Namensverwechslungen führen können.
Zudem müsstest Du beim Prüfen auf die Länge die Escape-Sequenzen berücksichtigen! Sprich: Ein UTF-8 codierter Eingabestring mit Länge 30 ist nicht notwendiger Weise überführbar. Dann müssten Zeichen abgeschnitten werden. Auch dafür bräuchtest Du Regeln.
Das ist nicht das Problem.
Aber um auf Deine Frage eine kleine Antwort zu geben: Auch jetzt scheinst Du ja einen Mechanismus zu verwenden, der die Strings mit Leerzeichen auffüllt und verbindet. Dazu muss dieser Mechnismus eigentlich die Länge der Strings ermitteln können. Das sollte mit ASCII-Texten ebenso funktionieren. Der Zwischenraum sollte eben kleiner werden...

Code: Alles auswählen

zeichen = 30
string_1 = 'Ich bin ein Kängeru!'
zdiff = zeichen - len(mein_string)
string_1_neu = '{}[{}'.format(string_1, zdiff * ' ')
Das Ergebnis würde bei mir stimmen, aber leider nicht beim Empfänger.

Wäre das vielleicht eine Möglichkeit:

Code: Alles auswählen

zeichen = 30
string_1 = 'Ich bin ein Kängeru!'
# ä = ASC(132)
string_1 = string_1.replave('ä', 'ASC(132)')
zdiff = zeichen - len(mein_string)
string_1_neu = '{}[{}'.format(string_1, zdiff * ' ')
if len(string_1_neu) > zeichen:
    string_1_neu = string_1_neu[:zeichen]
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Sirius3 hat geschrieben:@Nobuddy: Du schreibst, der Empfänger-Zeichensatz soll PC-DOS sein, also wahrscheinlich Code page cp437. Das kannst Du direkt in Python auch benutzen. Was soll aber mit Zeichen passieren, die nicht in diesem Encoding darstellbar sind? Die Länge kannst Du mit Stringformatierung festlegen:

Code: Alles auswählen

'%-30s%-10s'%(name.encode('cp437')[:30],ort.encode('cp437')[:10])
Wer ist der Empfänger? Wenn es irgendwie geht, sollte der auf ein sinnvolles Format umsteigen!
Der Empfänger ist ein Lieferant.
Ich hatte schon einmal den Vorschlag ihm unterbreitet, leider ohne Erfolg.

Ich werde Dein Code-Beispiel ausprobieren, vielleicht komme ich damit weiter.
BlackJack

@Nobuddy: Ich denke nicht Du sollst Umlaute durch die Zeichenfolge 'ASC(…)' ersetzen, sondern in das was bei dieser Funktion heraus kommt. Ich vermute mal es ist die Funktion gemeint die in vielen BASIC-Dialekten existiert und eine Zahl in ein Zeichen umwandelt. Das heisst da ist Sirius3 mit seinem Kodieren als 'cp437' schon auf der richtigen Spur. Wenn die Texte bei Dir als UTF-8 kodierte Bytestrings vorliegen, musst Du sie natürlich vorher dekodieren — Sirius3's Quelltext geht bei `name` und `ort` schon von Unicode-Objekten aus.
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Ich habe jetzt mal anhand Sirius Vorschlag, folgendes überprüft.

Code: Alles auswählen

#!/usr/bin/python3
# -*- coding: utf-8 -*-

name = 'Ösil Müller-Määr'
ort = 'Müllallee'
_name = name.encode('cp437')
_ort = ort.encode('cp437')
print(_name)
b'\x99sil M\x81ller-M\x84\x84r'
print(len(_name))
16
print(_ort)
b'M\x81llallee'
print(len(_ort))
9
Die Ausgabe von _name enthält mehr als 16 Zeichen, verwende ich len(_name), wird 16 ausgegeben.
Wie kann ich das verstehen, bzw. muß ich mir das vorstellen?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

In den Daten sind Escape-Sequenzen enthalten, welche für nicht-darstellbare Zeichen verwendet werden "\x84" ist zum Beispiel ein Zeichen und nicht vier. Es ist nichts anderes als "\n" für eine neue Zeile, nur dass es keinen abkürzenden Bezeichner gibt.
Das Leben ist wie ein Tennisball.
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo,
habe gerade nochmals mit dem Lieferanten gesprochen.
Der hat mir mitgeteilt, daß das Problem bekannt ist, aber keine Änderungen derzeit dafür vorgenommen werden.

Es handelt sich um den ASCII-Code, der keine Selbstlaute kennt und daher auch kein Encoding Sinn macht.
Die einzige Alternative, ist nur das Ersetzen von Ä in AE, ä in ae .... usw.

Grüße und Danke Nobuddy
BlackJack

@Nobuddy: Oder man lässt die Punkte weg. Wenn man so etwas nicht selber schreiben möchte, gibt es das `undecode`-Modul beim Python Package Index.

Code: Alles auswählen

In [6]: unidecode.unidecode(u'Hübner Straße')
Out[6]: 'Hubner Strasse'
Der Grund warum das 'u' nicht durch 'ue' ersetzt wird, ist dass das wahrscheinlich nicht in jeder Sprache möglich ist es so umzuschreiben.
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack,
Danke für den Hinweis, das kannte ich noch nicht.
Das wäre auch eine gute Alternative.
Daß die Pünktchen fehlen, würde mich persönlich nicht stören.
Man weiß aber nie, ob es evtl. unter der gleichen Postleitzahl, eine Hübner Straße und auch eine Hubner Straße gibt.

Habe das Paket python3-unidecode unter Kubuntu installiert.

Code: Alles auswählen

In [1]: import unidecode
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
/home/whtb/<ipython-input-1-ed476289ac2e> in <module>()
----> 1 import unidecode

ImportError: No module named unidecode
Defektes Paket oder falscher Importaufruf?
BlackJack

@Nobuddy: Sollten solche Fälle tatsächlich vorkommen, dann dürften die Postboten in dem Zustellbezirk aber sich darauf achten. Zumal es unwahrscheinlich ist, dass der Rest der Adresse auch zu beiden Strassen passt, also das zum Beispiel in beiden ein Peter Meier an der gleichen Hausnummer wohnt.

Sofern das auch ein IPython für Python 3 war, hätte das eigentlich funktionieren sollen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Eine Sache noch: Bei einigen Dingen *darf* ein Name nicht verändert werden. Rechnungen sind da ein beliebtes Beispiel... wenn der Adressat nun einmal "Müller" heißt, dann darf er nicht einfach so zu "Mueller" gemacht werden. Bei Provatpersonen und Endkundengeschäft kräht da einfach kein Hahn nach, aber spätestens wenn Firmenkunden ins Spiel kommen, wird es heikel...

@BlackJack: Nettes Modul; kannte ich noch nicht :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Tatsächlich war mein IPython nicht Ipython3.
Habe Ipython installiert.

Code: Alles auswählen

In [6]: In [1]: import unidecode

unidecode.unidecode(u'Hübner Straße')
  File "<ipython-input-6-64f1d6f8200e>", line 1
    unidecode.unidecode(u'Hübner Straße')
                                         ^
SyntaxError: invalid syntax


In [7]: unidecode.unidecode('Hübner Straße')
Out[7]: 'Hubner Strasse'
Ohne 'u' vor dem String funktioniert es bei mir.
BlackJack

@Nobuddy: Das u als Präfix funktioniert erst wieder ab Python 3.4.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BlackJack hat geschrieben:@Nobuddy: Das u als Präfix funktioniert erst wieder ab Python 3.4.
In 3.3 ging es auch schon.
BlackJack

:oops: Diese Python-Entwicklung geht einfach zu rasant für mich. :-)
Nobuddy
User
Beiträge: 1023
Registriert: Montag 30. Januar 2012, 16:38

Ja, das ist der reine Wahnsinn! :lol:

Ich habe noch Python 3.2, beim nächsten Upgrade unter Kubuntu, dürfte es dann auch 3.3 oder höher sein. :wink:
Benutzeravatar
Balmung
User
Beiträge: 44
Registriert: Sonntag 17. März 2013, 18:36

Und ich hab grad auf 3.4 unter Arch Linux aktualisiert. 8)
»Honk Honk«
Antworten