首页 【2017年整理】Linux入门系列教程(十一)之正则表达式基础

【2017年整理】Linux入门系列教程(十一)之正则表达式基础

举报
开通vip

【2017年整理】Linux入门系列教程(十一)之正则表达式基础【2017年整理】Linux入门系列教程(十一)之正则表达式基础 实验楼官方网站: 正则表达式基础 一、正则表达式 虽然我们这一节的标题是正则表达式,但实际我们这一节的内容并不会主要介绍它,而是只介绍grep,sed,awk这三个命令,而正则表达式作为这三个命令的一种使用方式(命令输出中可以包含正则表达式),正则表达式本身的内容很多,要把它说明清除需要单独一门课程来实现,不过我们这一节中涉及到的相关内容通常也能够满足很多情况下的需求了。 那什么是正则表达式呢, 正则表达式,又称正规表示式、正规表示法、...

【2017年整理】Linux入门系列教程(十一)之正则表达式基础
【2017年整理】Linux入门系列教程(十一)之正则表达式基础 实验楼官方网站: 正则表达式基础 一、正则表达式 虽然我们这一节的标题是正则表达式,但实际我们这一节的内容并不会主要介绍它,而是只介绍grep,sed,awk这三个命令,而正则表达式作为这三个命令的一种使用方式(命令输出中可以包含正则表达式),正则表达式本身的内容很多,要把它说明清除需要单独一门课程来实现,不过我们这一节中涉及到的相关内容通常也能够满足很多情况下的需求了。 那什么是正则表达式呢, 正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个 句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。 许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有regexp、regex,复数有regexps、regexes、regexen。 简单的说形式和功能上正则表达式和我们前面讲的通配符很像,不过它们之间又有很大差别,特别在于一些特殊的匹配字符的含义上,希望初学者注意不要将两者弄混淆。 1.先举个栗子 假设我们有这样一个文本文件,包含"shiyanlou",和"shilouyan"这两个字符串 同样一个表达式 shi* 如果这作为一个正则表达式,它将只能匹配shi,而如果不是作为正则表达式*作为一个通配符,则将同时匹配这两个字符串。这是为什么呢。因为在正则表达式中*表 示匹配前面的子表达式(这里就是它前面一个字符)零次或多次,比如它可以匹配 "sh","shii","shish","shiishi"等等,而作为通 配符表示匹配通配符后面任意多个任意字符,所以它可以匹配"shiyanlou",和"shilouyan"两个字符。 体验完了,下面就来开始正式学习正则表达式吧。 实验楼官方网站: 2.基本语法, 一个正则表达式通常被称为一个模式(pattern),为用来描述或者匹配一系列符合某个句法规则的字符串。 选择 |竖直分隔符表示选择,例如"boy|girl"可以匹配"boy"或者"girl" 数量限定 数量限定除了我们举例用的*,还有+加号,?问号,.点号,如果在一个模式中不加数量限定符则表示出现一次且仅出现一次: , +表示前面的字符必须出现至少一次(1次或多次),例如,"goo+gle",可以匹配 "gooogle","goooogle"等; , ?表示前面的字符最多出现一次(0次或1次),例如,"colou?r",可以匹配"color"或者 "colour"; , *星号代表前面的字符可以不出现,也可以出现一次或者多次(0次、或1次、或多次), 例如,“0*42”可以匹配42、042、0042、00042等。 范围和优先级 ()圆括号可以用来定义模式字符串的范围和优先级,这可以简单的理解为是否将括号内的模式串作为一个整体。例 如,"gr(a|e)y"等价于"gray|grey",(这里体现了优先级,竖直分隔符用于选择a或者e而不是gra和ey)," (grand)?father"匹配father和grandfather(这里体验了范围,?将圆括号内容作为一个整体匹配)。 语法,部分, 正则表达式有多种不同的风格,下面列举一些常用的作为PCRE子集的适用于perl和python编程语言及grep或egrep的正则表达式匹配规则:(由于markdown表格解析的问题,下面的竖直分隔符用全角字符代替,实际使用时请换回半角字符) PCRE(Perl Compatible Regular Expressions中文含义:perl语言兼容正则表达式)是一个用C语言编写的正则表达式函数库,由菲利普.海泽(Philip Hazel)编写。PCRE是一个轻量级的函数库,比Boost之类的正则表达式库小得多。PCRE十分易用,同时功能也很强大,性能超过了POSIX正 则表达式库和一些经典的正则表达式库. 字符 描述 将下一个字符标记为一个特殊字符、或一个原义字符。例如,“n”匹配字符\ “n”。“\n”匹配一个换行符。序列“\\”匹配“\”而“\(”则匹配“(”。 ^ 匹配输入字符串的开始位置。 实验楼官方网站: 字符 描述 $ 匹配输入字符串的结束位置。 n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,{n} 但是能匹配“food”中的两个o。 n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,{n,} 但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于 “o*”。 m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”{n,m} 将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和 两个数之间不能有空格。 匹配前面的子表达式零次或多次。例如,zo*能匹配“z”、“zo”以及“zoo”。* *等价于{0,}。 匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但+ 不能匹配“z”。+等价于{1,}。 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”? 中的“do”。?等价于{0,1}。 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹 配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的 字符串,而默认的贪? 婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?” 将匹配单个“o”,而“o+”将匹配所有“o”。 匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用. 像“(.,\n)”的模式。 匹配pattern并获取这一匹配的子字符串。该子字符串用于向后引用。要匹配圆(pattern) 括号字符,请使用“\(”或“\)”。 匹配x或y。例如,“z,food”能匹配“z”或“food”。“(z,f)ood”则匹x,y 配“zood”或“food”。 字符集合(character class)。匹配所包含的任意一个字符。例如,“[abc]” 可以匹配“plain”中的“a”。其中特殊字符仅有反斜线\保持特殊含义,用于 转义字符。其它特殊字符如星号、加号、各种括号等均作为普通字符。脱字符^[xyz] 如果出现在首位则表示负值字符集合;如果出现在字符串中间就仅作为普通字 符。连字符 - 如果出现在字符串中间表示字符范围描述;如果如果出现在首位 则仅作为普通字符。 排除型(negate)字符集合。匹配未列出的任意字符。例如,“[^abc]”可以匹[^xyz] 配“plain”中的“plin”。 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”[a-z] 范围内的任意小写字母字符。 排除型的字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可[^a-z] 以匹配任何不在“a”到“z”范围内的任意字符。 优先级 实验楼官方网站: 优先级为从上到下从左到右,依次降低 运算符 说明 \ 转义符 (), (?:), (?=), [] 括号和中括号 *、+、?、{n}、{n,}、{n,m} 限定符 ^、$、\任何元字符 定位点和序列 , 选择 更多正则表达式的内容可以参考以下链接: , 正则表达式wiki , 几种正则表达式引擎的语法差异 , 各语言各平台对正则表达式的支持 最后再来一张regex的思导图 实验楼官方网站: 二、grep模式匹配命令 上面空谈了那么多正则表达式的内容也并没有提及具体该如何使用它,实在枯燥,如果说正则表达式是一门武功话,那它也只能算得上一些口诀招式罢了,要把它真正练起来还得需要一些兵器在手才行,这里我们要介绍的grep命令以及后面要讲的sed,awk这些就该算作是这样的兵器了。 1.基本操作 grep命令用于打印输出文本中匹配的模式串,它使用正则表达式作为模式匹配的条件。grep支持三种正则表达式引擎,分别用三个参数指定: 参数 说明 -E POSIX扩展正则表达式,ERE -G POSIX基本正则表达式,BRE -P Perl正则表达式,PCRE 不过在你没学过perl语言的大多数情况下你将只会使用到ERE和BRE,所以我们接下来的内容都不会讨论到PCRE中特有的一些正则表达式语法(它们之间大部分内容是存在交集的,所以你不用担心会遗漏多少重要内容) 在通过grep命令使用正则表达式之前,先介绍一下它的常用参数 参数 说明 -b 将二进制文件作为文本来进行匹配 -c 统计以模式匹配的数目 -i 忽略大小写 -n 显示匹配文本所在行的行号 -v 反选,输出不匹配行的内容 -r 递归匹配查找 -A n n为正整数,表示after的意思,除了列出匹配行之外,还列出后面的n行 -B n n为正整数,表示before的意思,除了列出匹配行之外,还列出前面的n行 --color=auto 将输出中的匹配项设置为自动颜色显示 注:在大多数发行版中是默认设置了grep的颜色的,你可以通过参数指定或修改GREP_COLOR环境变量 实验楼官方网站: 2.使用正则表达式 使用基本正则表达式,BRE 位置 查找/etc/group文件中以"shiyanlou"为开头的行 $ grep 'shiyanlou' /etc/group $ grep '^shiyanlou' /etc/group 数量 # 将匹配以'z'开头以'o'结尾的所有字符串 $ echo 'zero\nzo\nzoo' | grep 'z.*o' # 将匹配以'z'开头以'o'结尾,中间包含一个任意字符的字符串 $ echo 'zero\nzo\nzoo' | grep 'z.o' # 将匹配以'z'开头,以任意多个'o'结尾的字符串 $ echo 'zero\nzo\nzoo' | grep 'zo*' 注意:其中\n为换行符 实验楼官方网站: 选择 # grep默认是区分大小写的,这里将匹配所有的小写字母 $ echo '1234\nabcd' | grep '[a-z]' # 将匹配所有的数字 $ echo '1234\nabcd' | grep '[0-9]' # 将匹配所有的数字 $ echo '1234\nabcd' | grep '[[:digit:]]' # 将匹配所有的小写字母 $ echo '1234\nabcd' | grep '[[:lower:]]' # 将匹配所有的大写字母 $ echo '1234\nabcd' | grep '[[:upper:]]' # 将匹配所有的字母和数字,包括0-9,a-z,A-Z $ echo '1234\nabcd' | grep '[[:alnum:]]' # 将匹配所有的字母 $ echo '1234\nabcd' | grep '[[:alpha:]]' 下面包含完整的特殊符号及说明: 特殊符号 说明 [:alnum:] 代表英文大小写字节及数字,亦即 0-9, A-Z, a-z [:alpha:] 代表任何英文大小写字节,亦即 A-Z, a-z [:blank:] 代表空白键与 [Tab] 按键两者 [:cntrl:] 代表键盘上面的控制按键,亦即包括 CR, LF, Tab, Del.. 等等 [:digit:] 代表数字而已,亦即 0-9 [:graph:] 除了空白字节 (空白键与 [Tab] 按键) 外的其他所有按键 实验楼官方网站: 特殊符号 说明 [:lower:] 代表小写字节,亦即 a-z [:print:] 代表任何可以被列印出来的字节 [:punct:] 代表标点符号 (punctuation symbol),亦即:" ' ? ! ; : # $... [:upper:] 代表大写字节,亦即 A-Z [:space:] 任何会产生空白的字节,包括空白键, [Tab], CR 等等 [:xdigit:] 代表 16 进位的数字类型,因此包括: 0-9, A-F, a-f 的数字与字节 注意:之所以要使用特殊符号,是因为上面的[a-z]不是在所有情况下都管用,这还与主机当前的语系有关,即设置在LANG环境变量的值,zh_CN.UTF-8的话[a-z],即为所有小写字母,其它语系可能是大小写交替的如,"a A b B...z Z",[a-z]中就可能包含大写字母。所以在使用[a-z]时请确保当前语系的影响,使用[:lower:]则不会有这个问题。 # 排除字符 $ echo 'geek|good' | grep '[^o]' 注意:当^放到中括号内为排除字符,否则表示行首 使用扩展正则表达式,ERE 要通过grep使用扩展正则表达式需要加上-E参数,或使用egrep 数量 # 只匹配"zo" $ echo 'zero\nzo\nzoo' | grep -E 'zo{1}' # 匹配以"zo"开头的所有单词 $ echo 'zero\nzo\nzoo' | grep -E 'zo{1,}' 注意:推荐掌握{n,m}即可,+,?,*,这几个不太直观,且容易弄混淆 选择 # 匹配"www.shiyanlou.com"和"www.google.com" $ echo 'www.shiyanlou.com\nwww.baidu.com\nwww.google.com' | grep -E 'www\.(shiyanlou|google)\.com' # 或者匹配不包含"baidu"的内容 $ echo 'www.shiyanlou.com\nwww.baidu.com\nwww.google.com' | grep -E 'www\.[^baidu].*\.com' 注意:因为.号有特殊含义,所以需要转义 实验楼官方网站: 关于正则表达式和grep命令的内容就介绍这么多,下面会介绍两个更强大的工具sed和awk,但同样也正是因为这两个工具的强大,我们的内容无法包含它们的全部,这里将只对基本内容作介绍 三、sed流编辑器 sed工具在man手册里面的全名为"sed - stream editor for filtering and transforming text ",意即,用于过滤和转换文本的流编辑器。 你要相信,在unix/linux的世界里敢称为编辑器的工具,大都非等闲之辈,比如前面的"vi/vim(编辑器之神)","emacs(神的编辑器)","gedit"这些个编辑器。sed与上述的最大不同之处大于它是一个非交互式的编辑器,下面我们就开始介绍sed这个编辑器。 sed常用参数介绍 sed 命令基本格式 sed [参数]... [执行命令] [输入文件]... # 形如: $ sed -i '1s/sad/happy/' test # 表示将test文件中第一行的"sad"替换为"happy" 参数 说明 -n 安静模式,只打印受影响的行,默认打印输入数据的全部内容 用于在脚本中添加多个执行命令一次执行,在命令行中执行多个命令通常-e 不需要加该参数 -f filename 指定执行filename文件中的命令 -r 使用扩展正则表达式,默认为 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 正则表达式 -i 将直接修改输入文件内容,而不是打印到标准输出设备 sed编辑器的执行命令(这里”执行“解释为名词) sed执行命令格式: [n1][,n2]command [n1][~step]command 实验楼官方网站: # 其中一些命令可以在后面加上作用范围,形如: $ sed -i 's/sad/happy/g' test # g表示全局范围 $ sed -i 's/sad/happy/4' test # 4表示指定行中的第四个匹配字符串 其中n1,n2表示输入内容的行号,它们之间为,逗号则表示从n1到n2行,如果为,波浪号则表示从n1开始以step为步进的所有行;command为执行动作,下面为一些常用动作指令 命令 说明 s 行内替换 c 整行替换 a 插入到指定行的后面 i 插入到指定行的前面 p 打印指定行,通常与-n参数配合使用 d 删除指定行 sed操作举例 我们先找一个用于 练习 飞向蓝天的恐龙练习非连续性文本练习把字句和被字句的转换练习呼风唤雨的世纪练习呼风唤雨的世纪课后练习 的文本文件 $ cp /etc/passwd ~ 打印指定行 # 打印2-5行 $ nl passwd | sed -n '2,5p' # 打印奇数行 $ nl passwd | sed -n '1~2p' 实验楼官方网站: 行内替换 # 将输入文本中"shiyanlou" 全局替换为"hehe",并只打印替换的那一行,注意这里不能省略最后的"p"命令 $ sed -n 's/shiyanlou/hehe/gp' passwd 注意: 行内替换可以结合正则表达式使用 行间替换 $ nl passwd | grep "shiyanlou" # 删除第21行 $ sed -n '21c\www.shiyanlou.com' passwd 思考练习 其它几个操作基本类似就不一一举例了,希望你自己练习其他几个命令动作的使用 , 练习1:结合正则表达式做更多练习 , 练习2:参考下面的链接,掌握sed处理文本的基本原理,理解pattern space和hold space概念 , 练习3:基于pattern space和hold space实现将一个文本倒序输出和交换奇数行和偶数 行 关于sed命令就介绍这么多,你如果希望了解更多sed的高级用法,你可以参看如下链接: , sed简明教程 , sed单行脚本快速参考 , sed完全手册 实验楼官方网站: 四、awk文本处理语言 看到上面的标题,你可能会感到惊异,难道我们这里要学习的是一门“语言”么,确切的说,我们是要在这里学习awk文本处理语言,只是我们并不会在这里学习到比较完整的关于awk的内容,还是因为前面的原因,它太强大了,它的应用无处不在,我们无法在这里以简短的文字描述面面俱到,如果你有目标成为一个linux系统管理员,确实想学好awk,你一不用担心,实验楼会在之后陆续上线linux系统管理员的学习路径,里面会有单独的关于正则表达式,awk,sed等相关课程,敬请期待吧。下面的内容,我们就作为一个关于awk的入门体验章节吧,其中会介绍一些awk的常用操作。 1.awk介绍 AWK是一种优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一.其名称得自于它的创 始人Alfred Aho(阿尔佛雷德?艾侯)、Peter Jay Weinberger(彼得?温伯格)和Brian Wilson Kernighan(布莱恩?柯林汉)姓氏的首个字母.AWK程序设计语言,三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程 序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。最简单地说,AWK是一种用于处理文本的编程语言工 具。 在大多数linux发行版上面,实际我们使用的是gawk(GNU awk,awk的GNU版本),在我们的环境中ubuntu上,默认提供的是mawk,不过我们通常可以直接使用awk命令(awk语言的解释器),因为 系统已经为我们创建好了awk指向mawk的符号链接。 $ ll /usr/bin/awk nawk: 在 20 世纪 80 年代中期,对 awk语言进行了更新,并不同程度地使用一种称为 nawk(new awk) 的增强版本对其进行了替换。许多系统中仍然存在着旧的awk 解释器,但通常将其安装为 oawk (old awk) 命令,而 nawk 解释器则安装为主要的 awk 命令,也可以使用 nawk 命令。Dr. Kernighan 仍然在对 nawk 进行维护,与 gawk 一样,它也是开放源代码的,并且可以免费获得; gawk: 是 GNU Project 的awk解释器的开放源代码实现。尽管早期的 GAWK 发行版是旧的 AWK 的替代程序,但不断地对其进行了更新,以包含 NAWK 的特性; mawk 也是awk编程语言的一种解释器,mawk遵循 POSIX 1003.2 (草案 11.3)定义的 AWK 语言,包含了一些没有在AWK 手册中提到的特色,同时 mawk 提供一小部分扩展,另外据说mawk是实现最快的awk 实验楼官方网站: 2.awk的一些基础概念 awk所有的操作都是基于pattern(模式)—action(动作)对来完成的,如下面的形式: $ pattern {action} 你可以看到就如同很多编程语言一样,它将所有的动作操作用一对{}花括号包围起来。其中 pattern通常是是表示用于匹配输入的文本的“关系式”或“正则表达式”,action则是表示匹配后将执行的动作。在一个完整awk操作中,这两者 可以只有其中一个,如果没有pattern则默认匹配输入的全部文本,如果没有action则默认为打印匹配内容到屏幕。 awk处理文本的方式,是将文本分割成一些“字段”,然后再对这些字段进行处理,默认情况下,awk以空格作为一个字段的分割符,不过这不是固定了,你可以任意指定分隔符,下面将告诉你如何做到这一点。 3.awk命令基本格式 awk [-F fs] [-v var=value] [-f prog-file | 'program text'] [file...] 其中-F参数用于预先指定前面提到的字段分隔符(还有其他指定字段的方式) ,-v用于预先为awk程序指定变量,-f参数用于指定awk命令要执行的程序文件,或者在不加-f参数的情况下直接将程序语句放在这里,最后为awk需要处理的文本输入,且可以同时输入多个文本文件。现在我们还是直接来具体体验一下吧。 4.awk操作体验 先用vim新建一个文本文档 $ vim test 包含如下内容: I like linux www.shiyanlou.com 使用awk将文本内容打印到终端 # "quote>" 不用输入 $ awk '{ > print > }' test # 或者写到一行 $ awk '{print}' test 实验楼官方网站: 说明:在这个操作中我是省略了patter,所以awk会默认匹配输入文本的全部内容,然后在"{}"花括号中执行动作,即print打印所有匹配项,这里是全部文本内容 将test的第一行的每个字段单独显示为一行 $ awk '{ > if(NR==1){ > print $1 "\n" $2 "\n" $3 > } else { > print} > }' test # 或者 $ awk '{ > if(NR==1){ > OFS="\n" > print $1, $2, $3 > } else { > print} > }' test 说明:你首先应该注意的是,这里我使用了awk语言的分支选择语句if,它的使用和很多高级语言如C/C++语言基本一致,如果你有这些语言的基础,这里将很好理解。另一个你需要注意的是NR与OFS,这两个是awk内建的变量,NR表示当前读入的记录数,你可以简单的理解为当前处理的行数,OFS表示输出时的字段分隔符,默认为" "空格,如上图所见,我们将字段分隔符设置为\n换行符,所以第一行原本以空格为字段分隔的内容就分别输出到单独一行了。然后是$N其中N为相应的字段号,这也是awk的内建变量,它表示引用相应的字段,因为我们这里第一行只有三个字段,所以只引用到了$3。除此之外另一个这里没有出现的$0,它表示引用当前记录(当前行)的全部内容。 将test的第二行的以点为分段的字段换成以空格为分隔 $ awk -F'.' '{ > if(NR==2){ > print $1 "\t" $2 "\t" $3 > }}' test 实验楼官方网站: # 或者 $ awk '{ > BEGIN{ > FS="." > OFS="\t" # 如果写为一行,两个动作语句之间应该以";"号分开 > } > if(NR==2){ > print $1, $2, $3 > }}' test 说明:这里的-F参数,前面已经介绍过,它是用来预先指定待处理记录的字段分隔符。我们需要注意的是除了指定OFS我们还可以在print 语句中直接打印特殊符号如这里的\t,print打印的非变量内容都需要用""一对引号包围起来。上面另一个版本,展示了实现预先指定变量分隔符的另一种方式,即使用BEGIN,就这个表达式指示了,其后的动作将在所有动作之前执行,这里是FS赋值了新的"."点号代替默认的" "空格 注意:首先说明一点,我们在学习和使用awk的时候应该尽可能将其作为一门程序语言来理解,这样将会使你学习起来更容易,所以初学阶段在练习awk时应该尽量按照我那样的方式分多行按照一般程序语言的换行和缩进来输入,而不是全部写到一行(当然这在你熟练了之后是没有任何问题的)。 6.awk常用的内置变量 变量名 说明 FILENAME 当前输入文件名,若有多个文件,则只表示第一个。如果输入是来自标准输入,则为空字符串 $0 当前记录的内容 $N N表示字段号,最大值为NF变量的值 FS 字段分隔符,由正则表达式表示,默认为" "空格 RS 输入记录分隔符,默认为"\n",即一行为一个记录 NF 当前记录字段数 NR 已经读入的记录数 FNR 当前输入文件的记录数,请注意它与NR的区别 OFS 输出字段分隔符,默认为" "空格 ORS 输出记录分隔符,默认为"\n" 关于awk的内容本课程将只会包含这些内容,如果你想了解更多,请期待后续课程,或者参看一下链接内容: 实验楼官方网站: , awk程序设计语言 , awk简明教程 , awk用户指南 五、linux乐趣多 今天给你推荐一个在线游戏,当然我们主要目的是学习,这个游戏也是有寓教于乐的性质,让你开速学会vim的基础操作,同时又验证你的聪明才智 vim大冒险 本文详细出处实验楼Linux入门系列教程: 实验楼官方网站: 第5章 字符串 我们一直在使用字符串,C#中string关键字的映射实际上指向.NET 基类System.String。System.String是一个功能非常强大且用途非常广泛的基类,但它不是.NET中唯一与字符串相关的类。其他的用来处理字符串的.NET类还有很多,如System.Text 和 System.Text.Regular Expressions命名空间中的类等等。 一、System.String类 System.String是一个类,专门用于存储字符串,允许对字符串进行许多操作。由于这种数据类型非常重要,C#提供了它自己的关键字和相关的语法,以便于使用这个类来处理字符串。 使用运算符重载可以连接字符串: string message1 = "Hello"; //return "Hello" message1 += ", There"; // return "Hello, There " string message2 = message1 + "!"; // return "Hello, There!" C#还允许使用类似于索引器的语法来提取指定的字符: char char4 = message[4]; // returns 'a'. Note the char is zero-indexed 这个类可以完成许多常见的任务,例如替换字符、删除空白和把字母变成大写形式等。可用的方法如表所示。 方 法 作 用 Compare 比较字符串的内容,考虑文化背景(区域),确定某些字符是否相等 CompareOrdinal 与Compare一样,但不考虑文化背景 把多个字符串实例合并为一个实例 Concat 把特定数量的字符从选定的下标复制到数组的一个全新实例中 CopyTo 格式化包含各种值的字符串和如何格式化每个值的说明符 Format 定位字符串中第一次出现某个给定子字符串或字符的位置 IndexOf 定位字符串中第一次出现某个字符或一组字符的位置 IndexOfAny 把一个字符串实例插入到另一个字符串实例的指定索引处 Insert 合并字符串数组,建立一个新字符串 Join LastIndexOf 与IndexOf一样,但定位最后一次出现的位置 LastIndexOfAny 与IndexOfAny,但定位最后一次出现的位置 在字符串的开头,通过添加指定的重复字符填充字符串 PadLeft 在字符串的结尾,通过添加指定的重复字符填充字符串 PadRight 用另一个字符或子字符串替换字符串中给定的字符或子字符串 Replace 在出现给定字符的地方,把字符串拆分为一个子字符串数组 Split 在字符串中获取给定位置的子字符串 Substring 把字符串转换为小写形式 ToLower 把字符串转换为大写形式 ToUpper 删除首尾的空白 Trim 注意:这个表并不完整,但可以让您明白字符串所提供的功能。 1、创建字符串 如上所述,string类是一个功能非常强大的类,它执行许多很有用的方法。但是,string类存在一个问题:重复修改给定的字符串,效率会很低,它实际上是一个不可变的数据类型,一旦对字符串对象进行了初始化,该字符串对象就不能改变了。表面上修改字符串内容的方法和运算符实际上是创建一个新的字符串,如果必要,可以把旧字符串的内容复制到新字符串中。例如,下面的代码: 实验楼官方网站: string greetingText = "Hello from all the guys at Wrox Press. "; greetingText += "We do hope you enjoy this book as much as we enjoyed writing it."; 在执行这段代码时,首先,创建一个System.String类型的对象,并初始化为文本“Hello from all the guys at Wrox Press. ”。注意句号后面有一个空格。此时.NET 运行库会为该字符串分配足够的内存来保存这个 文本(39个字符),再设置变量greetingText,表示这个字符串实例。 从语法上看,下一行代码是把更多的文本添加到字符串中。实际上并非如此,而是创建一个新字符串 实例,给它分配足够的内存,以保存合并起来的文本(共103个字符)。最初的文本“Hello from all the people at Wrox Press.”复制到这个新字符串中,再加上额外的文本“We do hope you enjoy this book as much as we enjoyed writing it.”。然后更新存储在变量greetingText中的地址,使变量正确地指向新的字符串对象。旧 的字符串对象被撤销了引用—— 不再有变量引用它,下一次垃圾收集器清理应用程序中所有未使用的对象 时,就会删除它。 这本身还不坏,但假定要对这个字符串加密,在字母表中,用ASCII码中的字符替代其中的每个字母(标 点符号除外),作为非常简单的加密模式的一部分,就会把该字符串变成“Ifmmp gspn bmm uif hvst bu Xspy Qsftt. Xf ep ipqf zpv fokpz uijt cppl bt nvdi bt xf fokpzfe xsjujoh ju.”。完成这个任务有好几种方式,但最简单、 最高效的一种(假定只使用String类)是使用String. Replace()方法,把字符串中指定的子字符串用另一个子 字符串代替。使用Replace(),加密文本的代码如下所示: string greetingText = "Hello from all the guys at Wrox Press. "; greetingText += "We do hope you enjoy this book as much as we enjoyed writing it."; for(int i = 'z'; i>='a' ; i--) { char old1 = (char)i; char new1 = (char)(i+1); greetingText = greetingText.Replace(old1, new1); } for(int i = 'Z'; i>='A' ; i--) { char old1 = (char)i; char new1 = (char)(i+1); greetingText = greetingText.Replace(old1, new1); } Console.WriteLine("Encoded:\n" + greetingText); 注意: Replace()以一种智能化的方式工作,在某种程度上,它并没有创建一个新字符串,除非要对旧字符串 进行某些改变。原来的字符串包含23个不同的小写字母,和3个不同的大写字母。所以Replace()就分配一 个新字符串,共26次,每个新字符串都包含103个字符。因此加密过程需要在堆上有一个能存储总共2678 个字符的字符串对象,最终将等待被垃圾收集~显然,如果使用字符串进行文字处理,应用程序就会有严 重的性能问题。 为了解决这个问题,Microsoft提供了System.Text.StringBuilder类。StringBuilder不像String那样支持 非常多的方法。在StringBuilder上可以进行的处理仅限于替换和添加或删除字符串中的文本。但是,它的 工作方式非常高效。 在使用String类构造一个字符串时,要给它分配足够的内存来保存字符串,但StringBuilder通常分配 的内存会比需要的更多。开发人员可以选择显式指定StringBuilder要分配多少内存,但如果没有显式指定, 存储单元量在默认情况下就根据StringBuilder初始化时的字符串长度来确定。它有两个主要的属性: ? Length指定字符串的实际长度; 实验楼官方网站: ? Capacity是字符串占据存储单元的最大长度。 对字符串的修改就在赋予StringBuilder实例的存储单元中进行,这就大大提高了添加子字符串和替换单个字符的效率。删除或插入子字符串仍然效率低下,因为这需要移动随后的字符串。只有执行扩展字符串容量的操作,才需要给字符串分配新内存,才可能移动包含的整个字符串。在添加额外的容量时,从经验来看,StringBuilder如果检测到容量超出,且容量没有设置新值,就会使自己的容量翻倍。 例如,如果使用StringBuilder对象构造最初的欢迎字符串,可以编写下面的代码: StringBuilder greetingBuilder = new StringBuilder("Hello from all the guys at Wrox Press. ", 150); greetingBuilder.AppendFormat("We do hope you enjoy this book as much as we enjoyed writing it"); 注意: 为了使用StringBuilder类,需要在代码中引用System.Text。 在这段代码中,为StringBuilder设置的初始容量是150。最好把容量设置为字符串可能的最大长度,确保StringBuilder不需要重新分配内存,因为其容量足够用了。理论上,可以设置尽可能大的数字,足够给该容量传送一个int,但如果实际上给字符串分配20亿个字符的空间(这是StringBuilder实例允许拥有的最大理论空间),系统就可能会没有足够的内存。 在调用Append()方法时,其他文本就放在空的空间中,不需要分配更多的内存。但是,多次替换文本才能获得使用StringBuilder所带来的性能提高。例如,如果要以前面的方式加密文本,就可以执行整个加密过程,无须分配更多的内存: StringBuilder greetingBuilder = new StringBuilder("Hello from all the guys at Wrox Press. ", 150); greetingBuilder.Append("We do hope you enjoy this book as much as we " + "enjoyed writing it"); Console.WriteLine("Not Encoded:\n" + greetingBuilder); for(int i = 'z'; i>='a' ; i--) { char old1 = (char)i; char new1 = (char)(i+1); greetingBuilder = greetingBuilder.Replace(old1, new1); } for(int i = 'Z'; i>='A' ; i–– ) { char old1 = (char)i; char new1 = (char)(i+1); greetingBuilder = greetingBuilder.Replace(old1, new1); } Console.WriteLine("Encoded:\n" + greetingBuilder); 这段代码使用了StringBuilder.Replace()方法,它的功能与String.Replace()一样,但不需要在过程中复制字符串。在上述代码中,为存储字符串而分配的总存储单元是150个字符,用于StringBuilder实例以及在最后一个Console.WriteLine()语句中执行字符串操作期间分配的内存。 一般,使用StringBuilder可以执行字符串的操作,String可以存储字符串或显示最终结果。 2 StringBuilder成员 前面介绍了StringBuilder的一个构造函数,它的参数是一个初始字符串及该字符串的容量。还有几个其他的StringBuilder构造函数,例如,可以只提供一个字符串: 实验楼官方网站: StringBuilder sb = new StringBuilder("Hello"); 或者用给定的容量创建一个空的StringBuilder: StringBuilder sb = new StringBuilder(20); 除了前面介绍的Length 和 Capacity属性外,还有一个只读属性MaxCapacity,它表示对给定的StringBuilder实例的容量限制。在默认情况下,这由int.MaxValue给定(大约20亿,如前所述)。但在构造StringBuilder对象时,也可以把这个值设置为较低的值: // This will both set initial capacity to 100, but the max will be 500. // Hence, this StringBuilder can never grow to more than 500 characters, // otherwise it will raise exception if you try to do that. StringBuilder sb = new StringBuilder(100, 500); 还可以随时显式地设置容量,但如果把这个值设置为低于字符串的当前长度,或者超出了最大容量,就会抛出一个异常: StringBuilder sb = new StringBuilder("Hello"); sb.Capacity = 100; 主要的StringBuilder方法如表所示。 名 称 作 用 给当前字符串添加一个字符串 Append() 添加特定格式的字符串 AppendFormat() 在当前字符串中插入一个子字符串 Insert() 从当前字符串中删除字符 Remove() 在当前字符串中,用某个字符替换另一个字符,或者用当前字符串中Replace() 的一个子字符串替换另一字符串 ToString() 把当前字符串转换为System.String对象(在System.Object中被重写) 其中一些方法还有几种格式的重载方法。 注意: AppendFormat()实际上会在调用Console.WriteLine()时调用,它负责确定所有像{0:D}的格式化表达式应使用什么表达式替代。下一节讨论这个问题。 不能把StringBuilder转换为String(隐式转换和显式转换都不行)。如果要把StringBuilder的内容输出为String,唯一的方式是使用ToString()方法。 前面介绍了StringBuilder类,说明了使用它提高性能的一些方式。注意,这个类并不总能提高性能。StringBuilder类基本上应在处理多个字符串时使用。但如果只是连接两个字符串,使用System.String会比较好。 3、格式化字符串 前面的代码示例中编写了许多类和结构,对这些类和结构执行ToString()方法,都是为了显示给定变量的内容。但是,用户常常希望以各种可能的方式显示变量的内容,在不同的文化或地区背景中有不同的格式。.NET基类System.DateTime就是最明显的一个示例:可以把日期显示为10 June 2008、10 Jun 2008、6/10/08 (美国)、10/6/08 (英国)或10.06.2008 (德国)。 同样,第6章中编写的Vector结构执行Vector.ToString()方法,是为了以(4, 56, 8)格式显示矢量。编写矢量的另一个非常常用的方式是4i + 56j + 8k。如果要使类的用户友好性比较高,就需要使用某些工具以用户希望的方式显示它们的字符串表示。.NET运行库定义了一种标准方式:使用接口IFormattable,本节的主题就是说明如何把这个重要特性添加到类和结构上。 在显示一个变量时,常常需要指定它的格式,此时我们经常调用Console.WriteLine()方法。因此,我们把这个方法作为示例,但这里的讨论适用于格式化字符串的大多数情况。例如,如果要在列表框或文本框 实验楼官方网站: 中显示一个变量的值,一般要使用String.Format()方法来获得该变量的合适字符串表示,但用于请求所需格式的格式说明符与传递给Console.WriteLine()的格式相同,因此本节把Console.WriteLine()作为一个示例来说明。首先看看在为基本类型提供格式字符串时会发生什么,再看看如何把自己的类和结构的格式说明符添加到过程中。 以前在Console.Write()和Console.WriteLine()中使用了格式字符串: double d = 13.45; int i = 45; Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i); 格式字符串本身大都由要显示的文本组成,但只要有要格式化的变量,它在参数列表中的下标就必须放在括号中。在括号中还可以有与该项的格式相关的其他信息,例如可以包含: ? 该项的字符串表示要占用的字符数,这个信息的前面应有一个逗号,负值表示该项应左对齐,正值表示该项应右对齐。如果该项占用的字符数比给定的多,其内容也会完整地显示出来。 ? 格式说明符也可以显示出来。它的前面应有一个冒号,表示应如何格式化该项。例如,把一个数字格式化为货币,或者以科学计数法显示。 数字类型的常见格式说明符: 格 式 应 用 含 义 示 例 符 数字类型 专用场合的货币值 C $4834.50 (USA) ?4834.50 (UK) 只用于整数一般的整数 D 4834 类型 数字类型 科学计数法 E 4.834E+003 数字类型 小数点后的位数固定 F 4384.50 数字类型 一般的数字 G 4384.5 数字类型 通常是专用场合的数字N 4,384.50 (UK/USA) 格式 4 384,50 (欧洲大陆) 数字类型 百分比计数法 P 432,000.00% 只用于整数十六进制格式 X 1120 (如果要显示 类型 0x1120,需要写上0x) 如果要在整数上加上前导0,可以将格式说明符0重复所需的次数。例如,格式说明符0000会把3显示为0003,99显示为0099。 这里不能给出完整的列表,因为其他数据类型有自己的格式说明符。本节的主要目的是说明如何为自己的类定义格式说明符。 1. 字符串的格式化 为了说明如何格式化字符串,看看执行下面的语句会得到什么结果: Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i); Console.WriteLine()只是把参数的完整列表传送给静态方法String.Format(),如果要在字符串中以其他方式格式化这些值,例如显示在一个文本框中,也可以调用这个方法。带有3个参数的WriteLine()重载方法如下: // Likely implementation of Console.WriteLine() public void WriteLine(string format, object arg0, object arg1) { Console.WriteLine(string.Format(format, arg0, arg1)); 实验楼官方网站: } 上面的代码依次调用了带有1个参数的重载方法WriteLine(),仅显示了传递过来的字符串的内容,没有对它进行进一步的格式化。 String.Format()现在需要用对应对象的合适字符串表示来替换每个格式说明符,构造最终的字符串。但是,如前所述,对于这个建立字符串的过程,需要StringBuilder实例,而不是String实例。在这个示例中,StringBuilder实例是用字符串的第一部分(即文本“The double is”)创建和初始化的。然后调用StringBuilder.AppendFormat()方法,传递第一个格式说明符“{0,10:E}”和相应的对象double,把这个对象的字符串表示添加到构造好的字符串中,这个过程会继续重复调用StringBuilder.Append()和StringBuilder.AppendFormat()方法,直到得到了全部格式化好的字符串为止。 下面的内容比较有趣。StringBuilder.AppendFormat()需要指出如何格式化对象,它首先检查对象,确定它是否执行System命名空间中的接口IFormattable。只要试着把这个对象转换为接口,看看转换是否成功即可,或者使用C#关键字is,也能实现此测试。如果测试失败,AppendFormat()只会调用对象的ToString()方法,所有的对象都从System.Object继承了这个方法或重写了该方法。在前面给出的编写各种类和结构的示例中,执行过程都是这样,因为我们编写的类都没有执行这个接口。这就是在前面的章节中,Object.ToString()的重写方法允许在Console.WriteLine()语句中显示类和结构如Vector的原因。 但是,所有预定义的基本数字类型都执行这个接口,对于这些类型,特别是这个示例中的double和int,就不会调用继承自System.Object的基本ToString()方法。为了理解这个过程,需要了解IFormattable接口。 IFormattable只定义了一个方法,该方法也叫作ToString(),它带有两个参数,这与System. Object版本的ToString()不同,它不带参数。下面是IFormattable的定义: interface IFormattable { string ToString(string format, IFormatProvider formatProvider); } 这个ToString()重载方法的第一个参数是一个字符串,它指定要求的格式。换言之,它是字符串的说明符部分,放在字符串的{}中,该参数最初传递给Console.WriteLine()或String. Format()。例如,在本例中,最初的语句如下: Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i); 在计算第一个说明符{0,10:E}时,在double变量d上调用这个重载方法,传递给它的第一个参数是E。StringBuilder.AppendFormat()传递的总是显示在原始字符串的合适格式说明符内冒号后面的文本。 本书不讨论ToString()的第2个参数,它是执行接口IFormatProvider的对象引用。这个接口提供了ToString()在格式化对象时需要考虑的更多信息—— 一般包括文化背景信息(.NET文化背景类似于Windows时区,如果格式化货币或日期,就需要这些信息)。如果直接从源代码中调用这个ToString()重载方法,就需要提供这样一个对象。但StringBuilder. Append Format()为这个参数传递一个空值。如果formatProvider为空,ToString()就要使用系统设置中指定的文化背景信息。 现在回过头来看看本例。第一个要格式化的项是double,对此要求使用指数计数法,格式说明符为E。如前所述,StringBuilder.AppendFormat()方法会建立执行IFormattable接口的对象double,因此要调用带有两个参数的ToString()重载方法,其第一个参数是字符串“E”,第二个参数为空。现在double的这个方法在执行时,会考虑要求的格式和当前的文化背景,以合适的格式返回double的字符串表示。StringBuilder.AppendFormat()则按照需要在返回的字符串中添加前导空格,使之共有10个字符。 下一个要格式化的对象是int,它不需要任何特殊的格式 (格式说明符是{1})。由于没有格式要求,StringBuilder.AppendFormat()会给该格式字符串传递一个空引用,并适当地响应带有两个参数的int.ToString()重载方法。由于没有特殊的格式要求,所以也可以调用不带参数的ToString()方法。 实验楼官方网站:
本文档为【【2017年整理】Linux入门系列教程&#40;十一&#41;之正则表达式基础】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_713593
暂无简介~
格式:doc
大小:278KB
软件:Word
页数:36
分类:
上传时间:2018-03-21
浏览量:10