2

编辑:我应该注意,我想要任何十六进制数组的一般情况,而不仅仅是我提供的谷歌。

编辑背景:背景是网络:我正在解析一个 DNS 数据包并试图获取它的 QNAME。我将整个数据包作为一个字符串接收,每个字符代表一个字节。显然这个问题看起来像一个 Pascal 字符串问题,使用 struct 模块似乎是要走的路。

我在 Python 2.7 中有一个 char 数组,其中包含八进制值。例如,假设我有一个数组

DNS = "\03www\06google\03com\0"

我想得到:

www.google.com

有什么有效的方法来做到这一点?我的第一个想法是遍历 DNS 字符数组并将字符添加到我的新数组答案中。每次我看到一个 '\' 字符时,我都会忽略 '\' 和它后面的两个字符。有没有办法在不使用新数组的情况下获得结果 www.google.com?

我令人作呕的实现(我的答案是一个字符数组,这不是我想要的,我只想要字符串 www.google.com:

DNS = "\\03www\\06google\\03com\\0"
answer = []
i = 0
while i < len(DNS):
    if DNS[i] == '\\' and DNS[i+1] != 0:
        i += 3    
    elif DNS[i] == '\\' and DNS[i+1] == 0:
        break
    else:
        answer.append(DNS[i])
        i += 1
4

4 回答 4

2

既然你已经解释了你真正的问题,那么到目前为止你得到的答案都不会奏效。为什么?因为它们都是\03从字符串中删除序列的方法。但是你没有像 一样序列\03,你有单个控制字符。

当然,您可以做类似的事情,只需用点替换任何控制字符。

但是您真正想做的不是用点替换控制字符,而是解析 DNS 数据包。

DNS 由RFC 1035定义。DNS 数据包中的 QNAME 是:

一个域名,表示为一系列标签,其中每个标签由一个长度八位字节后跟该八位字节数组成。域名以根的空标签的零长度八位字节结束。请注意,该字段可能是奇数个八位字节;不使用填充。

所以,让我们解析一下。如果您了解“由“长度八位字节后跟该八位字节数”组成的标签与“帕斯卡字符串”的关系,那么有一种更快的方法。此外,您可以将其写得更干净、更简洁。但是让我们来做吧最简单的方法:

def parse_qname(packet):
    components = []
    offset = 0
    while True:
        length, = struct.unpack_from('B', packet, offset)
        offset += 1
        if not length:
            break
        component = struct.unpack_from('{}s'.format(length), packet, offset)
        offset += length
        components.append(component)
    return components, offset
于 2013-11-09T02:06:24.020 回答
1
import re
DNS = "\\03www\\06google\\03com\\0"
m = re.sub("\\\\([0-9,a-f]){2}", "", DNS)
print(m)
于 2013-11-09T01:22:14.423 回答
1

也许是这样的?

#!/usr/bin/python3

import re

def convert(adorned_hostname):
    result1 = re.sub(r'^\\03', '', adorned_hostname )
    result2 = re.sub(r'\\0[36]', '.', result1)
    result3 = re.sub(r'\\0$', '', result2)
    return result3

def main():
    adorned_hostname = r"\03www\06google\03com\0"
    expected_result = 'www.google.com'
    actual_result = convert(adorned_hostname)
    print(actual_result, expected_result)
    assert actual_result == expected_result

main()
于 2013-11-09T01:29:24.107 回答
1

对于最初提出的问题,将字符串中的反斜杠十六进制序列替换为"\\03www\\06google\\03com\\0"点......</p>

如果您想使用正则表达式执行此操作:

  • \\匹配反斜杠。
  • [0-9A-Fa-f]匹配任何十六进制数字。
  • [0-9A-Fa-f]+匹配一个或多个十六进制数字。
  • \\[0-9A-Fa-f]+匹配后跟一个或多个十六进制数字的反斜杠。

你想找到每个这样的序列,并用一个点替换它,对吧?如果您查看re文档,您会发现一个名为的函数sub,用于用替换字符串替换模式:

re.sub(r'\\[0-9A-Fa-f]+', '.', DNS)

我怀疑这些实际上可能是八进制,而不是十六进制,在这种情况下你想要[0-7]而不是[0-9A-Fa-f],但没有其他任何改变。


另一种方法是识别这些是有效的 Python 转义序列。DNS.decode('string_escape')而且,如果我们将它们转义回它们来自的地方(例如,使用标准库struct模块。这具有在您读取数据时验证数据的优点,并且不会被任何可能出现的误报所抛弃,例如,如果其中一个字符串组件在它的中间有一个反斜杠。

当然,这更多地是关于数据的假设。似乎它的真正含义“一系列以长度为前缀的字符串,连接,然后反斜杠转义”,在这种情况下,您应该这样解析它。但它看起来像这样可能只是一个巧合,在这种情况下,这样解析它是一个非常糟糕的主意。

于 2013-11-09T01:39:53.130 回答