说我有一个像这样的字符串
'1 - hello.mp3'
'22 - hellox.mp3'
'223 - hellox.mp3'
'hellox.mp3'
我希望输出为
'001 - hello.mp3'
'022 - hellox.mp3'
'223 - hellox.mp3'
'hellox.mp3'
如果开头是数字,则将0附加为三位数。
有没有办法在python中使用正则表达式?
答案 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
答案 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或其他语言: