前言
归纳使用正则表达式的一些基础语法,使用 AI 生成的代码时,可以了解大致的逻辑,分辨出其中错误,并加以修改。
随着大模型产品的火热,日常写代码的时候也非常依赖 AI 帮忙生成代码。大模型有一个命门就是胡说八道,这也可以说是他能这么厉害的根基。因此,有时候生成的代码是基于不同类型的语言融合之后的结果,如果使用者对编程语言不熟悉,或者对 API 不是很了解,难免会被 AI 带到沟里去。
最近在使用 AI 生成正则表达式的时候,就遇到多次被坑的情况。究其原因还是对正则表达式基础语法的不了解,盲目信从 AI 生成的代码,在不断试错的过程中反而浪费了不少时间。下面对正则表达式的基础语法做一总结,方便后续使用时可以对照排查,方便验证和解决问题。
正则表达式基础知识
正则表达式(Regular Expression,简称Regex)是一种用于字符串搜索和操作的强大工具。
基本字符
- 普通字符 :直接表示其自身,如 
abc匹配 "abc"。 - 特殊字符 :具有特殊含义的字符,如 
.、*、+、?等。 
特殊字符和元字符
| 特殊字符 |     描述      |
|------|-------------|----------|
| .  | 任意单个字符(非换行) |
| ^  | 字符串的开始      |
| $  | 字符串的结束      |
| *  | 前面元素零次或多次   |
| +  | 前面元素一次或多次   |
| ?  | 前面元素零次或一次   |
| `   | `          | 逻辑或,分隔选项 |
| [] | 字符集,匹配括号内任意 |
| () | 分组和捕获       |
量词
| 量词 | 描述 | 
|---|---|
{n} | 
精确出现n次 | 
{n,} | 
至少出现n次 | 
{n,m} | 
出现n到m次 | 
字符类
| 字符类 | 描述 | 
|---|---|
\d | 
数字 [0-9] | 
\D | 
非数字 [^0-9] | 
\w | 
单词字符 [A-Za-z0-9_] | 
\W | 
非单词字符 [^A-Za-z0-9_] | 
\s | 
空白字符 | 
\S | 
非空白字符 | 
修饰符
i:忽略大小写。g:全局匹配。m:多行匹配。
示例
\d{3}-\d{2}-\d{4}:匹配电话号码。^https?://:匹配以 "http" 或 "https" 开始的URL。[a-zA-Z]:匹配任何单个字母。\bhe\w*:匹配以 "he" 开始的单词。
使用正则表达式
下面以正则表达式
            
            
              regex
              
              
            
          
          [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[a-z]{2,6}
        为例(匹配 xxx@yyy 类型的邮箱),看看正则表达式的用法。
            
            
              kotlin
              
              
            
          
          val emailRegex = Regex("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[a-z]{2,6}")
        - matches 完全匹配
 
            
            
              kotlin
              
              
            
          
          val text = "Please contact us at support@example.com or sales@example.com."
println(emailRegex.matches(text))
println(emailRegex.matches("yingkongshi11@gmail.com"))
        
            
            
              shell
              
              
            
          
          false
true
        - find 第一个匹配的项
 
            
            
              kotlin
              
              
            
          
          val matchResult = emailRegex.find(text)
println("matchResult ${matchResult?.value}")
println("group ${matchResult?.groups}")
println(matchResult?.groups?.get(0))
        
            
            
              shell
              
              
            
          
          matchResult support@example.com
group [MatchGroup(value=support@example.com, range=21..39)]
MatchGroup(value=support@example.com, range=21..39)
        - findAll 所有匹配的项
 
            
            
              kotlin
              
              
            
          
          val matchAllResult = emailRegex.findAll(text)
matchAllResult.forEach {
    println(it.value)
    println(it.range)
    println(it.groups)
    println(it.groupValues)
    println("=========================")
}
        
            
            
              shell
              
              
            
          
          // val matchAllResult: Sequence<MatchResult>
support@example.com
21..39
[MatchGroup(value=support@example.com, range=21..39)]
[support@example.com]
=========================
sales@example.com
44..60
[MatchGroup(value=sales@example.com, range=44..60)]
[sales@example.com]
=========================
        需要注意的是
- 
这里 findAll 返回的并不是列表,而是一个 Sequence 。
 - 
find/fidnAll 方法默认都是从第一个字符开始匹配,实际调用时可以基于需要传入合适的位置,koltin 默认参数真是太妙了。
 - 
replace 直接替换
 
            
            
              kotlin
              
              
            
          
          val newResult = emailRegex.replace(text, "O(∩_∩)O哈哈~")
println(newResult)
val newnewResult = emailRegex.replace(text) { r ->
    val sb = StringBuilder()
    sb.append(r.value.uppercase(Locale.getDefault()))
    sb
}
println(newnewResult)
val result1 = emailRegex.replaceFirst(text, "_(:з」∠)_ogo")
println(result1)
        
            
            
              shell
              
              
            
          
          //val newResult: String
Please contact us at O(∩_∩)O哈哈~ or O(∩_∩)O哈哈~.
val newnewResult: String
Please contact us at SUPPORT@EXAMPLE.COM or SALES@EXAMPLE.COM.
//val result1: String
Please contact us at _(:з」∠)_ogo or sales@example.com.
        可以用 regex 对象直接替换符合正则表达式的内容,不必自己获取位置后,在手动进行 replace。
- 在 Java 中使用
 
在 Java 中使用正则表达式会稍微麻烦一些,需要配合 Pattern 和 Matcher 两个类一起使用。
            
            
              java
              
              
            
          
          public class MatterTest {
    public static void main(String[] args) {
        String emailRegex = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[a-z]{2,6}";
        Pattern pattern = Pattern.compile(emailRegex);
        String email2 = "please contact us at example@example.com or bbc@136.com or cnn@kk.com";
        Matcher matcher2 = pattern.matcher(email2);
        while (matcher2.find()) {
            String info = String.format(Locale.getDefault(), "find result %20s,start from %3d,end at %3d", matcher2.group(), matcher2.start(), matcher2.end());
            System.out.println(info);
        }
    }
}
        在 Java 中 find() 返回的是 boolean 值,表示是否找到匹配的内容,因此在包含多个内时,可以进行多个匹配项的查找。
            
            
              shell
              
              
            
          
          total match-1 true
total match-2 false
find result  example@example.com,start from  21,end at  40
find result          bbc@136.com,start from  44,end at  55
find result           cnn@kk.com,start from  59,end at  69
        可以看到这里通过 while 循环返回的结果其实和 findAll 类似,只不过在 kotlin 中,通过 Matcher 对匹配到的结果进行了封装,更加符合面向对象编程的思想。
小结
正则表达式这类非常用的知识,每次用的时候都是查语法。对于一些简单的技巧形成自己的归纳总结,可以更快速的解决问题。同时正则表达式的实现非常灵活,同一个内容可能有不同的表达形式,重点还是表达式的正确和完善,不必过于追求表达式是否足够简洁和短。