2

編集:私が提供したGoogleのものだけでなく、任意の16進数配列の一般的なケースが必要であることに注意してください。

背景の編集: 背景はネットワークです: DNS パケットを解析して、その QNAME を取得しようとしています。パケット全体を文字列として取り込み、すべての文字が 1 バイトを表します。どうやらこの問題は Pascal の文字列の問題のようで、struct モジュールを使用するのがよいようです。

Python 2.7 に、8 進数値を含む char 配列があります。たとえば、配列があるとしましょう

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

私は手に入れたい:

www.google.com

これを行う効率的な方法は何ですか?私が最初に考えたのは、DNS char 配列を繰り返し処理し、新しい配列の回答に char を追加することでした。「\」文字が表示されるたびに、「\」とその後の 2 つの文字を無視します。新しい配列を使用せずに結果の 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

最初に尋ねられた質問については、文字列内のバックスラッシュと 16 進数のシーケンス"\\03www\\06google\\03com\\0"をドットなどに置き換えます…</p>

正規表現でこれを行いたい場合:

  • \\バックスラッシュに一致します。
  • [0-9A-Fa-f]任意の 16 進数に一致します。
  • [0-9A-Fa-f]+1 つ以上の 16 進数に一致します。
  • \\[0-9A-Fa-f]+バックスラッシュの後に 1 つ以上の 16 進数が続くものと一致します。

そのようなシーケンスをそれぞれ見つけて、ドットに置き換えたいと思いませんか? ドキュメントを調べると、パターンを置換文字列に置き換えるために使用されるre関数が呼び出されていることがわかります。sub

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

これらは実際には 16 進数ではなく 8 進数である可能性があると[0-7]思われ[0-9A-Fa-f]ます。


これを行う別の方法は、これらが有効な Python エスケープ シーケンスであることを認識することです。そして、元の場所にエスケープ解除すると (たとえば、 を使用してDNS.decode('string_escape'))、これは、長さの前に固定された (別名 "Pascal") 文字列のシーケンスに変わります。これは、さまざまな方法で解析できる標準形式です。 stdlibstructモジュール。これには、読み取り時にデータを検証できるという利点があり、たとえば、文字列コンポーネントの 1 つに途中でバックスラッシュが含まれている場合に現れる可能性のある誤検知によって放り出されることはありません。

もちろん、それはデータについてより多くのことを前提としています。これの本当の意味は、「連結され、バックスラッシュでエスケープされた長さのプレフィックス付き文字列のシーケンス」であると思われます。その場合、そのように解析する必要があります。しかし、そのように見えるのは単なる偶然かもしれません。その場合、そのように解析するのは非常に悪い考えです。

于 2013-11-09T01:39:53.130 に答える
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
import re
DNS = "\\03www\\06google\\03com\\0"
m = re.sub("\\\\([0-9,a-f]){2}", "", DNS)
print(m)
于 2013-11-09T01:22:14.423 に答える