根据函数输出替换字符串

时间:2015-05-30 10:19:45

标签: python regex

所以,输入:

accessibility,random good bye

我想要输出:

a11y,r4m g2d bye

所以,基本上,我必须以下列格式缩写所有长度大于或等于4的单词:first_letter + length_of_all_letters_in_between + last_letter

我尝试这样做:

re.sub(r"([A-Za-z])([A-Za-z]{2,})([A-Za-z])", r"\1" + str(len(r"\2")) + r"\3", s)

但它不起作用。在JS中,我很容易做到:

str.replace(/([A-Za-z])([A-Za-z]{2,})([A-Za-z])/g, function(m, $1, $2, $3){
   return $1 + $2.length + $3;
});

我如何在Python中做同样的事情?

编辑:我不能丢失原始字符串中的任何标点符号。

7 个答案:

答案 0 :(得分:7)

你在JavaScript中做的事情当然是正确的,你传递的是匿名函数。你在Python中做的是传递一个常量表达式(" \ 12 \ 3",因为在函数调用之前评估len(r"\2")),它不是一个可以为每个匹配计算的函数!

虽然Python中的匿名函数并不像JS中那样有用,但它们可以在这里完成工作:

>>> import re
>>> re.sub(r"([A-Za-z])([A-Za-z]{2,})([A-Za-z])", lambda m: "{}{}{}".format(m.group(1), len(m.group(2)), m.group(3)), "accessability, random good bye")
'a11y, r4m g2d bye'

这里发生的是为每个替换调用lambda,获取匹配对象。然后,我检索所需的信息并从中构建替换字符串。

答案 1 :(得分:3)

您遇到的问题是len(r'\2')始终为2,而不是正则表达式中第二个捕获组的长度。您可以使用lambda表达式创建一个与您在JavaScript中使用的代码类似的函数:

re.sub(r"([A-Za-z])([A-Za-z]{2,})([A-Za-z])",
       lambda m: m.group(1) + str(len(m.group(2)) + m.group(3),
       s)

lambda的m参数是match对象,对group方法的调用等同于之前使用的反向引用。

使用简单的单词匹配模式而不使用捕获组可能更容易(group()仍然可以在没有参数的情况下调用以获取整个匹配的文本):

re.sub(r'\w{4,}', lambda m: m.group()[0] + str(len(m.group())-2) + m.group()[-1], s)

答案 2 :(得分:2)

tmp, out = "",""
for ch in s:
    if ch.isspace() or ch in {",", "."}:
        out += "{}{}{}{}".format(tmp[0], len(tmp) - 2, tmp[-1], ch) if len(tmp) > 3 else tmp + ch
        tmp = ""
    else:
        tmp += ch
out += "{}{}{}".format(tmp[0], len(tmp) - 2, tmp[-1]) if len(tmp) > 3 else tmp
print(out)

a11y,r4m g2d bye

如果您只想要字母字符,请使用str.isalpha:

tmp, out = "", ""
for ch in s:
    if not ch.isalpha():
        out += "{}{}{}{}".format(tmp[0], len(tmp) - 2, tmp[-1], ch) if len(tmp) > 3 else tmp + ch
        tmp = ""
    else:
        tmp += ch
out += "{}{}{}".format(tmp[0], len(tmp) - 2, tmp[-1]) if len(tmp) > 3 else tmp
print(out)
a11y,r4m g2d bye

两者的逻辑是相同的,只是我们检查的是不同的,如果not ch.isalpha()为False我们发现了一个非alpha字符,所以我们需要处理tmp字符串并将其添加到输出字符串。根据要求,if len(tmp)不大于3我们只需将tmp字符串加上当前字符添加到我们的输出字符串中。

我们需要循环外的最后out += "{}{}{}来捕获字符串不以逗号,空格等结尾的时候。如果字符串以非alpha结尾,我们将添加一个空字符串,以便它对输出没什么影响。

它会保留标点符号和空格:

 s = "accessibility,random   good bye !!    foobar?"
def func(s):
    tmp, out = "", ""
    for ch in s:
        if not ch.isalpha():
            out += "{}{}{}{}".format(tmp[0], len(tmp) - 2, tmp[-1], ch) if len(tmp) > 3 else tmp + ch
            tmp = ""
        else:
            tmp += ch
    return "{}{}{}".format(tmp[0], len(tmp) - 2, tmp[-1]) if len(tmp) > 3 else tmp
print(func(s,3))
a11y,r4m   g2d bye !!    f4r?

答案 3 :(得分:1)

作为另一种精确方式,您可以为re.sub使用单独的函数,并使用简单的正则表达式r"(\b[a-zA-Z]+\b)"

>>> def replacer(x): 
...    g=x.group(0)
...    if len(g)>3:
...        return '{}{}{}'.format(g[0],len(g)-2,g[-1])
...    else :
...        return g
... 
>>> re.sub(r"(\b[a-zA-Z]+\b)", replacer, s)
'a11y,r4m g2d bye'

同样作为pythonic和一般方式,要在列表中获取替换的单词,您可以使用re.finditer使用列表推导:

>>> from operator import sub
>>> rep=['{}{}{}'.format(i.group(0)[0],abs(sub(*i.span()))-2,i.group(0)[-1]) if len(i.group(0))>3 else i.group(0) for i in re.finditer(r'(\w+)',s)]
>>> rep
['a11y', 'r4m', 'g2d', 'bye']

re.finditer将返回包含所有matchobjects的生成器,然后您可以迭代它并使用span()方法获取matchobject s的开头和结尾。

答案 4 :(得分:1)

保持简单......

>>> s = "accessibility,random good bye"
>>> re.sub(r'\B[A-Za-z]{2,}\B', lambda x: str(len(x.group())), s)
'a11y,r4m g2d bye'
在两个单词字符或两个非单词字符之间匹配的

\B有助于匹配除第一个和最后一个之外的所有字符。

答案 5 :(得分:0)

使用正则表达式和理解:

import re
s = "accessibility,random good bye"
print "".join(w[0]+str(len(w)-2)+w[-1] if len(w) > 3 else w for w in re.split("(\W)", s))

给出:

a11y,r4m g2d bye

答案 6 :(得分:-1)

查看以下代码

sentence = "accessibility,random good bye"
sentence = sentence.replace(',', " ")
sentence_list = sentence.split(" ")
for item in sentence_list:
    if len(item) >= 4:
        print item[0]+str(len(item[1:len(item)-1]))+item[len(item)-1]

你唯一应该照顾逗号和其他标点字符。

相关问题