Инвертировать строку в Python по два символа за раз (сетевой порядок байтов) - PullRequest
34 голосов
/ 03 мая 2011

Скажем, у вас есть эта строка:

ABCDEFGH

И вы хотите изменить ее так, чтобы она стала:

GHEFCDAB

Какое было бы наиболее эффективное / питонное решение?Я пробовал несколько разных вещей, но все они выглядят ужасно ...

Заранее спасибо!

Обновление :

На случай, если кому-то интересноэто не для домашней работы.У меня был скрипт, который обрабатывал данные из сетевого захвата и возвращал их в виде строки шестнадцатеричных байтов.Проблема была в том, что данные все еще были в сетевом порядке.Из-за того, как было написано приложение, я не хотел возвращаться и пытаться использовать, скажем, socket.htons, я просто хотел перевернуть строку.

К сожалению, мои попытки казались такими отвратительными, что я знал, что должен быть лучший способ (более питонное решение) - отсюда и мой вопрос.

Ответы [ 14 ]

31 голосов
/ 03 мая 2011

Краткий способ сделать это:

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

Это работает, сначала разбивая строку на пары:

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

, затем переворачивая это, и, наконец, объединяя результат обратно вместе.

14 голосов
/ 03 мая 2011

Множество забавных способов сделать это

>>> s="ABCDEFGH"
>>> "".join(map(str.__add__, s[-2::-2] ,s[-1::-2]))
'GHEFCDAB'
11 голосов
/ 03 мая 2011

Если кому-то интересно, это время для всех * ответов.

РЕДАКТИРОВАТЬ (ошибся с первого раза):

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)

результаты для string = "ABCDEFGH":

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

результаты для string = "ABCDEFGH"*5:

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

результаты для string = "ABCDEFGH"*10:

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

результаты для string = "ABCDEFGH"*100:

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

* Извините, @Lacrymology не смог сделать вашу работу!

7 голосов
/ 03 мая 2011
>>> import array
>>> s="abcdef"
>>> a=array.array('H',s)
>>> a.byteswap()
>>> a.tostring()
'badcfe'

Завершите, используя a.reverse () вместо a.byteswap (), если вы хотите поменять порядок элементов вместо порядка байтов.

Я позволил себе немного отредактировать эталонный скрипт Trufa. Модифицированный скрипт сгенерировал графический график , показывающий приблизительно линейное масштабирование для всех функций.

4 голосов
/ 03 мая 2011

Вот общий вид. Размер группировки может быть легко изменен на другое количество символов за раз. Длина строки должна быть кратна размеру группировки

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

(это Python 2, он не будет работать в 3)

3 голосов
/ 03 мая 2011
st = "ABCDEFGH"
"".join([st[x:x+2] for x in range(0,len(st),2)][::-1])

РЕДАКТИРОВАТЬ: Проклятия, очевидно, на 27 минут медленнее, чем другой плакат.Но мне больше нравится нотация обратного среза.

Здесь приведена дополнительная информация об обратном срезе: "". Join (обратный (val)) против val [:: - 1] ... которыйвещий?

3 голосов
/ 03 мая 2011

Вы можете использовать это, но не говорите никому, что я написал этот код: -)

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')
1 голос
/ 04 мая 2011

Мой друг Роб указал на красивое рекурсивное решение:

def f(s):
    return "" if not s else f(s[2:]) + s[:2]
0 голосов
/ 07 июля 2019

Вот функция, основанная на лучшем, самом быстром и наиболее питонском ответе выше и в текущем синтаксисе Python 3:

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
0 голосов
/ 28 января 2019

Мне больше всего нравится это решение, так как оно самое простое и изящное:

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