【问题标题】:How to swap adjacent bytes in a string of hex bytes (with or without regular expressions)如何在一串十六进制字节中交换相邻字节(有或没有正则表达式)
【发布时间】:2025-12-06 06:55:02
【问题描述】:

给定字符串(或任何长度为偶数对的字符串): "12345678"

如何交换相邻的“单词”?

我想要的结果是 "34127856"

此外,完成后我需要交换多头。 我想要的结果是: "78563412"

【问题讨论】:

  • 如果字符串是 "123456789012" ,输出应该是什么?

标签: python regex string hex


【解决方案1】:

我正在使用以下方法:

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)

为我工作!

【讨论】:

    【解决方案2】:

    正则表达式方法:

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

    编辑: 然而,这绝对不是 Python 中最快的方法——以下是人们发现这些事情的方法:首先,将所有“竞争”方法写入一个模块,这里我称之为'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)
    

    请注意,我设置了s 并尝试了这两种方法来快速检查它们实际上计算的是相同的结果——一般来说,对于这种“面对面的性能竞赛”来说,这是一种很好的做法!

    然后,在 shell 提示符下,使用timeit,如下所示:

    $ 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
    

    ...您发现在这种情况下,无 RE 的方法快了大约 4 倍,这是值得优化的。一旦有了这样的“测量工具”,当然,如果在此操作中需要“真正超快的速度”,也很容易尝试进一步的替代方案和调整以进一步优化。

    编辑:例如,这是一种更快的方法(添加到相同的swa.py,当然最后一行是print faster(s);-):

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

    这给出了:

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

    大约 5.6 微秒,低于最简单的无 RE 方法的大约 9.8 微秒,是另一个可能值得的微优化。

    当然,等等——有一个古老的(伪)定理说,任何程序都可以缩短至少一个字节,至少快一纳秒......;-)

    编辑:为了“证明”伪定理,这里有一个完全不同的方法(替换 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)
    

    这给出了:

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

    为了进一步可能值得加速。

    【讨论】:

    • 干得好,亚历克斯。一旦程序员打破了正则表达式的镜像大厅,他们就可以做出伟大的事情......就我个人而言,我希望我的所有软件都比我的竞争对手快 7 倍以上!
    • @cudamaru,最新版本比我用 RE 得到的快 14 倍,比我第一次没有它们时快 3 倍多。被 RE 的便利性催眠到不考虑替代方案的地步肯定是有害的。但是,不要忽视(在其他情况下)速度优势可能正好相反的可能性——当速度真的很重要时,请始终考虑并尝试各种合理的可能性!-)
    • 那里的出色工作。为我的反 RE 运动提供更多弹药;-)
    【解决方案3】:

    只针对字符串“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
    

    你可以把它变成一个泛型函数来做变长字符串。

    输出

    $ ./python.py
    34127856
    

    【讨论】:

      【解决方案4】:

      如果您想进行字节序转换,请在原始二进制数据上使用 Python 的 struct module

      如果这不是您的目标,这里有一个简单的示例代码来重新排列一个 8 个字符的字符串:

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

      【讨论】:

        【解决方案5】:
        >>> import re
        >>> re.sub("(..)(..)","\\2\\1","12345678")
        '34127856'
        >>> re.sub("(....)(....)","\\2\\1","34127856")
        '78563412'
        

        【讨论】:

          【解决方案6】:
          import re
          re.sub(r'(..)(..)', r'\2\1', '12345678')
          re.sub(r'(....)(....)', r'\2\1', '34127856')
          

          【讨论】:

            最近更新 更多