替换字符串带斜杠_python字符串零碎总结

687bdcf02823461502133898675f36df.png

本文讲一讲与python字符串打交道时的一些注意事项,最初是参考python cookbook一书,后来更新了一部分内容。目录如下

  • 尽量使用字符串方法而不是re库
  • 正则表达式中实现“或”的逻辑(2020年9月7日更新了多字符的或逻辑)
  • 字符串拼接
  • 文件读写时
  • 配合生成器使用
  • 字符串反斜杠和正则(2020年9月7日更新新增了这一节)
  • python 正则表达式技巧(2020年12月10日更新)

1.尽量使用字符串方法而不是re库

我平常操作字符串时最常用的是join strip split +这几个方法,如果遇到这几个方法解决不了的事情就去使用re库,如字符串匹配、替换等,甚至都完全忘记字符串本身提供了非常丰富的方法。

所以这里提醒关注find replace startswith endswith这几个方法,这些基础的方法会比re库快很多,字面匹配用这些比较好,涉及到需要写正则表达式才能匹配下来的情况再找re库

2.正则表达式中实现“或”的逻辑

比如下面拆分出单词,因为分隔符不同,所以要使用“或”的逻辑

import re
line = 'am are; friend, kind,good, foolish'

当你通过“或”的逻辑写下这样的代码时

re.split(';s*|,s*| s*', line)

# ['am', 'are', 'friend', 'kind', 'good', 'foolish']

要想到可以改成这样

re.split('[,; ]s*', line)

# ['am', 'are', 'friend', 'kind', 'good', 'foolish']

另外,如果分隔符想保留下来,可以加一个括号

re.split('([,; ]s*)', line)

# ['am', ' ', 'are', '; ', 'friend', ', ', 'kind', ',', 'good', ', ', 'foolish']

更灵活的方式是用(?:),可以实现多字符的“或”逻辑。假设分隔符不是单个字符,则[]无法使用,如下所示,假如&&也想作为一个分隔符

import re
line = 'am are; friend, kind,good, foolish&& bc'
re.split('[,; ]s*', line)
# ['am', 'are', 'friend', 'kind', 'good', 'foolish&&', 'bc']

re.split('(?:;|,| |&&)s*', line)
# ['am', 'are', 'friend', 'kind', 'good', 'foolish', 'bc']

这种“或”逻辑在re.findall等其他功能中也经常使用。

3.字符串拼接

a b两个字符串用空格拼接起来一般有三种方法

a = 'abc'
b = 'bcd'

' '.join((a, b))
a + ' ' + b
'{} {}'.format(a, b)

# 'abc bcd'

需要注意的是

(1)不要使用这种写法

s = ''
for p in parts:
    s += p

使用加号来连接大量字符是非常低效率的,因为会产生大量中间变量,涉及内存复制和垃圾回收操作

可以将每次新的值append到列表中,最好是定义在函数里,每次yield出来,最后得到一个生成器,举例如下

def get_string(a):
    yield 'This is a'
    if a > 1:
        yield 'big'
    else:
        yield 'small'
    yield 'one.'

' '.join(get_string(2))
# 'This is a big one.'

' '.join(get_string(0))
# 'This is a small one.'

(2)避免不必要的拼接

print(a + ':' + b + ':' + c) # Ugly
print(':'.join([a, b, c])) # Still ugly
print(a, b, c, sep=':') # Better

4.文件读写时

我们是应该将字符串拼接得很大一起读进文件里呢,还是一小条一小条读进去呢?

  • 如果每一条字符串都很短,那么拼起来读会好一些
  • 如果字符串很长,拼起来会占很大内存,则分开读会好一些

5.配合生成器使用

def sample():
    yield 'Is'
    yield 'Chicago'
    yield 'Not'
    yield 'Chicago?'

上面的代码是值得推荐的。因为得到的生成器可定制性很高,想对它进行什么处理都可以,如

text = ''.join(sample())

或者

for part in sample():
    f.write(part)

上面那么多yield看起来很别扭,但是想想我们经常把yield放在循环里,几百几千都用了,这几个不算什么的。

6. 字符串反斜杠和正则

我们来看看下面例子dt的区别

'd'   # 'd'
'd'  # 'd'
r'd'  # 'd'
r'd' #'d'

't'   # 't'
r't'  # 't'
't'  # 't'

解读一下这个结果

  • t有特殊的转移含义,在字符串中应看做一个整体
  • d应该看做两个字符,因为字符有转义功能,所以用表示反斜杠这个字符本身
  • 如果想表示t这两个字符,可以前面加r或者直接用
  • 'd'这种写法虽然也不会报错,但它不规范

在正则表达式中d表示数字,虽然有特殊含义,但它们依然是两个字符。下面例子三种方式都能得到相同的结果,但第一种写法不规范。

import re
line = 'a1b2c'
re.split('d', line) # bad
re.split('d', line) # good
re.split(r'd', line) # good

# ['a', 'b', 'c']

其实就是在写正则时加一个r的习惯。如果我们想写的pattern中既包含d又包含t,直接用r会将t理解为t,但其实这样也可以匹配到t,看下面这个例子

import re
line = 'a1b2ctd'
re.split(r'd|t', line)  # ['a', 'b', 'c', 'd']
re.split('d|t', line) # ['a', 'b', 'c', 'd']
re.split('d|t', line)  # ['a', 'b', 'c', 'd']
re.split('d|t', line)  # ['a', 'b', 'c', 'd']
re.split('d|t', line)   # ['a', 'b', 'c', 'd']

import re
line = r'a1b2ctd'
re.split('d|t', line)     # ['a', 'b', 'ctd']
re.split('d|t', line)    # ['a', 'b', 'ctd']
re.split('d|t', line)  # ['a', 'b', 'c', 'd']

上面结果说明

  • 匹配t,用tt都可以
  • 匹配t,必须用t,因为是在分别匹配三个字符

同理,我们也就很好地理解下面这两个例子

import re
line = 'a1b2cdd'
re.split('d', line)        # ['a', 'b', 'cdd']
re.split('d|d', line)  # ['a', 'b', 'c', 'd']

line = r'a1b2cdd'
re.split('d', line)        # ['a', 'b', 'cdd']
re.split('d|d', line)  # ['a', 'b', 'c', 'd']

因为'd'r'd' 都相当于'd',和上面t的情形一样。

总结规律:正则表达式匹配的是字符本身,而不是print出来的样子。

  • 比如匹配'd''t',就看做三个字符,不管它其中一个是用于转义,print出来只有一个反斜杠。
  • 比如匹配't',就看做两个字符用t,不管你print出来是否有特殊含义。而为什么t也可以呢?因为就像正则中用d匹配数字一样,正则中也用t来专门匹配t

python正则表达式技巧

  • . * ?这三个符号一般都是作用在单个字符上,如果想作用到多个字符上,要这样用(?:abc)*
  • “或”用|例如匹配的pattern是ab|cd,则表示abcd,如果想表示bc,就要这样做a(?:b|c)d
  • .*?是匹配除了n以外的字符,可以用flags=re.S令它可以匹配所有字符
  • 1可以表示匹配中括号括起来的部分,如果有多个括号,可以用2或更大的数字。举例:通过替换,进行如下变换
原字符串:[array([1,2,3]), array([[1],[3],[4]])]
新字符串:[[1,2,3], [[1],[3],[4]]]

代码如下

import re
s = '[array([1,2,3]), array([[1],[3],[4]])]'
re.sub(r'array(([.*?]))', r'1', s)
  • {}()[]什么时候需要用:在pattern中,如果能构成正则的特殊用法,则如果想要表示它本身,就要加;在替换时,repl参数不需要加
s = '{} {1}'  
re.sub('}', ':', s)         # {: {1:'
re.sub('}', ':', s)        # {: {1:'
re.sub('{}', ':', s)        # ': {1}'
re.sub('{1}', ':', s)       # error,    pattern 部分不能出现 {1} 形式
re.sub('{1}', ':', s)     # '{} :',    要加 
re.sub('{1}', '{2}', s)   # '{} {2}', repl 部分可以出现 {2} 形式

s = '() (1)'  
re.sub(')', ':', s)         # error, 对 () 来说,单独出现都要打 
re.sub(')', ':', s)        # '(: (1:'
re.sub('()', ':', s)        # ':(:): :(:1:):', () 被当成空值处理了
re.sub('(1)', ':', s)       # '() (:)', 正则表达式中没有 (1) 这种用法,所以没问题
re.sub(')', '(', s)        # '(( (1(', repl 中没有问题

s = '[] [1]' 
re.sub(']', ':', s)         # '[: [1:', 右括号没问题
re.sub('[', ':', s)         # error, 左括号不能单独出现
re.sub('[]', ':', s)        # error
re.sub('[]', ':', s)       # ': [1]', 只需要对左括号打  即可
re.sub('[12]', ':', s)      # '[] [:]', 这里表示的是正则的用法而不是它本身
re.sub('[12]', '[]', s)     # '[] [[]]', repl 中没有问题

专栏信息

专栏主页:python编程

专栏目录:目录

版本说明:软件及包版本说明

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值