Python 如何将特定长度的字符串与正则表达式匹配

Python 如何将特定长度的字符串与正则表达式匹配,python,regex,Python,Regex,对于我的一个项目,我正在尝试实现BitTorrent协议的一小部分,可以找到它。具体地说,我想使用其中的“Bencoding”部分,这是一种安全编码数据以便通过套接字传输的方法。格式如下: 8:a string => "a string" i1234e => 1234 l1:a1:be => ['a', 'b'] d1:a1:b3:one3:twoe => {'a':'b', 'one':two} 编码部分很简单,但解码却变得相当麻烦。例如,如果我有一个字符串列表,我没

对于我的一个项目,我正在尝试实现BitTorrent协议的一小部分,可以找到它。具体地说,我想使用其中的“Bencoding”部分,这是一种安全编码数据以便通过套接字传输的方法。格式如下:

8:a string => "a string"
i1234e => 1234
l1:a1:be => ['a', 'b']
d1:a1:b3:one3:twoe => {'a':'b', 'one':two}
编码部分很简单,但解码却变得相当麻烦。例如,如果我有一个字符串列表,我没有办法将它们分割成单独的字符串。我尝试了几种不同的解决方案,包括和一个自定义令牌解析器。我目前正在尝试使用正则表达式,它似乎进展得相当顺利,但我仍然对字符串问题犹豫不决。我现在的正则表达式是:

(?P<length>\d+):(?P<contents>.{\1})
(?P\d+):(?P.{\1})

然而,我似乎不能用第一组作为第二组的长度。有什么好办法吗?或者我的理解完全错了,而答案就在我面前?

如果您对字符串进行两次解析,就可以做到这一点。应用第一个正则表达式以获取长度。连接第二个正则表达式中的长度以形成有效表达式

不确定如何在python中实现这一点,但C#中的一个示例是:

string regex = "^[A-Za-z0-9_]{1," + length + "}$"
要将1与长度匹配,字符数可以是alphanumeric或u,其中长度是根据之前只检索长度的正则表达式确定的


希望这能有所帮助:)

用于此目的的任何解析器都需要是有状态的(即记住东西),而正则表达式大体上不是有状态的。他们是这个工作的错误工具

如果您只需要担心这些数据类型,我想我应该为每种数据类型编写自定义解析器,在读取第一个字符后将控制权传递给相应的解析器

我现在真的想实施一个,但已经晚了

好吧,我决定写一个实现:

from StringIO import StringIO
import string

inputs = ["10:a stringly",
         "i1234e" ,
         "l1:a1:be",
         "d1:a1:b3:one3:twoe"]

# Constants
DICT_TYPE = 'd'
LIST_TYPE = 'l'
INT_TYPE  = 'i'
TOKEN_EOF = ''
TOKEN_END = 'e'
COLON     = ':'


class BadTypeIndicatorException(Exception):pass


def read_int(stream):

   s = ""

   while True:
      ch = stream.read(1)
      if ch not in [TOKEN_EOF, TOKEN_END, COLON]:
         s += ch
      else:
         break

   return s


def tokenize(stream):

   s = ""

   while True:

      ch = stream.read(1)

      if ch == TOKEN_END or ch == TOKEN_EOF:
         return 

      if ch == COLON:
         length = int(s)
         yield stream.read(length)
         s = ""

      else:
         s += ch


def parse(stream):

   TYPE = stream.read(1)

   if TYPE in string.digits:
      length = int( TYPE + read_int(stream) )
      return stream.read(length)

   elif TYPE is INT_TYPE: 
      return int( read_int(stream) )

   elif TYPE is LIST_TYPE: 
      return list(tokenize(stream))

   elif TYPE is DICT_TYPE:
      tokens = list(tokenize(stream))
      return dict(zip(tokens[0::2], tokens[1::2]))

   else: 
      raise BadTypeIndicatorException



for input in inputs:
   stream = StringIO(input)
   print parse(stream)

你使用了错误的工具来完成工作。。。这需要某种状态保持,一般来说,正则表达式是无状态的


可以找到我在PERL中使用BDE编码(和bencoding)的示例实现

关于该函数如何工作的解释(因为我从未对其进行过注释[oops]):

基本上,您需要做的是设置一个递归函数。此函数接受字符串引用(因此可以修改)并返回“something”(其性质意味着它可以是数组、哈希表、int或字符串)

函数本身只检查字符串中的第一个字符,并根据该字符决定要执行的操作:

  • 如果它是一个
    i
    ,则解析出i和第一个e之间的所有文本,并尝试根据允许的规则将其解析为int
  • 如果它是一个数字,那么读取所有的数字,直到,然后从字符串中读取那么多字符
列表和字典是事情开始变得有趣的地方。。。如果第一个字符是ld,则需要去掉
l
/
d
,然后将当前字符串传递回函数,以便它可以开始解析列表或字典中的元素。然后,只需将返回的值存储在适当结构中的适当位置,直到找到一个
e
,然后返回剩下的结构


记住,我实现的函数是破坏性的。当函数通过引用传递而返回时,传入的字符串是空的,或者更准确地说,它将没有它解析和返回的任何内容(这就是为什么可以递归使用它:它没有处理的任何内容都保持不变)。不过,在大多数初始调用的情况下,这应该会处理所有事情,除非您一直在做一些奇怪的事情,因此上面的内容仍然有效。

您需要分两步来完成这项操作。对于这样的简单解析问题,正则表达式实际上有点过分了。我是这样做的:

def read_string(stream):
    pos = stream.index(':')
    length = int(stream[0:pos])
    string = stream[pos+1:pos+1+length]
    return string, stream[pos+1+length:]
这是一种函数式的解析方式,它返回解析的值和流的其余部分

对于列表,可能:

def read_list(stream):
    stream = stream[1:]
    result = []
    while stream[0] != 'e':
        obj, stream = read_object(stream)
        result.append(obj)
    stream = stream[1:]
    return result

然后定义一个read_对象,该对象检查流的第一个字符并进行适当的分派。

伪代码,不进行语法检查:

define read-integer (stream):
    let number 0, sign 1:
        if string-equal ('-', (c <- read-char (stream))):
            sign <- -1
          else:
            number <- parse-integer (c)
        while number? (c <- read-char (stream)):
            number <- (number * 10) + parse-integer (c)
        return sign * number

define bdecode-string (stream):
    let count read-integer (stream):
        return read-n-chars (stream, count)

define bdecode-integer (stream):
    ignore read-char (stream)
    return read-integer (stream)

define bdecode-list (stream):
    ignore read-char (stream)
    let list []:
        while not string-equal ('e', peek-char (stream)):
            append (list, bdecode (stream))
        return list

define bdecode-dictionary (stream):
    let list bdecode-list stream:
        return dictionarify (list)

define bdecode (stream):
    case peek-char (stream):
        number? => bdecode-string (stream)
        'i' => bdecode-integer (stream)
        'l' => bdecode-list (stream)
        'd' => bdecode-dictionary (stream)
定义读取整数(流):
让数字0,符号1:

如果字符串等于(“-”,(c)则不确定答案,但原始的Bit Torrent客户端是开源的。而且它甚至在Python中!因此,您可以尝试四处探索:“现在您有两个问题!”::rimshot::谢谢你的链接,MatrixFrog。我想我将只导入该文件,并在我的程序中使用原始实现。Python字符串是不可变的,所以你必须做一些不同的事情。也许传递一个偏移量变量或其他什么?或者在循环中进行。不过,我的思维大部分时间是递归的。你知道吗GEX是有状态的。正则表达式和不同解析器之间的唯一区别在于正则表达式只有固定的、有限的状态。事实上,这是定义正则语言的一种常见方法:任何可以使用固定的、有限的状态进行解析的语言。@Dietrich-我理解你的意思,但实际上我们谈论的是两种完全相同的语言“国家”一词的不同含义。现代编程中的这个词最常用,正如我所用的那样——某些进程在操作之间记住一些东西。在正则表达式中,我们可以称之为上下文,而正则表达式基本上是设计成与上下文无关的。我会选择这一点作为答案,但我决定不重新发明轮子,所以我使用了BitTorrent impleMatrixFrog链接到上面的实现。否则,我可能会使用你的实现,或者基于它的实现。我不知道为什么有人对此投了反对票,但我只是检查了原始bittorrent是如何实现的(感谢MatrixFrog的链接),几乎就是这样,加上错误检查,它可以处理