cookbook-第二章-字符串和文本处理
2018-03-25 22:14
113 查看
在任意多分隔符下分离字符串
字符串对象的split()方法适用场景很少(不适合于多分隔符或者分隔符后空格数目未知的情况)。使用re.split()
import re line = 'asdf dasdasdas; dasdasda, dadasd' print(re.split(r'[;,\s]\s*', line)) ['asdf', 'dasdasdas', 'dasdasda', 'dadasd']
使用捕获(capture group)
import re line = 'asdf dasdasdas; dasdasda, dadasd' print(re.split(r'(;|,|\s)\s*', line)) ['asdf', ' ', 'dasdasdas', ';', 'dasdasda', ',', 'dadasd']
我们发现,捕获可以获取()里面对应的模式,即分隔符
获取分隔符常常很有用,比如用分隔符重构字符串
import re line = 'asdf dasdasdas; dasdasda, dadasd' fields = re.split(r'(;|,|\s)\s*', line) #::表示隔2取,默认以0开始,如0,2,4,... values = fields[::2] #以1开始隔2取 delimiters = fields[1::2] + [''] print(values) print(delimiters) #''.join表示连接 print(''.join(v + d for v, d in zip(values, delimiters))) ['asdf', 'dasdasdas', 'dasdasda', 'dadasd'] [' ', ';', ',', ''] asdf dasdasdas;dasdasda,dadasd
如果不想获取分隔符,并且想用括号来对正则表达式分段,注意使用?:
import re line = 'asdf dasdasdas; dasdasda, dadasd' fields = re.split(r'(?:;|,|\s)\s*', line) print(fields) ['asdf', 'dasdasdas', 'dasdasda', 'dadasd']
字符串前缀后缀
使用str.startswith()或者str.endswith()
filename = 'spam.txt' print(filename.endswith('txt')) print(filename.startswith('file')) print('http://www.python.org'.startswith('http:')) True False True
使用元组提供多个选择
filename.startswith(('txt', 'spam'))
需要注意的地方:
元组不能替换为列表或者集合,如果为列表或集合,注意先使用tuple()转换为元组
startswith(endswith),元组和any结合使用
import os if any(name.startswith(('.c', '.h')) for name in os.listdir(dirname)):
用于判断某个目录下是否具有特定类型的文件
使用类shell通配符匹配字符串
使用fnmatch()
from fnmatch import fnmatch print(fnmatch('foo.txt', '*.txt')) print(fnmatch('foo.txt', '?oo.txt')) print(fnmatch('Dat45.csv', 'Dat[0-9]*'))
需要注意的是,fnmatch()的大小写判断依赖于操作系统。
使用fnmatchcase()
from fnmatch import fnmatch,fnmatchcase #无论任何操作系统,遵守约定俗成的大小写规则 print(fnmatchcase('foo.txt', '*.TXT')) False
需要注意的地方:
fnmatch()是字符串方法和强大的正则表达式之间的折中,如果只需要简单的通配符机制,那么这是一个不错的选择。
匹配和查找文本模式
如果你想匹配的文本是简单的字面值,那么可以使用startswith(),endswith(),find()来匹配如果是更复杂的匹配,那么就要用到正则表达式了
使用正则表达式
import re text1 = '3/25/2018' text2 = 'March 25, 2018' if re.match(r'\d+/\d+/\d+', text1): print('yes') else: print('no') if re.match(r'\d+/\d+/\d+', text2): print('yes') else: print('no') yes no
如果需要使用同一模式来多次匹配字符串,那么需要先用compile编译正则表达式
使用compile()
import re text1 = '03/25/2018' datepat = re.compile(r'\d+/\d+/\d+') if datepat.match(text1): print('yes') else: print('no') yes
需要注意的是,match总是匹配第一次出现的模式,如果想匹配所有模式,使用findall
使用findall匹配所有模式
import re text = 'Today is 03/25/2018.Yesterday is 03/24/2018.' datepat = re.compile(r'\d+/\d+/\d+') print(datepat.findall(text)) ['03/25/2018', '03/24/2018']
使用捕获
import re datepat = re.compile(r'(\d+)/(\d+)/(\d+)') m = datepat.match('03/25/2018') print(m) print(m.group(0), m.group(1), m.group(2), m.group(3), m.groups()) text = 'Today is 03/25/2018.Yesterday is 03/24/2018' print(datepat.findall(text)) for month, day, year in datepat.findall(text): print('{}-{}-{}'.format(month, day, year)) <_sre.SRE_Match object; span=(0, 10), match='03/25/2018'> 03/25/2018 03 25 2018 ('03', '25', '2018') [('03', '25', '2018'), ('03', '24', '2018')] 03-25-2018 03-24-2018
注意到,使用捕获很利于后续处理,因为捕获将匹配的模式分开了
findall找出所有匹配并且返回列表,如果想迭代处理,那么可以使用finditer()
使用finditer
import re datepat = re.compile(r'(\d+)/(\d+)/(\d+)') text = 'Today is 03/25/2018.Yesterday is 03/24/2018' for m in datepat.finditer(text): print(m.groups()) ('03', '25', '2018') ('03', '24', '2018')
对使用正则表达式的简单总结:
1.编译正则表达式,返回pattern对象
2.使用match(),find(),findall(),finditer()等方法进行匹配
对于match,需要注意,它只检查字符串的开始,这就导致有时候不是你想要的结果
import re datepat = re.compile(r'(\d+)/(\d+)/(\d+)') m = datepat.match('11/27/2012dzxdzxdzx') print(m) print(m.group()) <_sre.SRE_Match object; span=(0, 10), match='11/27/2012'> 11/27/2012
因此需要用到结束标志$
总结一下需要注意的地方:使用正则表达式之前记得使用compile()编译,可以减少开销
查找和替换文本
对于普通的字面字符串而言,使用str.replace()使用str.replace()
text = 'how do you' print(text.replace('do', 'are')) how are you
对于更复杂的情况,使用re.sub().
使用re.sub()
import re text = 'Today is 03/25/2018.Yesterday is 03/24/2018' #\3表示取捕获组的第三个,即年 #re.sub()的第一个参数为匹配的模式,第二个参数是替换的模式 print(re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)) Today is 2018-03-25.Yesterday is 2018-03-24
使用re.compile()
import re text = 'Today is 03/25/2018.Yesterday is 03/24/2018' datepat = re.compile(r'(\d+)/(\d+)/(\d+)') print(datepat.sub(r'\3-\1-\2', text)) Today is 2018-03-25.Yesterday is 2018-03-24
对于更加复杂的替换,可以使用一个回调函数来定义替换部分
使用回调函数
import re from calendar import month_abbr #回调函数的参数是一个match对象,通常由find或者match返回,可以调用group提取匹配特定部分 def change_date(m): #m.group(1)提取了月的部分,并且由month_abbr()转换为了月份缩写形式 mon_name = month_abbr[int(m.group(1))] #注意这里的{}相当于占位符,由format指定的参数来替换 return '{} {} {}'.format(m.group(2), mon_name, m.group(3)) text = 'Today is 03/25/2018.Yesterday is 03/24/2018' datepat = re.compile(r'(\d+)/(\d+)/(\d+)') print(datepat.sub(change_date, text)) Today is 25 Mar 2018.Yesterday is 24 Mar 2018
整个功能相当于,datepat调用sub(),通过编译的正则表达式获取到了匹配,即match对象,将match对象传递给回调函数,回调函数进行处理,返回的结果替换匹配,一次替换结束,再进行第二次替换,依次进行,直到结束。
如果想知道进行了多少次替换,可以使用re.subn()
使用re.subn()
import re from calendar import month_abbr text = 'Today is 03/25/2018.Yesterday is 03/24/2018' datepat = re.compile(r'(\d+)/(\d+)/(\d+)') newtext, n = datepat.subn(r'\3-\1-\2', text) print(newtext) print(n) Today is 2018-03-25.Yesterday is 2018-03-24 2
查找和替换大小写不敏感的文本
使用re.IGNORECASE标志
import re text = 'UPPER PYTHON,lower python, Mixed Python' print(re.findall('python', text, flags = re.IGNORECASE)) print(re.sub('python', 'case', text, flags = re.IGNORECASE)) ['PYTHON', 'python', 'Python'] UPPER case,lower case, Mixed case
这种做法暴露了一个缺陷。不能根据匹配内容的大小写替换对应的大小写内容(比如如果python为大写,仍然替换的是case)
使用支持函数
import re #这种写法的意思是,每次调用re.sub,会调用matchcase,而matchcase返回replace, #于是将匹配对象match传递给了replace,replace在matchcase的作用范围内,于是可以 #获取word的值进行处理 def matchcase(word): def replace(m): text = m.group() if text.isupper(): return word.upper() elif text.islower(): return word.lower() elif text[0].isupper(): return word.capitalize() else: return word return replace text = 'UPPER PYTHON,lower python, Mixed Python' print(re.sub('python', matchcase('case'), text, flags = re.IGNORECASE))
我们可以发现,替换的case随着python的大写,小写,首字母大写而变化
使匹配最短
先来看一个例子import re str_pat = re.compile(r'\"(.*)\"') text1 = 'Computer says "no."' print(str_pat.findall(text1)) text2 = 'Computer says "no." Phone says "yes."' print(str_pat.findall(text2)) ['no.'] ['no." Phone says "yes.']
注意到匹配了整个”no.” Phone says “yes.”“,而我们想要的应该是”no.”和”yes.”。这是由于正则表达式中的*为贪婪的(greedy),所以匹配总会找出符合条件的最长的匹配。为了解决这个问题,需要使用?
?操作符使匹配最短
import re str_pat = re.compile(r'\"(.*?)\"') text2 = 'Computer says "no." Phone says "yes."' print(str_pat.findall(text2)) ['no.', 'yes.']
?操作符使匹配非贪心,并且使匹配最短
正则表达式处理多行
先来看一个处理注释的例子import re comment = re.compile(r'/\*(.*?)\*/') text1 = '/* this is a comment */' text2 = '''/* this is a multiline comment */ ''' print(comment.findall(text1)) print(comment.findall(text2)) [' this is a comment '] []
我们会发现匹配多行的例子失败了,这是因为’.’虽然可以匹配任意字符,但是不能匹配换行符。
解决方法1
使用?:
import re #注意,这里使用了?:非捕获符 comment = re.compile(r'/\*((?:.|\n)*?)\*/') text2 = '''/* this is a multiline comment */ ''' print(comment.findall(text2)) [' this is a\n multiline comment ']
解决方法2
使用re.DOTALL
import re comment = re.compile(r'/\*(.*?)\*/', re.DOTALL) text2 = '''/* this is a multiline comment */ ''' print(comment.findall(text2)) [' this is a\n multiline comment ']
需要注意的地方:
re.DOTALL适用于简单的情况,如果正则表达式相当复杂可能会出问题。因此首推编写出正确的正则表达式,即第一种方法。
将Unicode文本正规化为标准文本
当处理Unicode字符串时,你需要确保每个字符串拥有相同的潜在表示形式。对于Unicode字符集,有些字符可以存在多种表示形式。看一个例子
s1 = 'Spicy Jalape\u00f1o' s2 = 'Spicy Jalapen\u0303o' print(s1) print(s2) print(s1 == s2) print(len(s1)) print(len(s2)) Spicy Jalapeño Spicy Jalapeño False 14 15
“Spicy Jalapeño”存在两种表示形式,第一种是一个整体\u00f1,第二种是n结合一个结合字符(combining character)\u0303
为了解决这个问题,我们需要使用unicodedata模块将字符串转换为同一种形式
使用unicodedata.normalize()
import unicodedata s1 = 'Spicy Jalape\u00f1o' s2 = 'Spicy Jalapen\u0303o' #normalize()的第一个参数指定正规化形式 #NFC代表完全组成 #NFD代表完全解题,使用结合字符 t1 = unicodedata.normalize('NFC', s1) t2 = unicodedata.normalize('NFC', s2) print(t1 == t2) print(ascii(t2)) t3 = unicodedata.normalize('NFD', s1) t4 = unicodedata.normalize('NFD', s2) print(t3 == t4) print(ascii(t4)) True 'Spicy Jalape\xf1o' True 'Spicy Jalapen\u0303o'
总结:
1.正规化是相当重要的,因为你不知道你的输入文本是什么形式,尤其你无法控制输入的时候
2.正规化也可以用来过滤文本,如下所示
import unicodedata s1 = 'Spicy Jalape\u00f1o' t1 = unicodedata.normalize('NFD', s1) #unicodedata.combing()可以识别出结合字符 print(''.join(c for c in t1 if not unicodedata.combining(c))) Spicy Jalapeno
注意到,结合字符被过滤了
在正则表达式中处理Unicode字符集
正则表达式库可以初步识别一些基本的Unicode字符类型,如\d可以识别数字import re num = re.compile('\d+') print(num.match('123')) #如果你想匹配特定Unicode字符,可以使用转义字符 print(num.match('\u0661\u0662\u0663')) <_sre.SRE_Match object; span=(0, 3), match='123'> <_sre.SRE_Match object; span=(0, 3), match='١٢٣'>
看一个比较麻烦的例子
总结一下关于处理Unicode需要注意的地方:
一定要注意正规化,将所有字符串都处理为同一种表示形式
从字符串中去除不想要的字符
使用strip(),lstrip(),rstrip()
s = ' hello word \n' print(s.strip()) print(s.lstrip()) print(s.rstrip()) t = '---------helloxxxxxxx' print(t.lstrip('-')) print(t.strip('-x')) hello word hello word hello word helloxxxxxxx hello
需要注意的地方:
1,strip()默认去除空白符(包括换行符),但是也可以指定去除特定字符
2.strip()只处理左右两边的空白处,不处理中间的。如果想处理中间的空白字符,需要使用replace或者re.sub()
迭代时,结合使用strip除去空白符
with open(filename) as f: #注意这里使用的是生成器表达式,因此会返回一个迭代器,而不是创建临时的列表保存数据 lines = (line.strip() for line in f) for line in lines: pass
清理文本
在一些很简单的层面上,可以使用str.upper(),str.lower()或者str.replace(),re.sub()等方法来处理文本,或者使用之前讲到的unicodedata.normalize()来正规化文本。但是更进一层,可以使用translate()使用translate()
注意到,translate()方法做的相当于是文本处理的映射。
更进一步,清除所有结合字符
可以看出,先通过dict.fromkeys建立了一个以所有结合字符为键,值为None的字典,然后translate使用这个字典将结合字符映射为空,即清除掉
再看一个例子,将Unicode字符集中的数字字符替换为ASCII码的等价形式
使用encode和decode
总结:
1.越简单,通常越快。例如str.replace()通常是最快的,尽管可能需要调用多次。
2.translate()对于字符->字符转换,或者字符->清除也是很快的
3.没有通用的最好的方法。模式是尝试各种方法,找出最优性能的那一个。
分配文本字符串
使用ljust(),rjust(),center()
text = 'Hello World' #填充到20个字符,不足的地方填充空白,字符串左移 print(text.ljust(20)) ##填充到20个字符,不足的地方填充空白,字符串右移 print(text.rjust(20)) ##填充到20个字符,不足的地方填充空白,字符串往中间移动 print(text.center(20)) Hello World Hello World Hello World #指定填充字符 print(text.rjust(20, '=')) print(text.center(20, '*')) =========Hello World ****Hello World*****
使用format()
text = 'Hello World' print(format(text, '>20')) print(format(text, '<20')) print(format(text, '^20')) print(format(text, '=>20s')) print(format(text, '*^20s')) Hello World Hello World Hello World =========Hello World ****Hello World***** #注意,format可以一次性格式化多个字符串 print('{:>10s} {:>10s}'.format('Hello', 'World')) Hello World
对于format()需要注意的地方:
它不仅适用于字符串,它适用于所有值类型。因此format()比其他方法也更具一般性
x = 1.2345 print(format(x, '>10')) print(format(x, '^10.2f')) 1.2345 1.23
使用’%’
text = 'Hello World' print('%-20s' % text) print('%20s' % text) Hello World Hello World
总结:
format()方法功能最强大,也更具一般性。另外,尽量不要用’%’
连接字符串
如果你想连接序列或者可迭代对象,最快的方法是join()使用join()
parts = ['Is', 'Chicago', 'Not', 'Chicago?'] print(' '.join(parts)) Is Chicago Not Chicago?
如果只是想连接很少的字符串,那么可以使用’+’
使用’+’
a = 'is' b = 'you' print(a + ' ' + b) is you
‘+’可以是format的替换
如果只是连接字面值,可以这样做
a = 'Hello ' 'World' print(a) Hello World
需要注意的地方:
当连接大量字符串时,’+’号的开销很巨大,因为有内存拷贝和垃圾回收等开销(跟java一样,字符串多了用StringBuilder).这种情况一定要用join()
使用生成器表达式
注意到,str()字符串转换和使用生成器表达式进行字符串连接同时进行。
有时候,字符串连接是不必要的。看看例子
如果涉及到I/O,那么字符串的连接需要一些权衡
对于版本1,当chunk1和chunk2很小时,会比版本2还快,因为版本2由于有两次I/O操作,会有额外开销,但是当chunk1和chunk2很大时,版本1的额外开销很大,因为它会产生临时结果以及内存拷贝等开销
通过生成器函数产生片段
def sample(): yield 'Is' yield 'Chicago' yield 'Not' yield 'Chicago' text = ''.join(sample()) for part in sample(): f.write(part) #短短几行就实现了缓冲区的功能,是不是相当酷(我说的是Python) def combine(source, maxsize): parts = [] size = 0 for part in source: parts.append(part) size += len(part) if size >= maxsize: yield ''.join(parts) parts = [] size = 0 yield ''.join(parts) for part in combine(sample(), 32768): f.write(part)
注意到,生成器函数并不在意数据组合的细节,他只负责产生数据(搬运工)
在字符串中插入变量
使用字符串对象的format()
s = '{name} has {n} messages' print(s.format(name = 'xxx', n = 'xxx')) xxx has xxx messages
如果变量已经定义过了,那么可以结合使用format_map()和vars()
name = 'xxx' n = 'xxx' s = '{name} has {n} messages' print(s.format_map(vars())) xxx has xxx messages
vars()还有一个特性,那就是适用于实例对象
class Info: def __init__(self, name, n): self.name = name self.n = n s = '{name} has {n} messages' a = Info('xxx', 11) print(s.format_map(vars(a))) xxx has 11 messages
需要注意的是,format()和format的一个缺陷是不能处理缺失值
s = '{name} has {n} messages' print(s.format(name = 'xxx')) Traceback (most recent call last): File "C:\Users\Administrator\Desktop\test.py", line 3, in <module> print(s.format(name = 'xxx')) KeyError: 'n'
一个解决办法是定义一个字典子类,编写missing()方法
使用missing()
class safesub(dict): def __missing__(self, key): return '{' + key + '}' name = 'xxx' n = 'xxx' del(name) s = '{name} has {n} messages' print(s.format_map(safesub(vars()))) {name} has xxx messages
注意到,缺失的数据没有被替换
如果你发现你经常使用format_map,那么应该用一个方法将它包装起来,这个方法用到了”frame hack”
使用frame hack
import sys class safesub(dict): def __missing__(self, key): return '{' + key + '}' def sub(text): return text.format_map(safesub(sys._getframe(1).f_locals)) name = 'xxx' n = 111 print(sub('Hello {name}')) print(sub('{n} hello')) print(sub('your favourite color is {color}')) Hello xxx 111 hello your favourite color is {color}
其他的插入变量的方式
需要注意的是,以上两种方式都没有format()好
总结:
1.format()的优势在于,可以结合字符串格式化(alignment, padding, numerical formatting)等使用
2.f_locals是一个本地变量的拷贝,类型为字典,因此不用担心改变它会产生额外影响
格式化文本到指定的列数
使用textwrap()
使用get_terminal_size()获取终端窗口大小
处理html和xml实体
使用html.escape()
使用errors处理ascii码转换
使用html parser或者xml parser
将文本转换为token
假设你有这样一个字符串,text = ‘foo = 23 +42 * 10’为了符号化这个字符串,你需要某种识别模式的方式,例如转化为这样的形式
为了符号化,第一步就需要定义所有可能的符号,包括空格。方法是正则表达式和命名捕获组(named capture group)
使用正则表达式和命名捕获组
我们注意到,一般形式为?p<符号名称>+正则表达式
符号化的第一步完成了,第二步是使用scanner()
使用scanner()
结合使用生成器和scanner
使用生成器表达式过滤token
总结:
1.必须想清楚每种可能的符号,如果有任何模式未被匹配,scanner的扫描过程就会终止,这也是为什么需要特别定义空字符的符号
2.符号的顺序也需要注意
编写解析器
BNF:EBNF:
表达式计算器
import re import collections NUM = r'(?P<NUM>\d+)' PLUS = r'(?P<PLUS>\+)' MINUS = r'(?P<MINUS>-)' TIMES = r'(?P<TIMES>\*)' DIVIDE = r'(?P<DIVIDE>/)' LPAREN = r'(?P<LPAREN>\()' RPAREN = r'(?P<RPAREN>\))' WS = r'(?P<WS>\s+)' master_pat = re.compile('|'.join([NUM, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN, WS])) Token = collections.namedtuple('Token', ['type', 'value']) def generate_tokens(text): scanner = master_pat.scanner(text) for m in iter(scanner.match, None): tok = Token(m.lastgroup, m.group()) if tok.type != 'WS': yield tok class ExpressionEvaluator: def parse(self, text): self.tokens = generate_tokens(text) self.tok = None self.nexttok = None self.advance() return self.expr() def advance(self): self.tok, self.nexttok = self.nexttok, next(self.tokens, None) def _accept(self, toktype): if self.nexttok and self.nexttok.type == toktype: self.advance() return True else: return False def _expect(self, toktype): if not self._accept(toktype): raise SyntaxError('Expected ' + toktype) def expr(self): expval = self.term() while self._accept('PLUS') or self._accept('MINUS'): op = self.tok.type right = self.term() if op == 'PLUS': expval += right elif op == 'MINUS': expval -= right return expval def term(self): termval = self.factor() while self._accept('TIMES') or self._accept('DIVIDE'): op = self.tok.type right = self.factor() if op == 'TIMES': termval *= right elif op == 'DIVIDE': termval /= right return termval def factor(self): if self._accept('NUM'): return int(self.tok.value) elif self._accept('LPAREN'): expval = self.expr() self._expect('RPAREN') return expval else: raise SyntaxError('Expected or LPAREN') e = ExpressionEvaluator() print(e.parse('2')) print(e.parse(' 2 + (3 * 3) + 4 / 5 + (5 / 5) * 10')) 2 21.8
解析器
class ExpressionTreeBuilder(ExpressionEvaluator): def expr(self): expval = self.term() while self._accept('PLUS') or self._accept('MINUS'): op = self.tok.type right = self.term() if op == 'PLUS': expval = ('+', expval, right) elif op == 'MINUS': expval = ('-', expval, right) return expval def term(self): termval = self.factor() while self._accept('TIMES') or self._accept('DIVIDE'): op = self.tok.type right = self.factor() if op == 'TIMES': termval = ('*', termval, right) elif op == 'DIVIDE': termval = ('/', termval, right) return termval def factor(self): if self._accept('NUM'): return int(self.tok.value) elif self._accept('LPAREN'): expval = self.expr() self._expect('RPAREN') return expval else: raise SyntaxError('Expected or LPAREN') e = ExpressionTreeBuilder() print(e.parse('2 + 3 * (4 + 5 / 6)')) ('+', 2, ('*', 3, ('+', 4, ('/', 5, 6))))
解析步骤总结(直接上原文吧,感觉自己总结的可能会误导。。。)
对字节字符串进行文本操作
字节字符串支持大多数文本字符串的内置函数注意,将正则表达式应用于字节字符串时,正则表达式也需要是字节形式
一点需要注意的字节字符串文本字符串的差异
1.索引后得到的值为整数而不是字符
2.字节字符串输出会形如’b’xxxxxx”
3.format()不支持字节字符串
4.如果将字节字符串作为文件名,会使文件名编解码失效。
总结:
处理文本,尽量使用文本字符串!
相关文章推荐
- python3-cookbook中一些关于字符串和文本的处理方式
- 「忐」sup() //把字符串显示为上标 20140827 ①文本处理
- python 文本字符串处理
- 第二章 Shell字符串处理之${}
- 字符串对象用于操纵和处理文本字符串
- R语言:文本(字符串)处理与正则表达式
- 【windows核心编程】第二章 字符和字符串处理
- 读书笔记_CLR.via.c#第十四章_字符,字符串和文本处理
- 利用boost库进行字符串与文本处理
- 【windows核心编程】第二章 字符和字符串处理
- 文本处理 - 检查字符串中是否包含某字符集中的字符
- 「斜」italics() //使用斜体显示字符串 20140817 ①文本处理
- 第二章 字符和字符串处理
- [CLR via C#]14. 字符、字符串和文本处理
- 【Linux】 字符串和文本处理工具 grep & sed & awk
- 第二章--(第六单元)--文本处理工具
- R语言:文本(字符串)处理与正则表达式
- 第二章 字符和字符串处理
- Excel文本字符串处理函数left,right,mid,find
- R语言:文本(字符串)处理与正则表达式