Подтвердить что ты не робот

Обратное преобразование строки в Python по два символа за раз (сетевой порядок байтов)

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

ABCDEFGH

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

GHEFCDAB

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

Спасибо заранее!

Обновление

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

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

4b9b3361

Ответ 1

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

"".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']

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

Ответ 2

Много интересных способов сделать это

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

Ответ 3

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

ИЗМЕНИТЬ (в первый раз он ошибался):

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 не смог сделать вашу работу!

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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')

Ответ 7

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

ИЗМЕНИТЬ: Проклятия, по-видимому, на 27 минут медленнее, чем у другого плаката. Но мне нравится реверсивное обозначение среза.

Ниже приведена дополнительная информация об обратном фрагменте: .join(reverseed (val)) vs val [:: - 1]... который является pythonic?

Ответ 8

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

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

Ответ 9

просто выстрел

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

это предполагает, что len (st) является четным, иначе измените его на диапазон (len (st)/2 + 1), и я даже уверен, что есть лучший способ сделать это разбиение на два.

Если ваш питон жалуется на s [:: - 1], вы можете использовать обратные (s)

Ответ 10

И еще один способ:

a = "ABCDEFGH"
new = ""

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

print new

Ответ 11

Это выглядит как домашнее задание. Итак, здесь очень неортодоксальный подход, который может показаться вам интересным:

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

Удачи!

Ответ 12

а другой -...

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

Ответ 13

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

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

Ответ 14

Вот функция, основанная на лучшем, самом быстром и наиболее питонском ответе выше и в текущем синтаксисе 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