令人抓狂的代码 - 万能正则表达式.*陷阱
先尝试执行下面的代码:
- println("play \n scala".matches(".*"))
你没看错,打印结果是false。正如键盘布局一样,这是由于一个历史问题导致的。早期的正则表达式工具是基于行处理文本的,所以.匹配的是除换行符以外的任意字符。大多数编程语言在设计正则表达式时沿用了这个传统,但是提供一个选项用于开启"点号匹配换行符"模式。
Java提供了两种方式开启"点号匹配换行符"模式,***种方式是在构建Pattern对象时指定匹配模式:
- val p = Pattern.compile(".*", Pattern.DOTALL)
- println(p.matcher("play\nscala").matches()) // true
另一种方式是在正则表达式开始位置指定嵌入模式修饰符(embedded mode modifier),这也是一种比较通用的方式:
- println("play\nscala".matches("(?s).*")) // true
Pattern.DOTALL和(?s)是等价的。
Java常用的匹配模式有以下几种:
1) Pattern.DOTALL
启用dotall模式。在dotall模式下,模式中的.匹配任意字符,包括换行符。在默认情况下(即未启用dotall模式),.不匹配换行符。等价于修饰符(?s)。
- val p = Pattern.compile(".*", Pattern.DOTALL)
- val m = p.matcher("play\nscala")
- println(m.matches())
- // 输出
- true
2)Pattern.MULTILINE
启用多行匹配模式。在多行匹配模式下,模式中的^和$将逐次匹配每一行的行首和行尾。在默认情况下(即未启用多行匹配模式),^和$将匹配整个字符串的首部和尾部。等价于修饰符(?m)。
- val p = Pattern.compile("^.*$", Pattern.MULTILINE)
- val m = p.matcher("play\nscala")
- while (m.find()) {
- println("find: " + m.group(0))
- }
- // 输出
- find: play
- find: scala
3) Pattern.UNIX_LINES
启用Unix换行模式,使用"\n"标识每一行的末尾,等价于修饰符(?d)。
- val p = Pattern.compile("^.*$", Pattern.UNIX_LINES | Pattern.MULTILINE)
- val m = p.matcher("play\r\nscala")
- while (m.find()) {
- println("find: " + m.group(0).length)
- }
- // 输出
- find: 5
- find: 5
输出的两个结果长度都为5,原因是play末尾还有一个字符\r。
4)Pattern.CASE_INSENSITIVE
启用大小写不敏感匹配,等价于修饰符(?i)。
- val p = Pattern.compile("^S.*A$", Pattern.CASE_INSENSITIVE)
- val m = p.matcher("scala")
- println(m.matches())
- // 输出
- true
5)Pattern.LITERAL
启用字面(literal)模式解析,模式中的元字符和转义字符将按照普通字符解析。
- val p = Pattern.compile(".*", Pattern.LITERAL)
- val m = p.matcher("scala")
- println(m.matches())
- // 输出
- false
6)Pattern.COMMENTS
正则表达式中允许出现空白符(whitespace)和注解(comments),空白符会被忽略,以#开头的注解行也将被忽略,等价于修饰符(?x);
- val p = Pattern.compile(" .* ", Pattern.COMMENTS)
- val m = p.matcher("scala")
- println(m.matches())
- // 输出
- true
注:有些编程语言(例如JavaScript)不支持嵌入模式修饰符(embedded mode modifier),这时可以使用另一种解决方案:
- [\s\S]*
[\s]会匹配任意空白字符,[\S]而则会匹配[\s]不能匹配的任意字符。把这二者组合起来构成[\s\S],这样就会得到一个包含所有字符的字符组,其中也包含了换行符。
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的