Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用多字符分隔符标记字符串_Python_Regex_Tokenize - Fatal编程技术网

Python 使用多字符分隔符标记字符串

Python 使用多字符分隔符标记字符串,python,regex,tokenize,Python,Regex,Tokenize,我尝试使用以下规则标记表达式: 分隔符是“}}”和“{” 分隔符之间的字符串应该保持完整(不包括丢弃的单个空格(可以在解析器中完成) 可嵌入分离器,并保持顺序 “{”和“}”的单个出现应该保持不变,并且不能用作分隔符(参见上一个测试) 结果中不应有空字符串(可以在解析器中完成) 这对异常(用parens标记)可以通过在解析中对(正确的)结果进行后处理来完成。结果将被反馈给递归下降解析器 下面是一些没有通过我所包含的单元测试的测试。find_all函数是最接近匹配的,但仍然设法删除了一些部分。

我尝试使用以下规则标记表达式:

  • 分隔符是“}}”和“{”

  • 分隔符之间的字符串应该保持完整(不包括丢弃的单个空格(可以在解析器中完成)

  • 可嵌入分离器,并保持顺序

  • “{”和“}”的单个出现应该保持不变,并且不能用作分隔符(参见上一个测试)

  • 结果中不应有空字符串(可以在解析器中完成)

这对异常(用parens标记)可以通过在解析中对(正确的)结果进行后处理来完成。结果将被反馈给递归下降解析器

下面是一些没有通过我所包含的单元测试的测试。
find_all
函数是最接近匹配的,但仍然设法删除了一些部分。我没有在下面的代码中使用
re.split()
(它将保留空字符串),但我尝试了它,但运气不太好。我希望正则表达式可以避免在我的代码中逐个字符地扫描字符串

def tokenize_search(line):
    token_pat = re.compile(r'({{)([^{(?!{)]*|[^}(?!})]*)(}})')
    tokens = re.search(token_pat, line).groups()
    return list (tokens)

def tokenize_findall(line):
    token_pat = re.compile(r'({{)([^{(?!{)]*|[^}(?!})]*)(}})')
    tokens = re.findall(token_pat, line)
    return tokens

def check(a1, a2):
    print(a1 == a2)

def main():
    check(tokenize_search('{{}}'), ['{{', '}}'])
    check(tokenize_search('aaa {{}}'), ['{{', '}}'])
    check(tokenize_search('{{aaa}}'), ['{{', 'aaa', '}}'])
    check(tokenize_search('{{aa}} {{bbb}}'), ['{{', 'aa', '}}', '{{', 'bbb', '}}'])
    check(tokenize_search('{{aaa {{ bb }} }}'), ['{{', 'aaa ', '{{', ' bb ', '}}', '}}'])
    check(tokenize_search('{{aa {{ bbb {{ c }} }} }}'), ['{{', 'aa ', '{{', ' bbb ', '{{', ' c ', '}}', '}}', '}}'])
    check(tokenize_search('{{a{a}{{ b{b}b {{ c }} }} }}'), ['{{', 'a{a}', '{{', ' b{b}b ', '{{', ' c ', '}}', '}}', '}}'])
更新

感谢Olivier提供了一个有效的解决方案。如果我能更好地理解regex lookout,我仍然希望regex解决方案能起作用。如果我使用下面的
tokenize\u finditer
方法,它就通过了测试,它所做的就是用中间的内容填充
跳过的
组(除了我可以进行后期处理以使代码更简单的空格)因此我希望我可以在
'({{)|(}}})
正则表达式中添加一个
子句,该子句表示:`或获取任何字符,后跟任何不匹配的字符'}}或'{{。不幸的是,我无法成功地编写这个匹配器。我见过一些正则表达式甚至可以进行递归匹配的例子,因为这不是递归的,所以听起来更可行

def tokenize_finditer(line):
    token_pat = re.compile(r'({{)|(}})')
    result = []
    if re.search(token_pat, line):
        prev = len(line)
        for match in re.finditer(token_pat, line):
            start, end = match.span()
            if start > prev:
                expr = line[prev:start]
                if not expr.isspace():
                    result.append(expr)
            prev = end
            result.append(match.group())

    return result

这个问题不是很严重,但我建议不要尝试用正则表达式来解决它

因为您要做的是使用给定的分隔符对字符串进行分区,所以我们可以基于
分区
函数编写一个解决方案,并进行一些调整以适应所有规则

import re

def partition(s, sep):
    tokens = s.split(sep)

    # Intersperse the separator betweem found tokens
    partition = [sep] * (2 * len(tokens) - 1)
    partition[::2] = tokens

    # We remove empty and whitespace-only tokens
    return [tk for tk in partition if tk and not tk.isspace()]


def tokenize_search(line):
    # Only keep what is inside brackets
    line = re.search(r'{{.*}}', line).group() or ''

    return [tk for sub in partition(line, '{{') for tk in partition(sub, '}}')]

上面的代码通过了所有测试。您需要将该结果提供给解析器以检查括号匹配。

我相信Olivier Melançon的分区方法是可行的。但是,正则表达式仍然有一些用途,例如检查所讨论的模式是否正确平衡,或者从更大的字符串中提取平衡(如第二个示例所示)

这样做需要一个递归正则表达式,如下所示:

{{((?>(?:(?!{{|}}).)++|(?R))*+)}}

由于pythonre模块不支持regex递归,因此您需要依赖替代模块来使用它


要进一步处理匹配结果,您需要查看
$1
中的内部部分,并一次深入一层,例如,但这很麻烦。

刚刚在twitter上收到您的消息:)我知道我晚了两个月才参加聚会,但我有一些新想法,以防您感兴趣

我查看了这些例子,注意到,您可以完全匹配和捕获所有“{{”或“}}”或“{{}}对”中的一个标记。幸运的是,这很容易表达:

/({{|}}|(?:(?!{{|}})[^ ])+(?!({{(?2)*?}}|(?:(?!{{|}}).)*)*$))/g

“在{}}对的中间”是唯一棘手的部分。为此,我使用了负向前看来确保我们的位置后面不是一个平衡数目的(可能是嵌套的){}对,然后是字符串的结尾。对于平衡良好的输入,这将确保所有匹配的标记都在{}对内

现在,您会问,“平衡良好的输入”部分如何?如果输入无效,那么像“aaa}}”这样的示例将产生
[“aaa”,“}}”]
结果。这并不理想。您可以单独验证输入;或者,如果您希望将其变成无法测量的怪物,那么您可以这样做:

/(?:^(?!({{(?1)*?}}|(?:(?!{{|}}).)*)*+$)(*COMMIT)(*F))?({{|}}|(?:(?!{{|}})[^ ])+(?!({{(?3)*?}}|(?:(?!{{|}}).)*)*+$))/g


这真的只是为了作秀。我同意其他建议,建议使用解析器或其他更易于维护的工具。但若你们看过我的博客,你们就会明白我对这些怪物很有兴趣:)

您应该编写一个基本的解析器来处理这个问题。我认为,正则表达式可能不是这里使用的最佳工具。您的第二条规则与您对测试4的预期结果相矛盾。空格没有保持完整。感谢您捕捉到这一点,这是一个拼写错误。我更新了规则。使用切片的3参数感觉很好。我放弃了ab找出正则表达式,因为不使用递归似乎是不可能的。