Hilfe bei Umsetzung von String-/Listen-Operation

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
#cousin#
User
Beiträge: 25
Registriert: Mittwoch 18. März 2009, 22:56
Wohnort: Mannheim
Kontaktdaten:

Hallo Zusammen,

ich sitze seit Tagen an einem Problem mit einem String und Operationen auf Diesem.

Die Liste besteht aus 8-Bit Binär Werten getrennt durch ein ";"

Code: Alles auswählen

10000110;01101000;01101110;01101000;01101110;01110010;01100000;10001010;01101000;10000010;10000110;10001100;10001010;
01110010;01100100;01100000;01101110;01100100;10000100;10000010;01100000;10000110;01100000;10000010;10000100;10000010;
10001010;01110010;10001100;01101110;10000100;01101110;01110010;10000110;01100000;10001010;
Ich möchte diese 8-Bit nun in 7-Bit Werte ändern, indem ich bei dem Ersten Block die erste Zahl abspalte und an das Ende des 2. Blockes anhänge. Bei dem 2. Block sollen dann die ersten beiden Zahlen abgespalten werden und an das Ende des 3. Blocks angehängt werden....usw....

in dem nachfolgenden Bild ist das Problem nochmal graphisch erklärt mit anderen Werten als den Obigen.
Bild

Ich stehe aktuell komplett auf dem Schlauch wie ich das am Besten in Python umsetze. Es wäre super, wenn mir Jemand von euch dabei helfen könnte.

Vielen Dank im Voraus!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Zeig doch mal ein wenig Code oder beschreibe genauer, wo du Probleme bei der Umsetzung hast. Es hört sich sonst so an, als ob du fertigen Code von uns haben wolltest ;-)
Das Leben ist wie ein Tennisball.
webster
User
Beiträge: 4
Registriert: Mittwoch 13. Mai 2009, 20:49

Was soll eigentlich mit Werten passieren, die an Position 8 und höher stehen? Da seh ich noch keine sinnvolle Fortsetzung.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Code: Alles auswählen

>>> b = "10000110;01101000;01101110;01101000;01101110;01110010;01100000;10001010;"
>>> raw = b.replace(";", "")
>>> [raw[i:i+7] for i in range(0, len(raw), 7)]
['1000011', '0011010', '0001101', '1100110', '1000011', '0111001', '1100100', '1100000', '1000101', '0']
>>> [int(raw[i:i+7], 2) for i in range(0, len(raw), 7)]
[67, 26, 13, 102, 67, 57, 100, 96, 69, 0]
MFG HerrHagen
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@HerrHagen: Den Ansatz habe ich beim ersten Durchlesen auch vermutet, das ist aber nicht das gesuchte Ergebnis.
Das Leben ist wie ein Tennisball.
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

Das scheint zu tun, was du willst:

Code: Alles auswählen

def recode_str(s, sep=";", prev=""):
    """WILL silently produce garbage with wrongly grouped strings"""
    OUT_SIZE = 7
    FILLER="" #to fill 8 'Bits'
    #FILLER="0"
    offset=len(prev) 
    res = []
    for i, part in enumerate (s.split(sep)):
        if part: # avoid empty parts
            i_off = (i + offset) % (OUT_SIZE+1)
            res.append(FILLER + part[i_off+1:] + prev)
            prev = part[:i_off+1] if i_off < OUT_SIZE else ""
    return ";".join(res), prev
Aber sag mir bitte, dass du nicht irgendwo Zahlen oder Strings in diese Binär-strings verwandlest, nur um sie wieder in Zahlen zu verwandeln - das geht mit , "&", ">>", "<<", etc. (und ggf. "ord()", "chr()") besser, logischer und schneller...

hth, Jörg
Wir haben schon 10% vom 21. Jahrhundert hinter uns!
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Stimmt..., vielleicht in etwa so..?:

Code: Alles auswählen

>>> b = "11101000;00110010;10011011;11111101;01000110;11011001;11011001;"
>>> l = b.split(";")
>>> for i in range(len(l) - 1):
	l[i+1] += l[i][:i+1]
	l[i] = l[i][i+1:]

>>> l
['1101000', '1100101', '1101100', '1101100', '1101111', '0101000', '1110110', '1101100']
EDIT: Da war offensichtlich schon jemand schneller (und ausführlicher)
#cousin#
User
Beiträge: 25
Registriert: Mittwoch 18. März 2009, 22:56
Wohnort: Mannheim
Kontaktdaten:

Hi Zusammen,

vielen Dank für die vielen Antworten.

Ich kann euch leider keine code-Fragmente posten, da ich schon ein Problem damit habe, wie ich das Ganze überhaupt angehe. Mir ist bewusst, dass ich dafür Schleifen benötige, aber schon bei dem Schleifenkopf harpert es.


@webster:
der 8. Wert entseht aus dem Teil vom 7. Wert der abgespalten wird und ab dem 9. Wert geht es wieder von Vorne los.

@b.esser-wisser:
am Ende läuft es darauf hinaus. Ich versuche das PDU encoding für SMS in Python zu implementieren und da ist es notwendig, die HEX-Werte in 8-Bit-inär zu wandeln, dann eben diese "Abspaltung" von Bits zu machen, und danach das Ganze dann wieder in HEX zu verwandeln und dann in ASCII. Am Ende habe ich dann aus dem gespeicherten HEX die orginal SMS wiederhergestellt.

@HerrHagen:
vielen Dank für das Code-Fragment. Ich werde es nun mal testen und schauen, was nach Position 7 passiert.
BlackJack

@#cousin#: Diese ganzen Zeichenkettenumwandlungen sind nicht nötig. Eine halbwegs vernünftige Lösung würde direkt auf den Bits operieren und nicht mit Zeichenketten mit verschiedenen Darstellungen von Zahlen in verschiedenen Basen arbeiten.

Der Schleifenkopf ist doch einfach: Du musst über die Oktette iterieren. Und dann in jedem Schritt etwas machen. Dabei musst Du Dir etwas aus dem jeweils vorherigen Schritt merken. Spiel das doch einfach mal auf dem Papier Schritt für Schritt durch.
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

#cousin# hat geschrieben:Hi Zusammen,

@b.esser-wisser:
am Ende läuft es darauf hinaus. Ich versuche das PDU encoding für SMS in Python zu implementieren und da ist es notwendig, die HEX-Werte in 8-Bit-inär zu wandeln, dann eben diese "Abspaltung" von Bits zu machen, und danach das Ganze dann wieder in HEX zu verwandeln und dann in ASCII. Am Ende habe ich dann aus dem gespeicherten HEX die orginal SMS wiederhergestellt.
Das hatte ich befürchtet - die reine Pythonlösung wird aber recht hässlich:

Code: Alles auswählen

def cycle_with_offset(iterable, offset=0):
    """itertools.cycle faengt immer vorne an"""
    cycler = it.cycle(iterable)
    if offset:
        for dummy in it.izip(xrange(offset), cycler):
            pass # fast forward
    return cycler

def recode_bytes(bytes, prev=0, offset=0):
    OUT_BITS = 7
    OUT_MASK = (1 << OUT_BITS) - 1
    res = []
    for i, value in it.izip(cycle_with_offset(xrange(OUT_BITS + 1), offset),
                        it.imap(ord,bytes)):
        current_bits = OUT_MASK >> (i+1)
        res.append(chr( ((value & current_bits) << i) | prev))
        prev = ( value & ~ current_bits) >> (OUT_BITS - i)
    return "".join(res), prev, i
hth, Jörg
BlackJack

Mein Versuch:

Code: Alles auswählen

def decode(bytes):
    carry = 0
    for i, byte in enumerate(bytes):
        shift = i % 7
        out, carry = ((byte << shift) | carry) & 0x7f, byte >> (7 - shift)
        yield out
        if shift == 6:
            yield carry
            carry = 0
#cousin#
User
Beiträge: 25
Registriert: Mittwoch 18. März 2009, 22:56
Wohnort: Mannheim
Kontaktdaten:

Hi Zusammen,

ich habe das Ganze nun mal versucht in Python umzusetzen und es funktioniert auch, ab dem Punkt bei dem ich die Binärwerte habe. Allerdings ist in dem Teil vorher, indem ich von HEX nach Binär 8Bit umwandle vermutlich ein Fehler.

Könnt ihr euch das mal anschauen?


kompletter code:

Code: Alles auswählen

# decodes the PDU Message to a readable message
def pduSMS(smsMessage, path):
    pdu_fobj = open(path + "/cuttedPieces_ASCII/pdu.ascii", "a")

    # String to HEX
    smsMessage = smsMessage.encode("hex")
    
    # HEX -> BIN 8-bit
    bin8sms = encodeToBin.b2b(16, 2, smsMessage)
    
    # split BIN string in parts with length of 8 digits
    part = ""
    i = 0
    k = 0
    while k < len(bin8sms):
        k = i + 8
        for j in range(i, i+8):
            if k < len(bin8sms):
                part = part + bin8sms[j]
            else:
                rest = (len(bin8sms) % 8)
                for m in range(i, i + rest):
                    part = part + bin8sms[m]
                break
        part = part + ";"
        i = i+8
    
    # BIN 8-bit -> BIN 7-bit
    l = part.split(";")
    for i in range(len(l) - 1):
        l[i+1] += l[i][:i+1]
        l[i] = l[i][i+1:]
    bin7sms = str(l)

    # BIN 7-bit -> DEC
    i = 0
    decsms = ""
    while i < len(l):
        if i <= len(l):
            decsms = decsms + str(int(l[i], 2)) + ";"
            i += 1
        else:
            break

    # DEC -> ASCII
    asciisms = ""
    n = decsms.split(";")
    for p in range(len(n) - 2):
        char = int(n[p])
        if char < 256:
            asciisms = asciisms + chr(char) + ";"
    pdu_fobj.write("ascii: " + asciisms + "\n")
    pdu_fobj.close()

encodeToBin.py

Code: Alles auswählen

import string

# converts n in base r1 to base r2
def b2b(r1,r2,n):             #Convert n in base r1 to base r2 and back
    x = base(r1,r2,n)
    return x

#This is the crown jewel of the routine where we use base 10 as a pivot to go from base r1 to r2.
def base(r1,r2,num):       
    digits=""
    for j in range(0,10):
        digits = digits + chr(j+48)
    for j in range(10,36):
        digits = digits + chr(j+55)
    for j in range(36,52):
        digits = digits + chr(j-4)
    for j in range(52,59):
        digits = digits + chr(j+6)
    for j in range(59,224):
        digits = digits + chr(j+32)
    for j in range(224,256):
        digits = digits + chr(j-224)
    num1 = str(num)
    ln  = len(num1)
    dec = 0
    for j in range(ln):
        # Get the ascii code of num
        asci = string.index(digits,num1[j]) 
        temp   = r1**(ln-j-1)
        # Convert the num in r1 to decimal
        dec += asci*temp
    # Init the storage string
    RDX = ""                    
    j=-1
    dec2=dec
    # get number of digits in decimal
    while dec2:                 
        dec2 = dec2 / r2
        j = j+1
    while j >= 0:
        pwr = r2**j
        Q   = dec // pwr
        dec = dec % pwr
        RDX = RDX + digits[Q]
        j-=1
    return RDX
    
#
# modified from http://mail.python.org/pipermail/tutor/2003-August/024506.html
BlackJack

@#cousin#: OMG das wird ja immer gruseliger. Hör bitte mit diesen Zeichenkettenumwandlungen auf. Das ist ja nicht mit anzusehen.

Du brauchst eine Umwandlung von Zeichen zu Bytewert (`ord()`), dann eine Funktion, die auf diesen Werten die entsprechenden Bitoperationen ausführt und aus den 8-Bit-Werten die 7-Bit-Werte macht, und wieder eine Umwandlung nach Zeichen (`chr()`), das war's. Letzteres wahrscheinlich noch über eine Umsetzungstabelle, weil SMS soweit ich weiss eine relativ unübliche Kodierung verwendet.

Alleine wenn ich schon sehe wie Du die Darstellung zur Basis 2 in 8er-Blöcke aufteilst. Das geht mit einer einzigen "list comprehension" wesentlich einfacher. Aber wie gesagt, diese Umwege über verschiedene Zahlendarstellungen als Zeichenketten sind genau dass: *Umwege*!
#cousin#
User
Beiträge: 25
Registriert: Mittwoch 18. März 2009, 22:56
Wohnort: Mannheim
Kontaktdaten:

Hi BlackJack,

ich geb dir vollkommen recht, dass der Code gruselig ist. Aber ich wusste mir nicht anders zu helfen. Der schlimmste Teil ist das Skript, welches is mir aus dem Netz gezogen habe um aus HEX die Binärwerte zu machen. Und ich glaube da liegt auch mein Fehler, warum das Ergebnis nicht stimmt.


zu deinen Anmerkungen:
-die Bitoperationen macht mein Skript dank HerrHagen ja und das sieht auch gut aus, bis auf den Rest den er immer noch als letztes Element speichert, aber den kann man getrost vergessen

-chr() verwende ich ja auch schon am Ende und soweit ich das verstanden habe, kann ich sogar den "normalen" chr nehmen, da die geheimnisvolle Kodierung durch das shiften in den Bits geschieht.

-ord() hier weiss ich leider nichts mit anzufangen....die Funktion liefert den int zu einem HEX-Wert, aber dann habe ich immernoch keine Binärdarstellung. Ich habe nun mal den Ansatz versucht, aber das Ergebnis passt immer noch nicht.

Code: Alles auswählen

import string, binascii, re

# converts a denary integer n into a binary string bStr
def Denary2Binary(n):
    bStr = ''
    if n < 0: raise ValueError, "must be a positive integer"
    if n == 0: return '0'
    while n > 0:
        bStr = str(n % 2) + bStr
        n = n >> 1
    return bStr


# decodes the PDU Message to a readable message
def pduSMS(smsMessage, messageLenght, path):
    pdu_fobj = open(path + "/cuttedPieces_ASCII/pdu.ascii", "a")
    pdu_fobj.write(smsMessage + "\n")
    
    # String to HEX
    smsMessage = smsMessage.decode("hex")
    
    # HEX -> BIN 8-bit
    bin8sms = ""
    for part in smsMessage:
        ordpart = ord(part)
        bin = Denary2Binary(ordpart)
        bin8sms = bin8sms + bin
    
    # split BIN string in parts with length of 8 digits
    part = ""
    i = 0
    k = 0
    while k < len(bin8sms):
        k = i + 8
        for j in range(i, i+8):
            if k < len(bin8sms):
                part = part + bin8sms[j]
            else:
                rest = (len(bin8sms) % 8)
                for m in range(i, i + rest):
                    part = part + bin8sms[m]
                break
        part = part + ";"
        i = i+8
    
    # BIN 8-bit -> BIN 7-bit
    l = part.split(";")
    for i in range(len(l) - 1):
        l[i+1] += l[i][:i+1]
        l[i] = l[i][i+1:]
    bin7sms = str(l)

    # BIN 7-bit -> DEC
    i = 0
    decsms = ""
    while i < len(l):
        if i <= len(l) and l[i] != "":
            decsms = decsms + str(int(l[i], 2)) + ";"
            i += 1
        else:
            break

    # DEC -> ASCII
    asciisms = ""
    n = decsms.split(";")
    for p in range(len(n) - 2):
        char = int(n[p])
        if char < 256:
            asciisms = asciisms + chr(char) + ";"
    pdu_fobj.write("ascii: " + asciisms + "\n") 
    pdu_fobj.close()
BlackJack

@#cousin#: Vergiss am besten die Funktion aus dem Netz. Mit der kann man nicht direkt Bytes/Zeichen in Binärdarstellung überführen.

Deine `Denary2Binary()` hat den falschen Namen und die falsche Beschreibung im Kommentar. Die wandelt *Integer* in eine Binärdarstellung als Zeichenkette um. Integer haben keine Basis, das sind einfach ganz abstrakt ganze Zahlen in irgendeiner internen Darstellung. Normalerweise zur Basis zwei, weil Rechner so gestrickt sind, aber das braucht den Programmierer nicht weiter zu interessieren.

Die Kommantare, die Du über die Funktionen setzt, machen sich übrigens ganz gut als Docstrings.

Bitoperationen benutzt Du gerade nicht, Du wandelst die Zahlen in eine Zeichenkette mit einer Darstellung zur Basis 2 um und machst dann Zeichenkettenoperationen darauf. Bitoperationen sind Bitverschiebungen, bitweises Und und Oder auf *Zahlen*, nicht auf Zeichenketten.

Womit wir beim `ord()` wären, was den Zahlwert zu einem Zeichen, also zu einem Byte liefert. Genau mit diesem Zahlwert und den Bitoperationen würde man das Problem direkt lösen können, ohne die unschönen Umwege. Meine `decode()`-Funktion nimmt Zahlen zwischen 0 und 255 und liefert einen Generator über Zahlen im 7-Bit-Bereich, also die dekodierten Daten. Diese 9 Zeilen sind alles was man braucht um von 8-Bit-Werten auf die 7-Bit-Werte zu kommen.
#cousin#
User
Beiträge: 25
Registriert: Mittwoch 18. März 2009, 22:56
Wohnort: Mannheim
Kontaktdaten:

Hi,

ich befürchte, dass ich dir gerade (bzw. die ganze Zeit schon) nicht so ganz folgen kann :lol:

wenn ich dich richtig verstanden habe arbeite ich am Anfang wie bisher:
- string in hex decodieren
- ord() anwenden

-danach nehme ich deine decode() von weiter oben

-und auf das Ergebnis wende ich chr() an

stimmt das so?
BlackJack

Nicht ganz, die Hex-Kodierung ist immer noch ein Schritt zuviel.

Code: Alles auswählen

def decode(encoded):
    carry = 0
    for i, byte in enumerate(encoded):
        shift = i % 7
        yield ((byte << shift) | carry) & 0x7f
        carry = byte >> (7 - shift)
        if shift == 6:
            yield carry
            carry = 0


def main():
    encoded = '\xe82\x9b\xfdF\x97\xd9\xec7'
    decoded = ''.join(map(chr, decode(map(ord, encoded))))
    assert len(decoded) == len(encoded) + len(encoded) // 7
    print decoded
#cousin#
User
Beiträge: 25
Registriert: Mittwoch 18. März 2009, 22:56
Wohnort: Mannheim
Kontaktdaten:

Hi BlackJack,

vielen Dank für die Unterstützung bei meinem Problem, allerdings bin ich noch nicht ganz dort wo ich hin möchte. :(

mein Skript sieht aktuell folgendermaßen aus:

Code: Alles auswählen

#!/usr/bin/python
#

import string

# decodes the PDU Message to a readable message
def pduSMS(smsMessage, messageLenght, path):
    pdu_fobj = open(path + "/cuttedPieces_ASCII/pdu.ascii", "a")
    pdu_fobj.write(smsMessage + "\n")
    output = ""
    output2 = ""
    sub = "\\x"
    for char in range(0, len(smsMessage), 2):
        output = sub + smsMessage[char] + smsMessage[char + 1]
        output2 = output2 + output
    asciisms = main(output2)
    pdu_fobj.write("ascii: " + asciisms + "\n") 
    pdu_fobj.close()

    
def decode(encoded):
    carry = 0
    for i, byte in enumerate(encoded):
        shift = i % 7
        yield ((byte << shift) | carry) & 0x7f
        carry = byte >> (7 - shift)
        if shift == 6:
            yield carry
            carry = 0


def main(encoded):
    decoded = ''.join(map(chr, decode(map(ord, encoded))))
    assert len(decoded) == len(encoded) + len(encoded) // 7
    print decoded
    return decoded

die decode() und die main() sind von dir, da habe ich nur den Beispiel-String entfernt.

die pduSMS() sollte nichts anderes machen als den ursprünglichen String in eine Datei zu schreiben und danach aus:

Code: Alles auswählen

E8329BFD4697D9EC37
den folgenden String mache:

Code: Alles auswählen

\xE8\x32\x9B\xFD\x46\x97\xD9\xEC\x37
damit deine main() was damit anfangen kann.

leider wird aus "E8329BFD4697D9EC37" nicht "hellohello" sondern ein String aus Steuerzeichen und nicht druckbaren Zeichen.

hast du noch eine Idee woran das liegen könnte?
BlackJack

Du musst Dir mal klar werden was für Daten im Speicher stehen und wie die Dargestellt werden, wenn Du sie als literale in den Quelltext schreibst, bzw. mit `repr()` ausgibst. '\\xE8' ist etwas *völlig* anderes als '\xE8'. Das erste sind *vier* Zeichen die ausgegeben \xE8 ergeben und das zweite ist *ein* Zeichen, das ausgegeben je nach Kodierung der Konsole ein "komisches" Zeichen oder ein Kästchen oder "Fragezeichen" ergibt, und den Bytewert 0xE8 (hexadezimal) oder 232 (dezimal) hat.

Wenn Du aus der Zeichenkette 'E8329BFD4697D9EC37' neun Bytes machen willst, dann überleg doch mal wie Du vorher aus einer Zeichenkette mit binärdaten so eine Hex-Darstellung gemacht hast. Mit `encode('hex')` -- und nun rate mal wie die andere Richtung geht.

Wobei ich mich frage warum Du vorher `encode()` verwendet hast, wenn Du doch anscheinen jetzt bei gleichen Daten(?) die Gegenrichtung brauchst!?
#cousin#
User
Beiträge: 25
Registriert: Mittwoch 18. März 2009, 22:56
Wohnort: Mannheim
Kontaktdaten:

Hi BlackJack,

also war mein decode(hex) aus dem obigen Beitrag doch richtig!?

Nun geht es auch fast so wie es soll, er setzt nur die Umlaute nicht richtig um, aber das bekomm ich per Regulärem-Ausdruck noch hin.

Vielen Dank für deine Unterstützung!
Antworten