概要
バイトストリームをラップし、特定の値をエスケープする必要があります。また、逆の方法が必要です。制御コードをエスケープ解除して、生のペイロードを取得します。ソケットを使用しています。socket-commandsはstring-parametersを使用します。Pythonでは、すべての文字列は基本的にchar*
-arrayのラッパーです。
素朴なアプローチ
その文字列であり、特定の値を他の値に置き換えたいと考えています。では、これを達成するための最も簡単な方法は何ですか?
def unstuff(self, s):
return s.replace('\xFE\xDC', '\xFC').replace('\xFE\xDD', '\xFE').replace('\xFE\xDE', '\xFE')
def stuff(self, s):
return s.replace('\xFC', '\xFE\xDC').replace('\xFD', '\xFE\xDD').replace('\xFE', '\xFE\xDE')
悪いようです。replace-callごとに、新しい文字列コピーが作成されます。
イテレータ
非常にPython的なアプローチは、この特定の問題のイテレータを定義することです。入力データを目的の出力に変換するイテレータを定義します。
def unstuff(data):
i = iter(data)
dic = {'\xDC' : '\xFC', '\xDD' : '\xFD', '\xFE' : '\xDE'}
while True:
d = i.next() # throws StopIteration on the end
if d == '\xFE':
d2 = i.next()
if d2 in dic:
yield dic[d2]
else:
yield '\xFE'
yield d2
else:
yield d
def stuff(data):
i = iter(data)
dic = { '\xFC' : '\xDC', '\xFD' : '\xDD', '\xFE' : '\xDE' }
while True:
d = i.next() # throws StopIteration on the end
if d in dic:
yield '\xFE'
yield dic[d]
else:
yield d
def main():
s = 'hello\xFE\xDCWorld'
unstuffed = "".join(unstuff(s))
stuffed = "".join(stuff(unstuffed))
print s, unstuffed, stuffed
# also possible
for c in unstuff(s):
print ord(c)
if __name__ == '__main__':
main()
stuff()
unstuff()
反復可能なもの(リスト、文字列、...)が必要で、iterator-objectを返します。print
結果を取得したり、に渡したりする場合はsocket.send
、(で示されているように)文字列に戻す必要があります"".join()
。予期しないデータはすべて何らかの方法で処理0xFE 0x__
されます。どのパターンとも一致しない場合は、逐語的に返されます。
RegExp
別の方法は、正規表現を使用することです。それは大きなトピックであり、時には問題の原因にもなりますが、単純に保つことができます。
import re
s = 'hello\xFE\xDCWorld' # our test-string
# read: FE DC or FE DD or FE DE
unstuff = re.compile('\xFE\xDC|\xFE\xDD|\xFE\xDE')
# read:
# - use this pattern to match against the string
# - replace what you have found (m.groups(0), whole match) with
# char(ord(match[1])^0x20)
unstuffed = unstuff.sub(lambda m: chr(ord(m.group(0)[1])^0x20), s)
# same thing, other way around
stuff = re.compile('\xFC|\xFD|\xFE')
stuffed = stuff.sub(lambda m: '\xFE' + chr(ord(m.group(0))^0x20), unstuffed)
print s, unstuffed, stuffed
前述のように、ソケットで使用できるようにするには、どこかに新しい文字列を作成する必要があります。少なくとも、このアプローチでは、文字列の不要なコピーが作成されることはありませんs.replace(..).replace(..).replace(..)
。これらのオブジェクトの構築は比較的費用がかかるため、パターンstuff
とunstuff
その周辺を維持する必要があります。
ネイティブC関数
Pythonで速度が低下する場合は、cpythonを使用してCコードとして実装することをお勧めします。基本的に、私は最初の実行を行い、オタクのバイト数を数え、新しい文字列を割り当て、2回目の実行を行います。私はpython-c-extensionsにあまり慣れていないので、このコードを共有したくありません。うまくいくようです。次の章を参照してください
比較
最適化の最も重要なルールの1つ:比較してください!すべてのテストの基本設定:
generate random binary data as a string
while less_than_a_second:
unstuff(stuff(random_data))
count += 1
return time_needed / count
私は知っている、セットアップは最適ではありません。しかし、いくつかの有用な結果が得られるはずです。
何が見えますか?ネイティブが最速の方法ですが、非常に小さい文字列の場合のみです。これはおそらくpython-interpreterが原因です。必要な関数呼び出しは3つではなく1つだけです。しかし、ほとんどの場合、マイクロ秒は十分に高速です。〜500バイト後、タイミングは単純なアプローチとほぼ同じです。実装では、そこにいくつかの深い魔法が起こっているに違いありません。イテレータと正規表現は、努力と比較して受け入れられません。
要約すると、単純なアプローチを使用します。何かを良くするのは難しい。また、単にタイミングを推測すると、ほとんどの場合間違っています。