第二章 字符串和文本 上

📅 发布时间:2026/7/13 11:21:19 👁️ 浏览次数:
第二章 字符串和文本 上
2.1 使用多个界定符分割字符串一般用的是string的splits1 “苹果,香蕉,橘子,葡萄”result1 s1.split(“,”)print(f简单分割: {result1})输出: [‘苹果’, ‘香蕉’, ‘橘子’, ‘葡萄’]不传参数时split() 会自动处理空格、Tab、换行等并忽略多余空白但是他无法处理多个不同的分隔符但存在局限性无法处理多个不同的分隔符所以最好使用 **re.split()**import reline asdf fjdk; afed, fjek,asdf, fooresultre.split(r[;,\s]\s*,line)print(result)r[;,\s]\s*是什么意思r代表转义方括号[]表示字符集即“匹配方括号内列出的任意一个字符”\s*匹配分隔符后的额外空格这部分紧跟在字符集后面。\s依然是匹配空白字符。*这是一个量词表示“匹配前面的字符0次或多次”。逻辑这部分用来“吃掉”分隔符后面可能存在的多余空格。如果想把分隔符号也保存输出出来可以用括号捕获分组fields re.split(r’( ; | , | \s) \ s*, line)[‘asdf’, ’ , ‘fjdk’, ‘;’, ‘afed’, ‘,’, ‘fjek’, ‘,’, ‘asdf’, ‘,’, ‘foo’]即会把分隔符号也输出保存2.2 字符串开头或结尾匹配检查字符串开头或结尾的一个简单方法是使用str.startswith()或者是str.endswith()方法。比如filename ‘spam.txt’filename.endswith(‘.txt’)Truefilename.startswith(‘file:’)Falseurl ‘http://www.python.org’url.startswith(‘http:’)True然后如果想检查多种匹配可能需要把所有匹配放到一个元组里面然后传给startswith()或者endswith()方法import osfilenames os.listdir(‘.’)filenames[ ‘Makefile’, ‘foo.c’, ‘bar.py’, ‘spam.c’, ‘spam.h’ ][name for name in filenames if name.endswith((‘.c’, ‘.h’)) ][‘foo.c’, ‘spam.c’, ‘spam.h’any(name.endswith(‘.py’) for name in filenames)True这个里面传入想匹配的末尾是(‘.c’, ‘.h’)是一个元组2.3用Shell通配符匹配字符串fnmatch()匹配必须是一对一的即fnmatch(‘foo.txt’, ‘*.txt’)如果是列表/集合等等需要用列表推导式即一次取一个[表达式 for 变量 in 可迭代对象 if 条件]from fnmatch import fnmatchfile_list[‘1.txt’,‘2.csv’,‘oo.txt’,‘1.c’]result[name for name in file_list if fnmatch(name,‘*.txt’)]print(result)2.4 字符串匹配和搜索如果你想匹配的是字面字符串那么你通常只需要调用基本字符串方法就行 比如str.find(),str.endswith(),str.startswith()textyeah, but no, but yeah, but no, but yeahprint(text.find(no))输出10即子字符串no在原字符串中第一次出现的位置索引是 10对于复杂的匹配需要使用正则表达式和re模块。 为了解释正则表达式的基本原理假设你想匹配数字格式的日期字符串比如11/27/2012你可以这样做import retext1 ‘11/27/2012’if re.match(r’\d/\d/\d,text1):print(‘yes’)else:print(‘no’)\d即匹配“一个或多个连续的数字”如果想用一个模式去匹配多次应该先将模式字符串预编译为模式对象即定义一个datapatre.compile(r’\d/\d/\d)后面就可以直接调用这个模板if datepat.match(text1):但这个match()总是从头匹配当待匹配的字段在句中间就没用了可以引入findall()text ‘Today is 11/27/2012. PyCon starts 3/13/2013.’datepat re.compile(r’\d/\d/\d)datepat.findall(text)**有一个需要注意的点\d 这个里面的代表多个数字也可以为1位 **然后用match的时候通常会利用括号去捕获分组例如datepat re.compile(r’(\d)/(\d)/(\d)‘)m.group(0)输出’11/27/2012’m.group(1)输出’11’findall()方法会搜索文本并以列表形式返回所有的匹配。 如果你想以迭代方式返回匹配可以使用finditer()方法来代替当写正则式字符串的时候相对普遍的做法是使用原始字符串比如r(\d)/(\d)/(\d)。 这种字符串将不去解析反斜杠这在正则表达式中是很有用的。 如果不这样做的话你必须使用两个反斜杠类似(\\d)/(\\d)/(\\d)。2.5 字符串搜索和替换对于简单的字面模式直接使用str.repalce()方法即可text.replace(‘yeah’, ‘yep’)对于复杂的模式请使用re模块中的sub()函数为了说明这个假设你想将形式为11/27/2012的日期字符串改成2012-11-27。示例如下**其中sub的语法是**re.sub(pattern, repl, string, count0, flags0)pattern被匹配的模式第二个参数是替换模式第三个是需要被替换的变量text ‘Today is 11/27/2012. PyCon starts 3/13/2013.’import rere.sub(r’(\d)/(\d)/(\d)‘, r’\3-\1-\2’, text)‘Today is 2012-11-27. PyCon starts 2013-3-13.’sub()函数中的第一个参数是被匹配的模式第二个参数是替换模式。反斜杠数字比如\3指向前面模式的捕获组号\d即代表单个/多个数字对于更加复杂的替换可以传递一个替换回调函数来代替from calendar import month_abbrdef change_date(m):… mon_name month_abbr[int(m.group(1))]… return ‘{} {} {}’.format(m.group(2), mon_name, m.group(3))…datepat.sub(change_date, text)‘Today is 27 Nov 2012. PyCon starts 13 Mar 2013.’return ‘{} {} {}’.format(m.group(2), mon_name, m.group(3))这个的语法是前面{}占位后面用format对应去填入什么是回调函数把函数想参数一样去传递然后调用比如这里的change_date2.6 字符串忽略大小写的搜索替换为了在文本操作时忽略大小写你需要在使用re模块的时候给这些操作提供re.IGNORECASE标志参数。比如text ‘UPPER PYTHON, lower python, Mixed Python’re.findall(‘python’, text, flagsre.IGNORECASE)[‘PYTHON’, ‘python’, ‘Python’]re.sub(‘python’, ‘snake’, text, flagsre.IGNORECASE)‘UPPER snake, lower snake, Mixed snake’2.7最短匹配模式**在用正则表达式匹配某个文本模式但是它找到的是模式的最长可能匹配**比如上面用的findall11python2222python我用findall 寻找所有python他返给我的是python2222python不符合我的预期所以我需要找寻最短匹配模式可以在模式的 * 后面加上修饰符*为了说明清楚考虑如下的例子str_pat re.compile(r’“(. * )”)text1 ‘Computer says “no.”’str_pat.findall(text1)[‘no.’]text2 ‘Computer says “no.” Phone says “yes.”’str_pat.findall(text2)[‘no. Phone says yes.’]在这个例子中模式r\(.*)\的意图是匹配被双引号包含的文本。 但是在正则表达式中 * 操作符是贪婪的因此匹配操作会查找最长的可能匹配。 于是在第二个例子中搜索text2的时候返回结果并不是我们想要的。为了修正这个问题可以在模式中的 * 操作符后面加上?修饰符就像这样str_pat re.compile(r’“(. * ? )”)str_pat.findall(text2)[‘no.’, ‘yes.’]这样就使得匹配变成非贪婪模式从而得到最短的匹配也就是我们想要的结果。2.8 多行匹配模式comment re.compile(r’/ \ * (.*?) \ * /)这个里面\是为了使得 * 能够被正确识别因为外面本来就转义的r这个里面用反斜杠 \ 是为了让这个 * 不被识别成次数的意思现在有一个问题用比如findall去匹配的时候换行就不支持正常匹配了为了修正这个问题你可以修改模式字符串增加对换行的支持。比如comment re.compile(r’/ \ * ((?:.|\n) * ?) \ * /‘)comment.findall(text2)[’ this is a\n multiline comment ]在这个模式中(?:.|\n)指定了一个非捕获组 (也就是它定义了一个仅仅用来做匹配而不能通过单独捕获或者编号的组)。想要非捕获组就是前面加一个然后这个里面 . | \n 前面的.代表任意字符代表或 \n代表换行这个就能解决上面的问题2.9 将Unicode文本标准化你正在处理Unicode字符串需要确保所有字符串在底层有相同的表示。解决方案在Unicode中某些字符能够用多个合法的编码表示。为了说明考虑下面的这个例子s1 ‘Spicy Jalape\u00f1o’s2 ‘Spicy Jalapen\u0303o’s1‘Spicy Jalapeño’s2‘Spicy Jalapeño’s1 s2Falselen(s1)14len(s2)15这里的文本”Spicy Jalapeño”使用了两种形式来表示。 第一种使用整体字符”ñ”(U00F1)第二种使用拉丁字母”n”后面跟一个”~”的组合字符(U0303)。在需要比较字符串的程序中使用字符的多种表示会产生问题。 为了修正这个问题你可以使用unicodedata模块先将文本标准化import unicodedatat1 unicodedata.normalize(‘NFC’, s1)t2 unicodedata.normalize(‘NFC’, s2)t1 t2Trueprint(ascii(t1))‘Spicy Jalape\xf1o’t3 unicodedata.normalize(‘NFD’, s1)t4 unicodedata.normalize(‘NFD’, s2)t3 t4Trueprint(ascii(t3))‘Spicy Jalapen\u0303o’normalize()第一个参数指定字符串标准化的方式。 NFC表示字符应该是整体组成(比如可能的话就使用单一编码)而NFD表示字符应该分解为多个组合字符表示。Python同样支持扩展的标准化形式NFKC和NFKD它们在处理某些字符的时候增加了额外的兼容特性。