zoukankan      html  css  js  c++  java
  • Regexp

    Unix 不会阻止用户干蠢事,因为那样也会妨碍用户做聪明的事。

    Linux grep,sed 常用语文本的搜索和处理,所以下面也基本通过这两个命令,练习regexp patterns.

    基础

    • grep -n "string" data.txt #最简单的正则使用,就是查询string字符串,-n 打印行号

    量词-出现的次数

    - +     出现次数>0, => {1,}
    - *     出现次数>=0. => {0,}
    - ?     出现次数0,1或指明一个非贪婪限定符 => {0,1}
    - {n}   匹配确定的 n 次。非负整数
    - {n,}  至少匹配n 次。
    - {n,m} 最少匹配 n 次且最多匹配 m 次。注意在逗号和两个数之间不能有空格。
    

    eg:

    1. grep -P "a+" data.txt        #a出现的次数>1, -P 采用perl regexp模式,不然无法使用d
    2. grep -P "a*" data.txt        #a出现的次数>0
    3. grep -P "a?" data.txt        #a出现的次数0,1
    4. grep -P "a{2}" data.txt      #a只连续2次的
    5. grep -P "a{2,}" data.txt     #a至少连续2次的
    5. grep -P "a{2,4}" data.txt    #a至少连续2次的, 最多4次
    

    Note: *+?量词都是贪婪的,因为它们会尽可能多的匹配内容(后面有记录)。

    括号表达式() [] {}

    eg:

    1. grep -P "(wrod)" data.txt        #查询word
    2. grep -P "(wrod){2}" data.txt     #查询word连续出现2次
    3. grep -P "(wrod1|word2)" data.txt #查询word1 or word2, `|` 指明两项之间的一个选择。
    4. grep -P "[asd|jkl]" data.txt     #查询a,s,k,|,j,k,l字符,注意这里|不是或的意思。
    5. grep -P "[02468]" data.txt       #只匹配0,2,4,6,8
    6. grep -P "[d]" data.txt          #[d] => [0-9] => [0123456789]
    7. grep -P "[D]" data.txt          #[D] => `[^0-9]` => `[^d]` 匹配非数字内容,^在[]起始位置,表示不匹配[]内部的内容
    8. grep -P "[w]" data.txt          #匹配单词字符,也就是英文单词的组成成分,字母数字下划线 => `[^A-Za-x0-9]`, `[W]`就是非单词字符 => `[^_A-Za-z0-9]`
    

    或,并集和差集

    eg:

    1. echo "2345678901234563452" | grep -P "[0-3,6-9]"    #0-3 || 6-9
    1. echo "2345678901234563452" | grep -P "[0-36-9]"     #也可以
    2. man ls | grep -P "[a-f,x-y]"    #并集
    3. 差集待续...
    

    定位符

    - ^  字符串的开始,对于文本来说,一行内容就是一个字符串。
    - $  字符串的结束
    -  匹配一个字边界,即字与空格间的位置。
    - B 非字边界匹配。
    
    对于 ^ and $, 使用vim的就非常熟悉了。
    

    Note: 不能将限定符与定位符一起使用。

    eg:

    1. grep -P "^a" data.txt        #字符串的以a开头
    2. grep -P "^abc" data.txt      #字符串的以abc开头 => `^(abc)`
    3. grep -P "^(abc|def)" data.txt#字符串的以abc 或 def开头
    4. grep -P "(abc|def)$" data.txt#字符串的以abc 或 def结尾
    5. grep -P " $" data.txt        #字符串的以 空格 结尾
    

    ^$是针对于一个字符串,一个字符串中含有很多单词, B就是对单词进行匹配的。

    1. grep -P "a" data.txt       #以a开头的单词,a,as,and,also,...
    2. grep -P "a" data.txt       #以a结尾的单词, a,data,meta ....
    3. grep -P "s[w]+e" data.txt  #以s开头,e结尾的单词,some,single,style,....
    4. grep -P "Bee" data.txt      #非单词边界,也就是单词中。 单词中含有ee的,three,needed,See,green...单词中,就不分单词的开始和结束了 => `eeB`
    5. grep -P "[1][02468]" data.txt                # 10-19的偶数
    6. grep -P "[02468]|[1-9][02468]" data.txt  # 0-99的偶数
    

    其他字符

    • . 匹配除 " " 之外的任何单个字符。要匹配包括 ' ' 在内的任何字符,请使用像"(.| )"的模式。

    eg:

    1. grep -P ".*" data.txt    # => `[^
    ]` => `[^
    
    ]`
    2. grep -P ".+" data.txt 
    

    元字符的字面值,一方面可以使用进行转义为普通字符,另一方面可以用QE把元字符包括起来,eg:Q$E,表达式将匹配$的字面值。

    非打印字符

    • a 报警字符

    • [] 退格字符

    • cx 匹配由x指明的控制字符。
      cM 匹配一个 Control-M 或回车符。
      x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。

    • f 匹配一个换页符。等价于 x0c 和 cL。

    • 匹配一个换行符。等价于 x0a 和 cJ。

    • 匹配一个回车符。等价于 x0d 和 cM。

    • 匹配一个制表符。等价于 x09 和 cI。

    • s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ f v]。

    • S 匹配任何非空白字符。等价于 [^ f v]。

    • v 匹配一个垂直制表符。等价于 x0b 和 cK。

    • V 非垂直制表符

    • h 水平空白符

    • H 非水平空白符

    • 空字符

    进制匹配

    • ml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
    • xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'x41' 匹配 "A"。'x041' 则等价于 'x04' & "1"。正则表达式中可以使用 ASCII 编码。
    • un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, u00A9 匹配版权符号 (?)。

    多重含义

    • um 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)1' 匹配两个连续的相同字符。
    • 标识一个八进制转义值或一个向后引用。如果 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
    • m 标识一个八进制转义值或一个向后引用。如果 m 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 m 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 m 将匹配八进制转义值 nm。

    运算符优先级

    从左到右进行计算,并遵循优先级顺序.不同优先级的运算先高后低。运算符的优先级顺序(高=>低)

     => (), (?:), (?=), [] => *, +, ?, {n}, {n,}, {n,m} => ^, $, 任何元字符、任何字符 定位点和序列(即:位置和顺序)=> | 
    

    中级

    主要是一些概念的理解。

    量词的贪心,懒惰和占有

    量词自身是贪心的。贪心的量词会首先匹配整个字符串。尝试匹配时,它会选定尽可能多的内容,也就是整个输入。量词首次尝试匹配整个字符串,如果失败,则回退一个字符后再次尝试。这就是回溯。它会每次回退一个字符,直到找到匹配的内容或没有字符可尝试为止。同时,它还记录所有的行为,因此他对资源的消耗最大。

    懒惰,或者勉强的量词从目标的起始位置开始尝试寻找匹配,每次检查字符串的一个字符,寻找要匹配的内容。最后,会尝试匹配整个字符串。设置一个量词为懒惰的,在普通量词后添加问好?即可。

    占有量词会覆盖整个目标然后尝试寻找匹配的内容,只尝试一次,不会回溯。在普通量词后面添加一个加号+即可。

    量词的贪心

    尽可能多的匹配,这是量词本身的一个特性,不多说了。

    懒惰量词

    量词的贪心是尽可能多的匹配,也就是选择最大的匹配次数进行匹配;而量词的懒惰,恰恰相反,就是选择最小的匹配次数进行匹配;

    1. grep -P "a{3,6}" data.txt        # aaa,baaab,aaaa,aaaaaa...都会被匹配,量词本身的贪心,匹配的结果尽可能的长;
    2. grep -P "a{3,6}?" data.txt        # aaa,baaab,..匹配的结果尽可能的短,对于aaaaa的字符串,只匹配前3个a,后两个丢弃。
    

    eg: 查找< and > 之间的内容

    1.echo "<h1>abcdefg</h1>" | grep -P "<.*>"        #返回结果:<h1>abcdefg</h1>尽可能多的贪婪匹配
    2.echo "<h1>abcdefg</h1>" | grep -P "<.*?>"       #返回结果:<h1>,</h1>, 匹配到了两个
    
    - ?? 懒惰匹配0 or 1次, 选择的匹配次数为 0
    - +? 懒惰匹配1 or more,选择的匹配次数为1
    - *? 懒惰匹配0 or more,选择的匹配次数为0
    - {n}? 懒惰匹配n
    - {n,}? 懒惰匹配n or more, 选择的匹配次数为n
    - {n,m}? 懒惰匹配n-m,选择的匹配次数为最小的n次 
    

    干活偷懒,总是选择最小的次数进行匹配,这就是懒惰。

    占有量词 (栗子暂没有想到,待续...)

    很像贪心式匹配,会选定尽可能多的内容。但是与贪心式匹配不同的是不进行回溯。不会放弃所找到的内容,很自私,所以把它称为占有式,紧紧抱住自己所选的内容,一点也不放弃。优点是速度快,因为无需回溯。当然,匹配失败的话也很快。

    0.*+ 所有0都被匹配了
    0.*+0 没有匹配,原因就是没有回溯。一下子就选定了所有的输入,不再回头查看。一下子没有在结尾找到0,也不知道该从哪里开始找。
    

    在量词后面添加+,即可转变为占用。

    捕获分组和后向引用

    正则表达式两边添加圆括号,将把匹配放到一个组中,组中的每个子匹配都按照在组中从左到右出现的顺序存储。 组中每个匹配的编号从 1 开始,最多可存储 99 个子表达式。 每个子表达式都可以使用 的形式访问。

    eg:

    1. grep -P "(d)d1" data.txt        # 匹配:101,111,202,131...
    
    - (d) 匹配一个数字,并将其捕获
    - d   匹配第二个数字,可是没有进行捕获
    - 1   对捕获的数字,进行后向引用
    
    2. man ls | grep -P "(w)w1"        # 匹配: SIS,non,ifi,ses,www,ewe....
    3. man ls | grep -P "(w)w{2,3}1"   # 匹配:indi,extension,text,stops,rever...
    4. man ls | grep -P "(w)w(w)12"  # 匹配:enses
    

    后向引用将通用资源指示符 (URI) 分解为其组件。假定您想将下面的 URI 分解为协议(ftp、http 等等)、域地址和页/路径:

    var str = "http://www.runoob.com:80/html/html-tutorial.html";
    var patt1 = /(w+)://([^/:]+)(:d*)?([^# ]*)/;
    arr = str.match(patt1);
    for (var i = 0; i < arr.length ; i++) {
        console.log(arr[i]);
    }
    

    output

    http://www.runoob.com:80/html/html-tutorial.html
    http
    www.runoob.com
    :80
    /html/html-tutorial.html
    
    • 第一个括号子表达式捕获 Web 地址的协议部分。该子表达式匹配在冒号和两个正斜杠前面的任何单词。
    • 第二个括号子表达式捕获地址的域地址部分。子表达式匹配 / 和 : 之外的一个或多个字符。
    • 第三个括号子表达式捕获端口号(如果指定了的话)。该子表达式匹配冒号后面的零个或多个数字。只能重复一次该子表达式。
    • 最后,第四个括号子表达式捕获 Web 地址指定的路径和 / 或页信息。该子表达式能匹配不包括 # 或空格字符的任何字符序列。

    第一个括号子表达式包含"http"
    第二个括号子表达式包含"www.runoob.com"
    第三个括号子表达式包含":80"
    第四个括号子表达式包含"/html/html-tutorial.html"

    非捕获分组:不会将其内容存储在内存中。所以会带来较高的性能。

    可以使用非捕获元字符 ?:、?= 或 ?! 来重写捕获,忽略对相关匹配的保存。

    捕获分组 => 非捕获分组

    eg:

    (the|The|THE) => (?:the|The|THE) =>不区分大小写 (?i)(?:the) or (?:(?i)the)  The better way: (?i:the)
    
    • (?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
    • (?=pattern) 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
    • (?!pattern) 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。

    原子分组

    有的正则表达式引擎是进行回溯操作,回溯操作会尝试每一种可能性,消耗时间和计算资源,有时候会占用大量时间,可能产生巨大的负面效应。通过原子分组(?>the)可以关闭回溯操作,可是只针对原子分组内的部分,不针对整个表达式。

    环视

    一种非捕获分组,根据某个模式之前或之后的内容匹配其他模式。又叫做零宽度断言,环视分为 正前瞻,反前瞻,正后顾,反后顾

    Perl语言有这种功能,grep测试失败,待续...

    正前瞻:要匹配keyword,且要求紧随其后的单词是anotherword,可以使用正前瞻 (?i)keyword (?=anotherword)
    反前瞻: 对正前瞻的取反操作,匹配某个模式的时候,需要在它后面找不到含有给定前瞻模式的内容。eg: (?i)keyword (?!anotherword)
    正后顾: 会查看左边的内容,与正前瞻方向相反。eg: `(?i)(?<keyword) anotherword`
    反后顾: 会查看某个模式在从左到右的文本流的后面没有出现。eg:`(?i)(?<!keyword) anotherword`
    

    grep的正则基础

    统计单词a|A出现的次数

    1. man ls | grep -Eoc "(a|A)"    #101
    
    -E 使用扩展的正则表达式
    -o 只显示一行中与模式相匹配的那部分
    -c 只返回结果的数量
    
    2. man ls | grep -Eo "(a|A)" | wc -l #263
    

    第二条命令的结果大一些。因为-c给出的是匹配的行的数目,但是一行中可能有多个单词匹配。
    而第二条命令,指定的单词不管是哪一种形式出现,每次出现都会最为单独的一行统计。所以,结果就大一些。

    - (THE|The|the) 三个子模式的匹配互不影响
    - (t|T)h(e|eir) 后面子模式(e|eir)的匹配依赖于前面子模式(t|T)的匹配。
    

    sed的正则基础使用

    sed替换

    1. echo Hello | sed 's/Hello/Goodbye/'    #单引号
    

    sed 的s表示,将Hello替换成Goodbye, 就相当于vim命令下的:0,$s/Hello/Goodbye/gc

    sed -n 's/^/<h1>/;s/$/</h1>/p;' data.txt    # 符合命令,这样尝试失败
    
    - sed默认的操作是直接复制每行输入并输出。-n选项覆盖默认操作。这样只让正则表达式影响第一行。
    - 's/^/<h1>/' 在行的开头添加 '<h1>'
    - 分号;用于分割命令
    - 's/$/</h1>/' 在行的末尾添加...
    - 命令p会打印受影响的一行
    - q结束程序
    

    或者使用-e分别引导命令

    3. sed -ne 's/^/<h1>/' -e 's/$/</h1>/p' -e 'q' data.txt    #注意/的转义/
    

    或者,将命令写入文件:

    #!/usr/bin/sed
    
    s/^/<h1>/
    s/$/</h1>/
    q
    
    sed -f h1.sed data.txt
    
    1. 用br标签代替空行来分割内容
    sed -E 's/^$/<br/>/' data.txt
    
    1. 后向引用

    eg:

    1. echo hello | sed 's/(hello)/<title>1</title>/'
    2. echo hello | sed 's/(hello)/<h1>1</h1><title>1</title>/'
    

    sed 插入与追加

    sed中的插入命令i允许你在文件或字符串中的某个位置之前插入文本;
    sed中的追加命令a允许你在文件或字符串之后添加文本;

    1. sed '1 i	his is first line' data.txt    #在第一行的位置,插入
    2. sed -i '5i	his is a new line' data.txt  #在第五行的位置插入
    

    追加标签

    1. sed '$aappend a new line' data.txt
    

    sed捕获分组和后向引用

    1. echo onetwothree | sed -E 's/(one)(two)/2 1 /'    #two one three
    

    -E 选项调用ERE扩展的正则表达式
    -n 覆盖打印每一行的默认设置

    sed 处理罗马数字

    1. sed -En 's/^(ARGUMENT.|I{0,3}V?I{0,2}.)$/<h2>1</h2>/' data.txt
    

    sed处理特定段落

    1. sed -En '5s/^([A-Z].*)$/<p>1</p>/' data.txt
    

    仅仅处理第5行的内容。这个和vim的替换有相同的语法。哈哈

    sed 处理多行内容

    2. sed -E '9s/^[ ]*(.*)/ <p>1<br/>/;10,832s/^([ ]{5,7}.*)/1<br/>/;833s/^(.*)/1</p>' data.txt
    

    常用正则

    runoob

    缩写

    • ASCII American Standard Code for Information Interchange.美国信息交换标准代码
    • POSIX Portable Operating System Interface 可移植操作系统接口

    Reference

    runoob.com
    POSIX Basic Regular Expressions
    wikipedia
    regex online tool
    sed comand


  • 相关阅读:
    组成原理(九):数值表示
    组成原理(八):DMA方式
    组成原理(七):程序查询及中断方式
    组成原理(六):I/O概述
    组成原理(五):缓存,辅存
    组成原理(四):存储器扩展,汉明码,访存提速
    组成原理(三):存储器概述,RAM,ROM
    组成原理(二):总线
    组成原理(一):入门
    django_python3_pycharm_"font&&size"
  • 原文地址:https://www.cnblogs.com/zi-xing/p/7747444.html
Copyright © 2011-2022 走看看