文本处理:字符串替换

方法1:str.replace

str.replace(old, new[, count])
Return a copy of the string with all occurrences of substring old replaced by new. If the optional argument count is given, only the first count occurrences are replaced.

该方法逻辑大致如下所示,存在多个 oldnew 映射对时,需要串联使用多个 replace 方法, 效率较低。

matches = list(re.finditer(old, string))

if len(matches) == 0:
	return
	
if count > 0:
	for match in matches:
		string[match.start(0): match.end(0)] = match.group(0)
		count -= 1
		
		if count == 0:
			break
else:
	for match in matches:
		string[match.start(0): match.end(0)] = match.group(0)

 

方法2:str.translate

在了解该方法之前,先了解类方法 str.maketrans

str.maketrans(x[, y[, z]])

  • If there is only one argument, it must be a dictionary mapping Unicode ordinals (integers) or characters (strings of length 1) to Unicode ordinals, strings (of arbitrary lengths) or None. Character keys will then be converted to ordinals.
  • If there are two arguments, they must be strings of equal length, and in the resulting dictionary, each character in x will be mapped to the character at the same position in y. If there is a third argument, it must be a string, whose characters will be mapped to None in the result.
  • 如果实参值是一个字典,则字典的键可以为“单个字符”、“Unicode 序数”, 字典值可以为“Unicode 序数”、“任意长度的字符串”、None。返回结果为一个字典,返回字典的键会转换为“Unicode 序数”, 返回字典的值与实参值一致。

  • 如果传入两个实参值,则位置参数xy 必须是一个任意长度的字符串,会将同一位置的x字符映射为y 字符,如果存在第三个实参值,位置参数z 中的每一个字符将与 None 建立映射关系:

    assert len(x) == len(y)
    
    d = dict()
    for c_x, c_y in zip(x, y):
    	d.update({ord(c_x): c_y}
    
    if z is not None:
    	for c_z in z:
    	d.update({ord(c_z}: None)
    

str.translate(table)
Return a copy of the string in which each character has been mapped through the given translation table.

位置参数table 接受的实参值类似于 str.maketrans 的返回结果,str.translate 会将字符串一一进行映射,如果字符对应的 Unicode 序数在实参值字典中,则可能会被映射为其它任意长度的字符串,或者被删除(Unicode 序数对应 None 时)。
 

方法3:re.sub(比较复杂)

re.sub(pattern, repl, string, count=0, flags=0)
Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl.

此函数的逻辑大致如下:

matches = list(re.finditer(pattern, string))

if len(matches) == 0:  # If the pattern isn’t found, string is returned unchanged.
	return string
else:
	for match in matches:
		if isinstance(repl, str):  # repl can be a string or a function
			string[match.start(0): match.end(0)] = repl
		else:
			string[match.start(0): match.end(0)] = repl(match)
	
	return string

从左往右,将string 中匹配到pattern并且非重叠的部分替换成replrepl 可以是一个字符串,也可以是一个函数。

“非重叠”的意思是指 patternstring匹配的部分不存在重叠情况,如下:pattern 第一次匹配 string中的 “12”, 第二次匹配 string中的 “34”, 而非“23”,否则相邻的两个匹配存在重叠。

re.sub(r"(\d)(\d)", "99", "1234")  # return "9999"

repl 为字符串类型时,支持“转义”与组内容的反向引用,即会对repl 中转义的部分进行特殊处理, 注意包含转义符号\ 时,需使用 Python Raw String 表示法

  • 转义 ASCII 字符,并且是有效转义,替换后的结果采用转义后的字符,如 “\n” 表示一个换行符。

    re.sub(r"(\d)(\d)", r"a\nb", "1234")   # return 'a\nba\nb'
    
  • 如果转义 ASCII 失败,则抛出异常:

    re.sub(r"(\d)(\d)", r"\z", "1234")  # 抛出异常 re.error: bad escape \z at position 0
    
  • 如果与其它非 ASCII 转义,不做处理,仅保留非 ASCII 字符:

    re.sub(r"(\d)(\d)", r"\&", "1234")   # return '\\&\\&'
    
  • 如果转义符号与组编号组合,则引用第编号组匹配到的内容:

    re.sub(r"(\d)(\d)", r"\1", "1234")  # return "13
    
    # 如果第二项为 "\1", 则返回'\x01\x01'————双重转义,Python 与 正则表达式都将 “\” 作为转义字符。
    # ord("\x01") -> 1
    # chr(1) -> "\x01"
    
  • 在 Python 3.9 中,除了支持组编号,也支持组别名,来引用特定组匹配到的内容,只是此种情形中,需在 pattern 中为组进行命名 :

    re.sub(r"(?P\d)(?P\d)", r"\", "1234")  # 使用组名 “13”
    re.sub(r"(?P\d)(?P\d)", r"\<1>", "1234")  # 使用组编号 “13”
    

如果 repl 为函数, 则传入实参为 re.Match,函数返回值需为字符串, 如下:

def func(match):
	if len(match.group(0)) > 1:
		return "9" * len(match.group(0))
	else:
		return ""

re.sub(r"\d+", func, "a1b23c456")  # return "ab99c999"

你可能感兴趣的:(Python,编程,python)