Java多行正则表达式行为疑问:multiline模式结果不符求解答
嘿,这种情况我太熟了!Java正则的多行模式(Pattern.MULTILINE)看起来简单,但藏着不少容易踩的坑,你大概率忽略了下面这些关键细节:
多行模式只改
^/$,.还是不匹配换行
这是最常见的误解!很多人以为开了多行模式,.就能匹配换行符,其实完全不是。Pattern.MULTILINE的唯一作用是改变锚点^和$的行为:- 默认模式下:
^匹配整个字符串的开头,$匹配整个字符串的结尾 - 多行模式下:
^匹配每一行的开头,$匹配每一行的结尾
要是想让.匹配换行符,你得同时开启Pattern.DOTALL(也就是所谓的“单行模式”,名字确实反直觉)。举个实际例子:
String testText = "Hello\nWorld"; // 仅开多行模式,.不匹配\n,所以匹配不到跨两行的内容 Pattern wrongPattern = Pattern.compile("Hello.*World", Pattern.MULTILINE); // 同时开多行+DOTALL,.才能匹配换行,成功匹配 Pattern rightPattern = Pattern.compile("Hello.*World", Pattern.MULTILINE | Pattern.DOTALL);- 默认模式下:
换行符的多样性要注意
不同系统的换行符不一样:Unix是\n,Windows是\r\n,旧Mac是\r。多行模式下^/$会自动识别这些换行,但如果你正则里硬写了\n,在Windows环境下可能匹配不到\r\n的换行。建议用\R(Java 8及以上支持)来匹配任何换行符,或者用[\r\n]+兼容所有情况。\A/\Z/\z锚点不受多行模式影响
别把这些全局锚点和^/$搞混!\A永远匹配整个字符串的开头,\Z匹配整个字符串的结尾(不包含最后的换行符),\z匹配字符串的绝对结尾——不管你开不开多行模式,它们的行为都不会变。如果你的正则里用了这些,多行模式对它们起不到任何作用。贪婪/非贪婪量词的影响被放大
有时候问题不是多行模式本身,而是量词的行为。比如你想用^.*$匹配每一行内容,在默认模式下(不开DOTALL),.*会停在每行的换行前;但如果开了DOTALL,贪婪的.*会直接匹配整个字符串,这时候你得换成非贪婪的.*?,或者用[^\\r\n]*来明确限制只匹配一行内的字符。模式标记的正确使用
偶尔会有人犯低级错误:要么忘记把Pattern.MULTILINE传入Pattern.compile(),要么用内联标记(?m)时放错了位置——(?m)必须放在正则表达式的开头,才能对整个正则生效。比如(?m)^foo$是对的,而^foo$(?m)就完全没用。
对照这几点检查你的正则和测试文本,应该就能找到预期和实际结果不符的原因啦!
内容的提问来源于stack exchange,提问作者dotwin

