2

I am working with Ruby every day, but i have an issue in Python. I was found this languages very similar... But I have some problems with migration from Ruby :)

Please, help me to convert this action in python:

   string = "qwerty2012"
   (var, some_var, another_var)  = string.unpack("a1a4a*")

this should return three variables with unpacked values from string:

   var         = "q"      # a1
   some_var    = "wert"   # a4
   another_var = "y2012"  # a*

Help me to represent it in Python Thank you!

gabeio
  • 1,282
  • 1
  • 23
  • 37
Roman
  • 143
  • 1
  • 8
  • 3
    You probably don't want to name a variable `string` – Levon Jun 18 '12 at 23:26
  • 1
    Actually, `string` is fine. The type in Python is `str`. – Chinmay Kanchi Jun 18 '12 at 23:28
  • 2
    @ChinmayKanchi, yeah, but there's also the string module, and we don't know if OP is importing it, so assigning `string` to something new would take it out of the namespace. Kind of bad practice. – imm Jun 18 '12 at 23:29
  • 1
    @Chinmay But there's still the `string` package - but yes it's probably not especially problematic. – Voo Jun 18 '12 at 23:29
  • For some reason, I thought the module was called `strings`. Guess that says something about how often I find myself using it. Yeah, not a great idea to name something the same as a standard module. – Chinmay Kanchi Jun 18 '12 at 23:32
  • struct module does unpacking but doesn't support arbitrary length structures, slicing is best. – jan zegan Jun 19 '12 at 00:00

5 Answers5

9
s = "qwerty2012"
(a, b, c) = s[:1], s[1:5], s[5:]
mVChr
  • 49,587
  • 11
  • 107
  • 104
5

Python does have a similar module named struct. It lacks the ability to grab the rest of the string in the same way that Ruby and PHP lifted from Perl. You can almost get there though:

>>> import struct
>>> s = 'qwerty2012'
>>> struct.unpack_from('1s4s', s)
('q', 'wert')
>>> def my_unpack(format, packed_string):
...    result = []
...    result.extend(struct.unpack_from(format, packed_string))
...    chars_gobbled = struct.calcsize(format)
...    rest = packed_string[chars_gobbled:]
...    if rest:
...        result.append(rest)
...    return result
...
>>> my_unpack('1s4s', 'qwerty2012')
['q', 'wert', 'y2012']
>>> my_unpack('1s4s', 'qwert')
['q', 'wert']
>>> [hex(x) for x in my_unpack('<I', '\xDE\xAD\xBE\xEF')]
['0xefbeadde']

I wish that the struct module implemented the rest of Perl's unpack and pack since they were incredibly useful functions for ripping apart binary packets but alas.

D.Shawley
  • 58,213
  • 10
  • 98
  • 113
4
s = "qwerty2012"
var, some_var, another_var = s[:1], s[1:5], s[5:]

will do the assignment and yield respectively:

q
wert
y2012

The above assignment makes use of slice notation as described by the Python Docs. This SO post Good Primer for Python Slice Notation gives a good explanation too.

Community
  • 1
  • 1
Levon
  • 138,105
  • 33
  • 200
  • 191
2

Here's a preliminary recreation of unpack:

import re
import StringIO

def unpack(s, fmt):
    fs = StringIO.StringIO(s)
    res = []
    for do,num in unpack.pattern.findall(fmt):
        if num == '*':
            num = len(s)
        elif num == '':
            num = 1
        else:
            num = int(num)
        this = unpack.types[do](num, fs)
        if this is not None:
            res.append(this)
    return res

unpack.types = {
    '@': lambda n,s: s.seek(n),             # skip to offset
    'a': lambda n,s: s.read(n),             # string
    'A': lambda n,s: s.read(n).rstrip(),    # string, right-trimmed
    'b': lambda n,s: bin(reduce(lambda x,y:256*x+ord(y), s.read(n), 0))[2:].zfill(8*n)[::-1],   # binary, LSB first
    'B': lambda n,s: bin(reduce(lambda x,y:256*x+ord(y), s.read(n), 0))[2:].zfill(8*n)          # binary, MSB first
}
unpack.pattern = re.compile(r'([a-zA-Z@](?:_|!|<|>|!<|!>|0|))(\d+|\*|)')

It works for your given example,

unpack("qwerty2012", "a1a4a*")  # -> ['q', 'wert', 'y2012']

but has a long list of datatypes not yet implemented (see the documentation).

Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99
1

This might ease your migration from Ruby:

import re
import struct

def unpack(format, a_string):
    pattern = r'''a(\*|\d+)\s*'''
    widths = [int(w) if w is not '*' else 0 for w in re.findall(pattern, format)]
    if not widths[-1]: widths[-1] = len(a_string) - sum(widths)
    fmt = ''.join('%ds' % f for f in widths)
    return struct.unpack_from(fmt, a_string)

(var, some_var, another_var) = unpack('a1a4a*', 'qwerty2012')  # also 'a1 a4 a*' OK
print (var, some_var, another_var)

Output:

('q', 'wert', 'y2012')
martineau
  • 119,623
  • 25
  • 170
  • 301