替换字符串中的子串

1 投票
3 回答
1069 浏览
提问于 2025-04-17 12:50

我的函数可以在字符串中找到十六进制表示的颜色(也就是十六进制的CSS颜色),然后把它们替换成简短的表示方式。
比如:#000000可以简化成#000

import re

def to_short_hex (string):
    match = re.findall(r'#[\w\d]{6}\b', string)

    for i in match:
        if not re.findall(r'#' + i[1] + '{6}', i):
            match.pop(match.index(i))

    for i in match:
        string = string.replace(i, i[:-3])

    return string;

to_short_hex('text #FFFFFF text #000000 #08088')

输出:

text #FFF text #000 #08088

有没有什么办法可以用列表推导式之类的来优化我的代码呢?

3 个回答

1

在遍历一个列表的时候使用 pop 是个坏主意。因此,这不是一种优化,而是修复一个错误。此外,我修改了 re,以防止像 '#34j342' 这样的字符串被接受:

>>> def to_short_hex(s):
...     matches = re.findall(r'#[\dabcdefABCDEF]{6}\b', s)
...     filtered = [m for m in matches if re.findall(r'#' + m[1] + '{6}', m)]
...     for m in filtered:
...         s = s.replace(m, m[:-3])
...     return s
... 
>>> to_short_hex('text #FFFFFF text #000000 #08088')
'text #FFF text #000 #08088'

另外,我觉得在第二个 re 中使用 re.search 就足够了。

3

这样怎么样?你可以通过把 is6hexdigit 嵌入到 to_short_hex 中来加快速度,但我希望代码更易读一些。

hexdigits = "0123456789abcdef"

def is6hexdigit(sub):
    l = sub.lower()
    return (l[0] in hexdigits) and (l.count(l[0]) == 6)

def to_short_hex(may_have_hexes):
    replaced = ((sub[3:] if is6hexdigit(sub[:6]) else sub)
                        for sub in may_have_hexes.split('#'))
    return '#'.join(replaced)
2

这就是re.sub的用处!用正则表达式去查找某个东西,然后再进行一系列的查找和替换操作,这样做并不是个好主意。首先,这样很容易不小心替换掉你不想替换的内容;其次,这样做会重复很多不必要的工作。

另外,你可能想把'#aaccee'缩短成'#ace'。这个例子也实现了这个功能:

def to_short_hex(s):
    def shorten_match(match):
        hex_string = match.group(0)
        if hex_string[1::2]==hex_string[2::2]:
            return '#'+hex_string[1::2]
        return hex_string
    return re.sub(r"#[\da-fA-F]{6}\b", shorten_match, s)

解释

re.sub可以接受一个函数,这个函数会应用到每一个匹配的结果上。它会接收一个匹配对象,并返回要替换的字符串。

切片表示法允许你进行步进操作。hex_string[1::2]会从字符串中每隔一个字符取一次,开始于索引1,一直到字符串的末尾。hex_string[2::2]则是从索引2开始,每隔一个字符取一次,直到末尾。所以对于字符串"#aaccee",我们得到"ace"和"ace",它们是匹配的。而对于字符串"#123456",我们得到"135"和"246",它们是不匹配的。

撰写回答