忽略字符串中的大小写、标点和空格

0 投票
2 回答
2852 浏览
提问于 2025-04-15 18:40

如何在处理字符串时高效地忽略大小写、标点和空格呢?这些字符串应该按单词来分,而不是按字符来分。在比较和切片这些单词字符串时,速度要尽可能快。

我原本打算在下面的代码中使用不区分大小写和标点的字符串,但看到评估 class Slice: def __eq__(self, other): return self.root == other.root 需要花费很长时间后,我决定改用 data = tuple(string.split())。因为让字符串在处理时不区分大小写、标点和空格,并且按单词而不是字符来处理,会让下面的代码变得非常复杂和耗时。

class Slice:

    def __init__(self, data, offset, length):
        self.prefix = data[:offset]
        self.root = data[offset:offset+length]
        self.suffix = data[offset+length:]

    def __eq__(self, other):
        return self.root == other.root

    def __len__(self):
        return len(self.root)

################################################################################

class Match:

    def __init__(self, data, key, prefix_tree, suffix_tree):
        self.data = data
        self.key = key
        self.prefix_tree = prefix_tree
        self.suffix_tree = suffix_tree
        self.__value = len(key) + prefix_tree.value() + suffix_tree.value()

    def value(self):
        return self.__value

################################################################################

class Tree(tuple):

    def __new__(cls, nodes):
        tree = super().__new__(cls, nodes)
        tree.__value = max(map(Match.value, tree)) if tree else 0
        return tree

    def value(self):
        return self.__value

    def find(self, value):
        for index, match in enumerate(self):
            if match.value() == value:
                return index
        raise ValueError()

################################################################################

def search(data, key):
    length = 0
    nodes = []
    for d_block in shrink(data, len(key)):
        block_len = len(d_block)
        if length > block_len:
            return Tree(nodes)
        for k_block in slide(key, block_len):
            if d_block == k_block:
                length = block_len
                prefix_tree = search(d_block.prefix, k_block.prefix)
                suffix_tree = search(d_block.suffix, k_block.suffix)
                match = Match(d_block, k_block, prefix_tree, suffix_tree)
                nodes.append(match)
    return Tree(nodes)

def shrink(data, max_len):
    for length in range(min(len(data), max_len), 0, -1):
        for block in slide(data, length):
            yield block

def slide(data, length):
    for offset in range(len(data) - length + 1):
        yield Slice(data, offset, length)

################################################################################

def build_tree(nodes):
    match = nodes[nodes.find(nodes.value())]
    node = match.key
    if match.prefix_tree:
        node.prefix = build_tree(match.prefix_tree)
    if match.suffix_tree:
        node.suffix = build_tree(match.suffix_tree)
    return node

def flatten_tree(node):
    array = [0]
    _flatten(node, array)
    return tuple(array)

def _flatten(node, array):
    if isinstance(node.prefix, Slice):
        _flatten(node.prefix, array)
    else:
        array.append(node.prefix)
    array[0] += 1
    array.append((array[0], node.root))
    if isinstance(node.suffix, Slice):
        _flatten(node.suffix, array)
    else:
        array.append(node.suffix)

2 个回答

2

如果你想让一个字符串的实例在迭代时能遍历它的 self.__string,就像你的 __iter__ 方法所表明的那样,那么返回 __string 的长度是唯一合理的选择。如果 len(x)sum(1 for _ in x) 返回的值不一样,那就真的很奇怪了。

我得承认,我不太明白这个类的目的是什么(尤其是你为什么选择使用旧式的方式,以及你为什么用这么复杂的方式来构建 __simple),但内部的一致性还是很重要的。所以,要么改一下 __iter__,要么让 __len__ 和它逻辑上兼容。

你的切片逻辑我也完全搞不懂——为什么你要以一种可能和从切片的 __string 重建得到的结果不同的方式来构建切片的 __simple?比如,如果 self.__string 是 '?Boh!',因此 self.__simple 是 'boh',那你为什么会想要 self[1:-1]__string 是 'Boh',但 __simple 是 'o',这样就不兼容、不一样,也和从切片重新计算得到的 __simple 不一致呢……?

我想这和关于长度的问题没有直接关系,但我只是对你做出的这些非常奇怪的设计选择感到好奇……

2

“解决这个问题的最好方法是什么?”

最好的方法,也是唯一的方法,就是先弄清楚这个对象“代表”什么,以及这个对象的“长度”又代表什么。

这个对象看起来像是一个单词的列表,仅此而已。这似乎就是_string中的内容。

至于_simple是什么,除了它是_string中单词的一个无法访问的过滤子集,其他的就不太清楚了。

那么,长度是什么呢?是单词的长度,还是过滤后子集中的单词长度?

只有你才能定义这个类代表什么。这个定义将决定如何实现__len__。在你明确这个定义之前,根本无法确定任何东西应该如何实现。

撰写回答