Reverse a string in Python two characters at a time (Network byte order)

Question:

Say you have this string:

ABCDEFGH

And you want to reverse it so that it becomes:

GHEFCDAB

What would be the most efficient / pythonic solution? I’ve tried a few different things but they all look horrible…

Thanks in advance!

Update:

In case anyone’s interested, this wasn’t for homework. I had a script that was processing data from a network capture and returning it as a string of hex bytes. The problem was the data was still in network order. Due to the way the app was written, I didn’t want to go back through and try to use say socket.htons, I just wanted to reverse the string.

Unfortunately my attempts seemed so hideous, I knew there must be a better way (a more pythonic solution) – hence my question here.

Asked By: PeterM

||

Answers:

Here is a general form. The size of the grouping can easily be changed to a different number of characters at a time. The string length should be an exact multiple of the grouping size

>>> "".join(map("".join, reversed(zip(*[iter("ABCDEFGH")]*2))))
'GHEFCDAB'

(this is Python 2, it won’t work in 3)

Answered By: John La Rooy

A concise way to do this is:

"".join(reversed([a[i:i+2] for i in range(0, len(a), 2)]))

This works by first breaking the string into pairs:

>>> [a[i:i+2] for i in range(0, len(a), 2)]
['AB', 'CD', 'EF', 'GH']

then reversing that, and finally concatenating the result back together.

Answered By: Greg Hewgill

Lots of fun ways to do this

>>> s="ABCDEFGH"
>>> "".join(map(str.__add__, s[-2::-2] ,s[-1::-2]))
'GHEFCDAB'
Answered By: John La Rooy

just a shot

st = "ABCDEFGH"
s = [st[2*n:2*n+1] for n in range(len(st)/2)]
return s[::-1].join('')

this assumes that len(st) is even, otherwise change that to range(len(st)/2+1) and I’m even sure there’s a better way to do that split into twos.

If your python complains about s[::-1] you can use reversed(s)

Answered By: Lacrymology

You can use this, but don’t tell anyone I wrote this code 🙂

import struct

def pair_reverse(s):
    n = len(s) / 2
    fmt = '%dh' % n
    return struct.pack(fmt, *reversed(struct.unpack(fmt, s)))

pair_reverse('ABCDEFGH')
Answered By: Alok Singhal

And yet another way:

a = "ABCDEFGH"
new = ""

for x in range(-1, -len(a), -2):
    new += a[x-1] + a[x]

print new
Answered By: Trufa
st = "ABCDEFGH"
"".join([st[x:x+2] for x in range(0,len(st),2)][::-1])

EDIT: Curses, apparently 27 minutes slower than another poster. But I like the reverse slice notation better.

Some more information on the reverse slice here: "".join(reversed(val)) vs val[::-1]…which is pythonic?

Answered By: elliot42

If anybody is interested, this is the timing for all* the answers.

EDIT (had got it wrong the first time):

import timeit
import struct

string = "ABCDEFGH"

# Expected resutlt => GHEFCDAB

def rev(a):
    new = ""

    for x in range(-1, -len(a), -2):
        new += a[x-1] + a[x]

    return new

def rev2(a):
    return "".join(reversed([a[i:i+2] for i in range(0, len(a), 2)]))

def rev3(a):
    return "".join(map(str.__add__, a[-2::-2] ,a[-1::-2]))

def rev4(a):
    return "".join(map("".join, reversed(zip(*[iter(a)]*2))))


def rev5(a):
    n = len(a) / 2
    fmt = '%dh' % n
    return struct.pack(fmt, *reversed(struct.unpack(fmt, a)))

def rev6(a):
    return "".join([a[x:x+2] for x in range(0,len(a),2)][::-1])


print "Greg Hewgill %f" %timeit.Timer("rev2(string)", "from __main__ import rev2, string").timeit(100000)
print "gnibbler %f" %timeit.Timer("rev3(string)", "from __main__ import rev3, string").timeit(100000)
print "gnibbler second %f" %timeit.Timer("rev4(string)", "from __main__ import rev4, string").timeit(100000)
print "Alok %f" %timeit.Timer("rev5(string)", "from __main__ import rev5, struct, string").timeit(100000)
print "elliot42 %f" %timeit.Timer("rev6(string)", "from __main__ import rev6, struct, string").timeit(100000)
print "me %f" %timeit.Timer("rev(string)", "from __main__ import rev, string").timeit(100000)

results for string = "ABCDEFGH":

Greg Hewgill 0.853000
gnibbler 0.428000
gnibbler second 0.707000
Alok 0.763000
elliot42 0.237000
me 0.200000

results for string = "ABCDEFGH"*5:

Greg Hewgill 2.246000
gnibbler 0.811000
gnibbler second 1.205000
Alok 0.972000
elliot42 0.594000
me 0.584000

results for string = "ABCDEFGH"*10:

Greg Hewgill 2.058000
gnibbler 1.178000
gnibbler second 1.926000
Alok 1.210000
elliot42 0.935000
me 1.082000

results for string = "ABCDEFGH"*100:

Greg Hewgill 9.762000
gnibbler 9.134000
gnibbler second 14.782000
Alok 5.775000
elliot42 7.351000
me 18.140000

*Sorry @Lacrymology could not make your’s work!

Answered By: Trufa

This looks like homework. So here’s a very unorthodox approach that you might find interesting:

>>> s = "ABCDEFGH"
>>> ''.join([s[::2][::-1][i]+s[::-2][i] for i in range(len(s[::2]))])
'GHEFCDAB'

Good Luck!

Answered By: inspectorG4dget
>>> import array
>>> s="abcdef"
>>> a=array.array('H',s)
>>> a.byteswap()
>>> a.tostring()
'badcfe'

Finish up by using a.reverse() instead of a.byteswap() if you wanted to swap element order rather than byte order.

I took the liberty of editing Trufa’s benchmark script a bit. The modified script generated a graphical plot showing approximately linear scaling for all functions.

Answered By: Yann Vernier

My friend Rob pointed out a beautiful recursive solution:

def f(s):
    return "" if not s else f(s[2:]) + s[:2]
Answered By: elliot42

and another gets on…

>>> rev = "ABCDEFGH"[::-1]
>>> ''.join([''.join(el) for el in zip(rev[1::2], rev[0::2])])
'GHEFCDAB'
Answered By: dansalmo

I like this solution the most as it’s the simplest and the neatest:

import struct
hex = struct.pack('<I', 0x41424344) #ABCD
print(hex) # BCDA
Answered By: AK_

Here is a function based on the best, fastest, and most Pythonic answer above and in current Python 3 syntax:

def reverse_hex(hex_string):
    if isinstance(hex_string, str):
        input_is_string = True
        hex_string = hex_string.encode()
    a = array.array('H', hex_string)
    a.reverse()
    output = a.tobytes()
    if input_is_string:
        return output.decode()
    else:
        return output
Answered By: Utkonos
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.