开发者

How to swap adjacent bytes in a string of hex bytes (with or without regular expressions)

开发者 https://www.devze.com 2022-12-16 16:11 出处:网络
Gi开发者_运维知识库ven the string (or any length string with an even-number of word pairs): \"12345678\"

Gi开发者_运维知识库ven the string (or any length string with an even-number of word pairs): "12345678"

How would I swap adjacent "words"?

The result I want is "34127856"

As well as, when that's done I need to swap the longs. The result I want is: "78563412"


A regex approach:

import re
twopairs = re.compile(r'(..)(..)')
stringwithswappedwords = twopairs.sub(r'\2\1', basestring)
twoquads = re.compile(r'(....)(....)')
stringwithswappedlongs = twoquads.sub(r'\2\1', stringwithswappedwords)

Edit: However, this is definitely not the fastest approach in Python -- here's how one finds out about such things: first, write all "competing" approaches into a module, here I'm calling it 'swa.py'...:

import re

twopairs = re.compile(r'(..)(..)')
twoquads = re.compile(r'(....)(....)')

def withre(basestring, twopairs=twopairs, twoquads=twoquads):
  stringwithswappedwords = twopairs.sub(r'\2\1', basestring)
  return twoquads.sub(r'\2\1', stringwithswappedwords)

def withoutre(basestring):
  asalist = list(basestring)
  asalist.reverse()
  for i in range(0, len(asalist), 2):
    asalist[i+1], asalist[i] = asalist[i], asalist[i+1]
  return ''.join(asalist)

s = '12345678'
print withre(s)
print withoutre(s)

Note that I set s and try out the two approaches for a fast sanity check that they're actually computing the same result -- good practice, in general, for this kind of "head to head performance races"!

Then, at the shell prompt, you use timeit, as follows:

$ python -mtimeit -s'import swa' 'swa.withre(swa.s)'
78563412
78563412
10000 loops, best of 3: 42.2 usec per loop
$ python -mtimeit -s'import swa' 'swa.withoutre(swa.s)'
78563412
78563412
100000 loops, best of 3: 9.84 usec per loop

...and you find that in this case the RE-less approach is about 4 times faster, a worthwhile optimization. Once you have such a "measurement harness" in place, it's also easy to experiment with further alternative and tweaks for further optimization, if there is any need for "really blazing speed" in this operation, of course.

Edit: for example, here's an even faster approach (add to the same swa.py, with a final line of print faster(s) of course;-):

def faster(basestring):
  asal = [basestring[i:i+2]
          for i in range(0, len(basestring), 2)]
  asal.reverse()
  return ''.join(asal)

This gives:

$ python -mtimeit -s'import swa' 'swa.faster(swa.s)'
78563412
78563412
78563412
100000 loops, best of 3: 5.58 usec per loop

About 5.6 microseconds, down from about 9.8 for the simplest RE-less approach, is another possibly-worthwhile micro-optimization.

And so on, of course -- there's an old folk (pseudo)theorem that says that any program can be made at least one byte shorter and at least one nanosecond faster...;-)

Edit: and to "prove" the pseudotheorem, here's a completely different approach (replace the end of swa.py)...:

import array
def witharray(basestring):
  a2 = array.array('H', basestring)
  a2.reverse()
  return a2.tostring()

s = '12345678'
# print withre(s)
# print withoutre(s)
print faster(s)
print witharray(s)

This gives:

$ python -mtimeit -s'import swa' 'swa.witharray(swa.s)'
78563412
78563412
100000 loops, best of 3: 3.01 usec per loop

for a further possible-worthy speedup.


import re
re.sub(r'(..)(..)', r'\2\1', '12345678')
re.sub(r'(....)(....)', r'\2\1', '34127856')


just for the string "12345678"

from textwrap import wrap
s="12345678"
t=wrap(s,len(s)/2)
a,b=wrap(t[0],len(t[0])/2)
c,d=wrap(t[1],len(t[1])/2)
a,b=b,a
c,d=d,c
print a+b+c+d

you can make it to a generic function to do variable length string.

output

$ ./python.py
34127856


>>> import re
>>> re.sub("(..)(..)","\\2\\1","12345678")
'34127856'
>>> re.sub("(....)(....)","\\2\\1","34127856")
'78563412'


If you want to do endianness conversion, use Python's struct module on the original binary data.

If that is not your goal, here's a simple sample code to rearrange one 8 character string:

def wordpairswapper(s):
    return s[6:8] + s[4:6] + s[2:4] + s[0:2]


I'm using the following approach:

data = "deadbeef"
if len(data) == 4: #2 bytes, 4 characters
   value = socket.ntohs(int(data, 16))
elif len(data) >= 8:
   value = socket.ntohl(int(data, 16))
else:
   value = int(data, 16)

works for me!

0

精彩评论

暂无评论...
验证码 换一张
取 消