使用正则表达式添加前导零

时间:2019-06-23 10:57:23

标签: python regex python-3.x

说我有一个像这样的字符串

'1 - hello.mp3'
'22 - hellox.mp3'
'223 - hellox.mp3'
'hellox.mp3'

我希望输出为

'001 - hello.mp3'
'022 - hellox.mp3'
'223 - hellox.mp3'
'hellox.mp3'

如果开头是数字,则将0附加为三位数。

有没有办法在python中使用正则表达式?

3 个答案:

答案 0 :(得分:2)

是的,正则表达式可以做到这一点。将re.sub()与回调函数一起使用:

import re

def pad_number(match):
    number = int(match.group(1))
    return format(number, "03d")

fixed_text = re.sub(r"^(\d+)", pad_number, text)

我使用的模式,^(\d+)匹配1个或多个数字(\d是一个数字,+至少匹配一次,但将包含所有后续数字),但仅在字符串的开头(^是此处的“文本开头”锚点。)

然后,对于每个匹配的模式,调用pad_number()函数,该函数返回的字符串用于替换匹配的模式。由于该模式使用捕获组()之间的所有字符都是这样的组),因此该函数可以通过调用match.group(1)来访问匹配的数字。

该函数将数字转换为整数,然后使用format() function将整数转换为文本,但这一次是0填充的3个字符宽的数字。这就是03格式指令告诉format()生成的内容。

请注意,该模式可以匹配更多个数字,但是限制它们没有多大意义,除非您要限制一个严格的上限(此时还需要添加一个限制下一个字符 not 为数字)。 format(number, "03d")指令产生的数字至少至少3个数字,但可以处理更长的值。

演示:

>>> import re
>>> samples = [
...     '1 - hello.mp3',
...     '22 - hellox.mp3',
...     '223 - hellox.mp3',
...     'hellox.mp3',
... ]
>>> def pad_number(match):
...     number = int(match.group(1))
...     return format(number, "03d")
...
>>> for sample in samples:
...     result = re.sub(r"^(\d+)", pad_number, sample)
...     print(f"{sample!r:20} -> {result!r:20}")
...
'1 - hello.mp3'      -> '001 - hello.mp3'
'22 - hellox.mp3'    -> '022 - hellox.mp3'
'223 - hellox.mp3'   -> '223 - hellox.mp3'
'hellox.mp3'         -> 'hellox.mp3'

同样,请考虑到此方法不是特殊情况,即开头不超过4个数字的字符串。您只需获得更长的数字序列即可:

>>> re.sub(r"^(\d+)", pad_number, "4281 - 4 digits")
'4281 - 4 digits'
>>> re.sub(r"^(\d+)", pad_number, "428117 - 6 digits")
'428117 - 6 digits'

即使我们将\d模式限制为最多只能匹配3个数字(例如,\d{1,3}),也会发生这种情况。

如果要使填充宽度可配置,则可以将所有内容放在嵌套函数中并使用字符串格式。您真的不需要

import re

def pad_leading_number(text, width):
    def pad_number(match):
        number = int(match.group(1))
        return format(number, f"0{width}d")

    return re.sub(fr"^(\d+)", pad_number, text)

演示:

>>> pad_leading_number("22 - hellox.mp3", 3)
'022 - hellox.mp3'
>>> pad_leading_number("22 - hellox.mp3", 7)
'0000022 - hellox.mp3'

答案 1 :(得分:0)

您专门要求使用正则表达式-(请参见Martijn's solution),但也可以通过字符串操作来实现:

data = [ '1 - hello.mp3', '22 - hellox.mp3', '223 - hellox.mp3', 'hellox.mp3' ]


def prefixZeroIfStartWithNumber(t):
    """Splits t at first space, if convertable to in, prefixes with three zeros
    and returns it together with remainder of t. Else returns t."""
    spl = t.split(" ",1)
    try:
        k = int(spl[0])
        return f"{k:03} {spl[1]}" # format with zeros, add remainder
    except (ValueError, IndexError): 
        return t

for text in data:
    print(prefixZeroIfStartWithNumber(text))

输出:

001 - hello.mp3
022 - hellox.mp3
223 - hellox.mp3
hellox.mp3

另一种方法-应该更快:

def prefixZeroIfStartWithNumber(t):
    number, _, remainder = t.partition(" ")
    try:
        k = int(number)
        return f"{k:03} {remainder}" # format with zeros, add remainder
    except (ValueError, IndexError): 
        return t 

使用str.partition

答案 2 :(得分:0)

@martijn Pieters为您提供了正确答案,但是您也可以做同样的简单操作,两次运行此正则表达式:

regex = r"^(?=\d\d? )"
replacedby = "0"


^ asserts position at start of a line
Positive Lookahead (?=\d\d? )
\d matches a digit (equal to [0-9])
\d? matches a digit (equal to [0-9]) between zero and one times
Matches SPACE

I use the positive lookahead to match the lines starting with ## - but not including the ## - in the match, then I replace the start of the line with 0

为简单起见,我在这里用javascript显示相同的内容:

const regex = /^(?=\d\d? )/gm;
const str = `
1 - hello.mp3
22 - hellox.mp3
223 - hellox.mp3
hellox.mp3`;

const subst = `0`;

const result = str.replace(regex, subst).replace(regex, subst);

console.log('Substitution result: ', result);

在此链接中在线查看示例,并将其导出为python或其他语言:

https://regex101.com/r/jVsY5v/1/