正则表达式

一. 特殊符号的含义
  1. 量词

    • *:表示前面的模式零次或多次
    • +:表示前面的模式一次或多次
    • ?:表示前面的模式零次或一次
    • {n}:表示前面的模式恰好 n 次
    • {n,}:表示前面的模式至少 n 次
    • {n,m}:表示前面的模式至少 n 次且不超过 m 次
  2. 分组和捕获

    • ( ):用于分组和捕获子表达式
    • (?: ):用于分组但不捕获子表达式
  3. 特殊字符

    • \:转义字符,用于表示特殊字符本身
    • .:表示任意字符(除了换行符\r\n)
    • |:用于指定多个模式的选择
  4. 锚点

    • ^表示字串开头
    • $表示字串结尾
    • \b表示一个单词的边界,即字与空格间的位置
    • \B表示非单词的边界
  5. 字符类

    • [ ]:表示括号内的任意一个字符。例如,`[abc]`` 表示字符 “a”、“b” 或 “c”
    • [^ ]:表示除了括号内的字符以外的任意一个字符。例如,[^abc] 表示除了字符 “a”、“b” 或 “c” 以外的任意字符
    • \w:表示一个字符,该字符是字母,数字,下划线. 等价于[A-Za-z0-9_]
    • \d:表示一个阿拉伯数字. 等价于[0-9]
    • \D: 匹配一个非数字字符。等价于 [^0-9]
    • \s: 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\n\r\t\v]
    • \S: 匹配任何非空白字符。等价于 [^\f\n\r\t\v]

eg: ^[0-9].*[abc]$ 表示"以数字开头,以a或b或c结尾的字串"
[^aeiou]表示字串中,所有不是aeiou的字符
([1-9])([a-z])

二. 正则表达式可以干什么
  1. 测试字符串是否满足正则表达的某个格式
  2. 替换正则表示到的文本
  3. 从字串中提取正则表示的子字符串
三. 定位查询

前面讲了, ( )用于匹配后捕获字串的. 但如果( )里是以下面这4个符号?=、?!、?<=、?<!开头,则表示以括号内的内容作为定位

  1. exp1(?=exp2): 以 exp2 为定位, 匹配 exp2 前面的 exp1
    runoob(?=[\d]+) : 匹配后面是数字的 runoob
    在这里插入图片描述
    [例子]: 匹配一个用’_'分割的字符串, 且最后一个分组没有 ‘_’
    在这里插入图片描述

  2. exp1(?!exp2): 匹配 exp1, 但不要在 exp2 之前
    runoob(?![\d]+) : 匹配后面不是数字的 runoob
    在这里插入图片描述

  3. (?<=exp2)exp1: 匹配 exp2 后面的 exp1
    (?<=[\d]+)runoob: 匹配前面是数字的 runoob
    在这里插入图片描述

  4. (?<!exp2)exp1: 匹配 exp1, 但不要在 exp2 后面
    (?<![\d]+)runoob: p匹配前面不是数字的 runoob
    在这里插入图片描述

四. 反向引用

()匹配到的内容,会被正则引擎放到缓冲区中.缓冲区编号从1开始,最大到99.如果想引用前面缓冲区匹配到的内容, 用 \n 表示即可. 比如 \1 表示第一个缓冲区匹配到的内容.反向引用的最简单的、最有用的应用之一,是匹配出文本中两个相同的相邻单词字串.
eg: 匹配 ‘Is is the cost of of gasoline going up up’ 字串中相同的连续字串.
在这里插入图片描述

eg: 用这则表达式判断一个数是否是素数
首先, 把整数 n 写成 n 个 1. 比如,数字 13 写成 1111111111111. 则表达式中的1+表示至少一个1,所以11+表示2个以上的1. \1表示(11+)匹配到的串,这样(11+)\1表示能被2整除. 后面加上’+'就能表示能被3以上整除了
在这里插入图片描述

五. shell 脚本中的正则
  1. shell 中使用正则匹配, 用 grep -E 匹配. 但 grep 不能很好的支持抓取分组结果. 本想用 sed , 但发现懒惰匹配模式貌似不支持, 改用 perl -pe 获取分组结果.

    # 取 url 参数在aaa,bbb 中任意一个,切后面有 oaid 参数的报文
    # perl -pe 抓取 oaid 参数值:  \1 取第一个分组
    # grep -v '^$' 去掉空行
    oaid=`hdfs dfs -text $fileName | grep -E 'url=(aaa|bbb).*&oaid=(.*?)&.*' | perl -pe 's/.*&oaid=(.*?)&.*/\1/g' | grep -v '^$' | head -1`
    echo "oaid: $oaid"
    
  2. grep 正则匹配引擎的问题

    • grep 默认使用的是基本正则表达式(basic regexp)
      基本正则表达式是一种比较简单的正则表达式语法,它包括一些基本的元字符和特殊字符,如 .、* 和 [] 等等。
    • -E 选项:将使用扩展正则表达式(extended regexp)
      扩展正则表达式是基本正则表达式的扩展,增加了一些新的元字符和特殊字符,如 +、?、| 和 () 等等。
    • -P 选项:将使用 Perl 正则表达式(Perl regexp)
      Perl 正则表达式是一种更加强大和灵活的正则表达式语法,它支持更多的元字符和特殊字符,如 \w、\d、\s 和 (?<=…) 等等
    # ipv4 检查
    cat aaa.log | grep -P "^(\d+\.){3}\d+ -" | wc -l
    
    # ipv6 检查
    cat aaa.log | grep -P "^(\w{0,4}:){1,7}\w{0,4} -"| wc -l
    
六. Java 的 find 匹配和 match 匹配
  • find() 可用于递进地循环匹配子串
  • match() 用于一次性匹配整个字串
/**
 * target: 有时候,我们有一个匹配日志中参数的正则表达式,想对这个正则表达式,获取其中的分组名称。
 *   思路:正则表达式中的分组名称,是使用 (?<分组名称>) 的形式定义的,所以我们可以使用正则表达式来匹配出这些分组名称。
 *   注意:正则表达式中的分组名称,是区分大小写的,不能以数字开头。否则匹配不出来
 *  这里分别用 Matcher 的 match() 和 find() 方法来演示
 *      由于 match() 方法是匹配整个字符串,所以要匹配多个分组名称,正则表达式首先要能匹配整个字串, 然后就要重复多次写出所有匹配 (?<分组名称>) 的表达式
 *      由于 find() 方法是匹配子串,
 * @param regex
 * @return
 */
public static List<String> getNamedGroupListMatch(String regex) {
    Set<String> namedGroups = new LinkedHashSet<>();

    // (?<分组名称>) 表示匹配分组名称,正则为 \(\?<([a-zA-Z]*)> 
    // 字串中有多少个分组名,就要写多少个  \(\?<([a-zA-Z]*)> 
    // 开头加上 .*? 表示最短匹配。匹配最早出现的 \(\?<([a-zA-Z]*)>, 否则只会匹配出最玩出现的 \(\?<([a-zA-Z]*)>
    Matcher m = Pattern.compile(".*?\\(\\?<([a-zA-Z]*)>.*?\\(\\?<([a-zA-Z]*)>.*?\\(\\?<([a-zA-Z]*)>.*?\\(\\?<([a-zA-Z]*)>.*").matcher(regex);
    if(m.matches()){    // matches() 方法对整个输入字串进行匹配,如果匹配成功,则返回 true。所以如果让“只匹配子串的正则表达式”匹配整个字符串,这样会返回 false
        System.out.println(m.group(1));
        System.out.println(m.group(2));
        System.out.println(m.group(3));
        System.out.println(m.group(4));
    }

    return new ArrayList<>(namedGroups);
}

/**
 * 用 find() 方法循环匹配分组名
 * @param regex
 * @return
 */
public static List<String> getNamedGroupList(String regex) {
    Set<String> namedGroups = new LinkedHashSet<>();

    // 用于 find() 匹配的正则表达式,只匹配子串即可。
    Matcher m = Pattern.compile("\\(\\?<([a-zA-Z]*)>").matcher(regex);

    // find()用于循环匹配字串。 find() 尝试匹配下一个满足表达式的子串。如果之前调用过 find(), 则上次调用不会重置 match 的起始位置, 而是接着向后匹配
    while (m.find()) { 
        namedGroups.add(m.group(1));
    }

    return new ArrayList<>(namedGroups);
}

public static void main(String[] args) {
    // (?<分组名>分组表达式)
    // [^ ] 表示除了中括号里的都匹配, 这里是除了空格不匹配都匹配
    // “.*?” 表示最短匹配。 “.*? aaa”表示 aaa 前面的任意字符,最短匹配
    String log = "127.0.0.1 - xxx.com.cn [08/Jul/2024:08:00:00 +0800] \"GET /xx.gif?name=zhang&age=30 HTTP/1.1\" 204 0 \"SohuVideoMobile/9.9.23 (Platform/6)\"";
    String regex = "(?<ip>(?:\\d+\\.){3}\\d+|(?:\\w{0,4}:){1,7}\\w{0,4}) \\S* (?<domain>[^ ]*?) \\[(?<ctime>.*?)\\] \".*\\s\\/mvv\\.gif\\?(?<param>.*?)? HTTP\\/1\\.\\d+\" \\d{3} .*?";
    List<String> namedGroupList = getNamedGroupListMatch(regex);  // 效果同 getNamedGroupList(regex)
    System.out.println(namedGroupList);
}
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值