6.Java中的正则表达式

Java 中的正则表达式

1. Java 的正则流派

  • java.util.regex 使用传统型 NFA
  • 匹配模式的启用通过各种 method 和 factory 来设定标志位, 或内嵌在表达式中的 (?mods-mods)(?mode-mods:...) 修饰符
    这里写图片描述
字符缩略表示法
元字符说明
\a
[\b]只有在字符组内, \b 才表达退格字符, 其他场合都是表达单词分界符
\e
\f
\n
\r
\t
\0octal要求开头为 0, 后接 1 到 3 位十进制数字
\x##只容许出现两位十六进制数字, 例如 \x7Aber 匹配 zber
\u####只容许出现四位十六进制数字, 例如 \x007Aber 匹配 zber
\cchar区分大小写, 直接对后面字符的十进制编码进行异或操作
字符组及相关结构
元字符说明
[…][^…]字符组, 可包含集合运算符
.点号, 几乎任何字符, 根据模式不同, 有各种含义
\p{Prop} \P{Prop}Unicode 属性和区块
\w[0-9a-zA-Z]
\W[^0-9a-zA-Z]
\d[0-9]
\D[^0-9]
\s[.\t\n\f\r\0x0b]
\S[^.\t\n\f\r\0x0b]
锚点及其他零长度断言
元字符说明
^ \A行/字符串起始位置
$ \z \Z行/字符串结束位置
\G当前匹配的起始位置
\b \B单词分界符, 可以识别 Unicode 字符
(?=…) (?!…) (?<=…) (?环视结构, 逆序环视中的子表达式只能匹配长度有限的文本
注释及模式修饰符
元字符说明
(?mods-mods)模式修饰符, 容许出现的模式: x d s m i u
(?mods-mods:…)模式修饰范围
#注释, 从 # 到行未(只在启用时有效, \x 修饰符或 Pattern.COMMENTS)
\Q…\E文字文本模式
分组及捕获
元字符说明
(…) \1 \2…捕获型括号
(?:…)仅分组的括号
(?>…)固化分组
****
* + ? {n} {n,} {n,m}匹配优先量词
*? +? ?? {n}? {n,}? {n,m}?忽略优先量词
*+ ++ ?? {n}+ {n,}+ {n,m}+占有优先量词
Match 和 Regex 的方法
编译选项(?mode)描述
Pattern.UNIX_LINESd更改点号和 ^ 的匹配
Pattern.DOTALLs点号能匹配任何字符
Pattern.MULTILINEm扩展 ^$ 的匹配规定
Pattern.COMMENTSx宽松排列和注释模式(在字符组内部也有效)
Pattern.CASE_INSENSITIVEi对 ASCII 字符进行不区分大小写的匹配
Pattern.UNICODE_CASEu对 Unicode 字符进行不区分大小写的匹配
Pattern.CANON_EQUnicode 按规则等价匹配模式(不同编码中的同样字符视为相等)
Pattern.LITERAL将 regex 参数作为文字文本, 而非正则表达式

2. 使用 java.util.regex

import java.util.regex.*;

public class JavaRegex {

    public static void main(String[] args) {
        String text  = "this is my 1Test string";
        String regex = "\\d+t";  // 表示 \d+

        // 创建编译对象, 并指定模式(将正则表达式解析并编译为内部形式)
        // Pattern pattern = Pattern.compile("(?di)\\d+t");  // 在正则内指定匹配模式, 与下面的效果一样
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);  // 使用预定义常量

        // 创建匹配器
        Matcher matcher = pattern.matcher(text);

        if(matcher.find()) {
            String matchedText = matcher.group();  // 获取匹配的文本
            int start = matcher.start();           // 获取开始匹配位置
            int end   = matcher.end();             // 获取结束匹配的位置
            System.out.println("matchedText : " + matchedText + ", index : " + start + "~" + end);
        }
    }
}

3. Matcher 对象

  • 将正则表达式和目标字符串联系起来, 可以以多种方式将将应用到目标字符串中, 并查询应用的结果

3.1 常用方法

# 可以设置和修改元素的方法
matcher.pattern()               // 获取当前 pattern 对象
matcher.usePattern(pattern)     // 更改 pattern 对象
matcher.reset(text)             // 更改目标字符串
matcher.region(start, end)      // 指定检索范围, 通过 regionStart() 和 regionEnd() 查看设置的值
matcher.useAnchoringBounds(b)   // 是否将检索范围的边界设置为文本起始位置和文本结束位置, 默认为 true
matcher.useTransparentBounds(b) // 当检索范围是整个目标字符串的一段文本时, 设置为 true 空话各种结构检查超越范围的边界, 检查外部的文本

# 访问只读元素的方法
matcher.groupCount()            // 获取捕获型括号数
matcher.find()                  // 查找下一个匹配
matcher.hitEnd()                // 表示到达字符串结尾的上一次尝试是否成功的标志位
matcher.group(group)            // 获取指定序号的捕获型括号内的值
matcher.toMatchResult()         // 获取匹配结果对象

3.2 应用正则表达式

public static void main(String[] args) {
    String text  = "Mastering Regular Expressions";
    String regex = "\\w+";
    Matcher matcher = Pattern.compile(regex).matcher(text);

    // 获取第一个匹配的文本
    if(matcher.find()) {
        System.out.println(matcher.group());
    }

    // 获取所有匹配的文本
    while(matcher.find()) {
        System.out.println(matcher.group());
    }

    // 从指定位置开始尝试匹配
    if(matcher.find(10)) {
        System.out.println(matcher.group());
    }

    // 判断是否完全匹配, 字符串也支持 matches 方法
    "1234".matches("\\d+");  // true
    "123!".matches("\\d+");  // false
}


/************************************ 查询匹配结果 ************************************/
public static void main(String[] args) {
    String url = "http://regex.info/blog";
    String regex = "(?x)^(https?)://([^/:]+)(?:(\\d+))?";
    Matcher matcher = Pattern.compile(regex).matcher(url);
    if(matcher.find()) {
        System.out.println(matcher.group() + " " + matcher.start() + "~" + matcher.end());    // http://regex.info 0~17
        System.out.println(matcher.group(1) + " " + matcher.start(1) + "~" + matcher.end(1)); // http 0~4
        System.out.println(matcher.group(2) + " " + matcher.start(2) + "~" + matcher.end(2)); // regex.info 7~17
    }
    // 第三个括号可能无法匹配, 特殊处理
    if(matcher.group(3) == null) {
        System.out.println("没有指定指定端口号");
    }else {
        System.out.println("端口号 : " + matcher.group(3));
    }
}


/************************************ 简单查找和替换 ************************************/
public static void main(String[] args) {
    String text  = "Before Java 1.5 was Java 1.4.2 After Java 1.5";
    String regex = "\\bJava\\s*1\\.5\\b*";
    Matcher matcher = Pattern.compile(regex).matcher(text);

    String firstResult = matcher.replaceFirst("Java 5.0"); // 替换第一个匹配到的结果
    System.out.println(firstResult);

    String result = matcher.replaceAll("Java 5.0");      // 替换所有 Java 1.5 为 Java 5.0
    System.out.println(result);
}


/************************************ 高级查找-替换 ************************************/
public static void main(String[] args) {
    Matcher m = Pattern.compile("cat").matcher("one cat two cats in the yard");
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        m.appendReplacement(sb, "dog");  // 存入匹配前的文本和替换文本 dog
    }
    m.appendTail(sb);                    // 找到所有匹配后, 将目标字符串中剩下的文本附加到 sb 中
    System.out.println(sb.toString());   // one dog two dogs in the yard
}


/**
 * 自己实现 replaceAll 方法
 */
public static String replaceAll(Matcher m, String replacement) {
    m.reset();                            // 保证 Matcher 不受之前的影响
    StringBuffer sb = new StringBuffer(); // 生成替换副本
    while(m.find()) {
        m.appendReplacement(sb, replacement);
    }
    m.appendTail(sb);
    return sb.toString();                      
}


/************************************ 原地查找-替换 ************************************/
public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("It's SO very RUDE to shout");
    Matcher matcher = Pattern.compile("\\b[\\p{Lu}\\p{Lt}]+\\b").matcher(sb);      // \p{Lu} 大写字母, \p{Lt} 首字母是字母
    while(matcher.find())
        sb.replace(matcher.start(), matcher.end(), matcher.group().toLowerCase()); // 原地替换, 长度没有变化
    System.out.println(sb.toString());
}


/************************************ 长度变化的替换 ************************************/
public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("It's SO very RUDE to shout");
    Matcher matcher = Pattern.compile("\\b[\\p{Lu}\\p{Lt}]+\\b").matcher(sb); // \p{Lu} 大写字母, \p{Lt} 首字母是字母
    int matchPointer = 0;
    while(matcher.find()) {
        matchPointer = matcher.end();   // 记录本次匹配结束的位置
        sb.replace(matcher.start(), matcher.end(), "<b>" + matcher.group().toLowerCase() + "</b>");
        matchPointer += 7;              // 算上添加的 <b> 和 </b>
    }
    System.out.println(sb.toString());
}


/************************************ 限定范围查找 ************************************/
public static void main(String[] args) throws IOException {
    InputStream is = new URL("http://www.cnblogs.com/xing901022/p/5978989.html").openStream();
    String html    = new String(IOUtils.readFully(is, is.available(), true));

    Matcher mImg = Pattern.compile("(?id)<IMG\\s+(.*?)/?>").matcher(html);  // 查找 imgage tag
    Matcher mAlt = Pattern.compile("(?id)\\b ALT \\S* =").matcher(html);    // 查找 alt 属性

    // 循环查找每一个 image tag
    while(mImg.find()) {
        // 将查找范围局限在刚刚找到的 tag 中
        mAlt.region(mImg.start(1), mImg.end(1));
        if(!mAlt.find()) {
            System.out.println(mImg.group());
        }
    }
}


/************************************ 边界透明 ************************************/
public static void main(String[] args) throws IOException {
    String text  = "Madagascar is best seen by car or bike.";
    Matcher matcher = Pattern.compile("\\bcar\\b").matcher(text);
    matcher.region(7, text.length());   // 设置检索范围
    matcher.useTransparentBounds(true); // 设置边界透明, 引擎可以感知到第一个 car前面有个 s, 从而 \b 无法匹配
    matcher.find();
    System.out.println(matcher.start());
}

4. Pattern 的其它方法

pattern.split(input);    // 切割字符串
pattern.pattern();       // 返回用于创建本 pattern 正则表达式的字符串
pattern.toString();      // 等价于 pattern 方法
pattern.flags();         // 所有创建时传递的 compile factory 的 flag 参数
pattern.quote(s);        // 返回字符串对应的正则文字字符串
pattern.matches(regex, input)  // 判断正则表达式能否匹配文本, 等价于 Pattern.compile(regex).matcher(text).matches();


/************************************ split 方法说明 ************************************/
Pattern.compile(":").split(":xx:");
Pattern.compile(":").split(":xx:", -1);  // 小于 0, 保留数组结尾的空元素
Pattern.compile(":").split(":xx:", 0);   // 等于 0, 默认值, 不会保留结尾的空元素
Pattern.compile(":").split(":xx:", 1);   // 大于 0, 返回的数组最多包括 limit 个元素

5. 拓展示例

为 Image Tag 添加宽度和高度属性

public static void main(String[] args) throws IOException {
    StringBuffer html = new StringBuffer("<img src=https://www.baidu.com/img/bd_logo1.png /><img src=https://www.baidu.com/img/bd_logo1.png />");
    Matcher mImg    = Pattern.compile("(?id)<img\\s(.*?)/?>").matcher(html);
    Matcher mSrc    = Pattern.compile("(?ix)\\bSrc=(\\S+)").matcher(html);
    Matcher mWidth  = Pattern.compile("(?ix)\\bWidth=(\\S+)").matcher(html);
    Matcher mHeight = Pattern.compile("(?ix)\\bHeight=(\\S+)").matcher(html);

    int imgMatchPointer = 0;
    while(mImg.find(imgMatchPointer)) {
        imgMatchPointer   = mImg.end();        // 记录本次匹配结束的位置
        boolean hasSrc    = mSrc.region(mImg.start(1), mImg.end(1)).find();
        boolean hasWidth  = mWidth.region(mImg.start(1), mImg.end(1)).find();
        boolean hasHeight = mHeight.region(mImg.start(1), mImg.end(1)).find();

        if(hasSrc && (!hasWidth || !hasHeight)) {
            BufferedImage img = ImageIO.read(new URL(mSrc.group(1)));
            String size;
            if(hasWidth) {
                size = "height='" + ((int)(Integer.parseInt(mWidth.group(1)) * img.getHeight() / img.getWidth()) + "' ");
            }else if(hasHeight) {
                size = "width=" + ((int)(Integer.parseInt(mWidth.group(1)) * img.getWidth() / img.getHeight()) + "' ");
            }else {
                size = "width=" + img.getWidth() + "' " +  "height='" + img.getHeight() + "' ";
            }
            html.insert(mImg.start(1), size);
            imgMatchPointer += size.length();   // 更新匹配结束的位置
        }
    }
    System.out.println(html.toString());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值