вводить
- Регулярные выражения обычно используются для сопоставления строк, поиска строк и замены строк. Не стоит недооценивать их роль, гибкое использование регулярных выражений для обработки строк в работе и учебе может значительно повысить эффективность, а удовольствие от программирования так просто.
- Может быть страшно сразу давать кучу правил сопоставления.Ниже будет объяснено использование регулярных выражений от поверхностного к глубокому.С практическими примерами.
Понимание сопоставления регулярных выражений на простом примере
public class Demo1 {
public static void main(String[] args) {
//字符串abc匹配正则表达式"...", 其中"."表示一个字符
//"..."表示三个字符
System.out.println("abc".matches("..."));
System.out.println("abcd".matches("..."));
}
}
//输出结果
true
false
-
String
класс имеетmatches(String regex)
Метод, возвращаемое логическое значение, используется, чтобы определить, соответствует ли эта строка заданному регулярному выражению.
- В этом примере мы даем регулярное выражение как
...
, каждый, из которых.
Представляет один символ, все регулярное выражение означает три символа, очевидно, при сопоставленииabc
когда результатtrue
, Спичкиabcd
когда результатfalse
.
Поддержка регулярных выражений в Java (есть соответствующие реализации для разных языков)
- существует
java.util.regex
В пакете есть два класса для регулярных выражений, один из нихMatcher
класс, другойPattern
Типичное использование этих двух классов приведено в официальной документации Java, код выглядит следующим образом:
public class Demo2 {
public static void main(String[] args) {
//[a-z]表示a~z之间的任何一个字符, {3}表示3个字符, 意思是匹配一个长度为3, 并且每个字符属于a~z的字符串
Pattern p = Pattern.compile("[a-z]{3}");
Matcher m = p.matcher("abc");
System.out.println(m.matches());
}
}
//输出结果
true
- Если вы хотите углубиться в принцип работы регулярных выражений, это будет связано с такими знаниями, как автоматы в принципе компиляции, и не будет здесь описываться, а для облегчения понимания будет описано более ярким языком здесь .
-
Pattern
Можно понимать как шаблон, строка должна соответствовать определенному шаблону, напримерDemo2
, шаблон, который мы определяем,一个长度为3的字符串, 其中每个字符必须是a~z中的一个
.
- мы видим создание
Pattern
объект вызывается, когдаPattern
в классеcompile
метод, то есть объект шаблона получается после компиляции регулярного выражения, которое мы передаем. Этот скомпилированный объект шаблона значительно повысит эффективность использования регулярного выражения, и в качестве константы его можно безопасно использовать для нескольких потоков. одновременно.
-
Matcher
Его можно понимать как результат сопоставления определенной строки с шаблоном.После того, как строка соответствует определенному шаблону, может быть много результатов, которые будут объяснены в следующих примерах.
- Наконец, когда мы звоним
m.matches()
возвращает результат, который соответствует полной строке с шаблоном
- Приведенные выше три строки кода можно сократить до одной строки кода.
System.out.println("abc".matches("[a-z]{3}"));
- Но если регулярному выражению необходимо многократно сопоставляться, этот способ написания менее эффективен.
Предварительное понимание + * ?
- Прежде чем вступить, первое, что нужно отметить, это то, что конкретное значение регулярных выражений не нужно запоминать, значение каждого символа указано в официальном документе Java.
Pattern
Подробные определения есть в описании класса или на сайте, конечно лучше с ним ознакомиться.
public class Demo3 {
/**
* 为了省略每次写打印语句, 这里把输出语句封装起来
* @param o
*/
private static void p(Object o){
System.out.println(o);
}
/**
* . Any character (may or may not match line terminators), 任意字符
* X? X, once or not at all 零个或一个
* X* X, zero or more times 零个或多个
* X+ X, one or more times 一个或多个
* X{n} X, exactly n times x出现n次
* X{n,} X, at least n times x出现至少n次
* X{n,m} X, at least n but not more than m times 出现n~m次
* @param args
*/
public static void main(String[] args) {
p("a".matches("."));
p("aa".matches("aa"));
p("aaaa".matches("a*"));
p("aaaa".matches("a+"));
p("".matches("a*"));
p("a".matches("a?"));
// \d A digit: [0-9], 表示数字, 但是在java中对"\"这个符号需要使用\进行转义, 所以出现\\d
p("2345".matches("\\d{2,5}"));
// \\.用于匹配"."
p("192.168.0.123".matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"));
// [0-2]指必须是0~2中的一个数字
p("192".matches("[0-2][0-9][0-9]"));
}
}
//输出结果
//全为true
Сфера
-
[]
Используется для описания ряда символов, вот несколько примеров
public class Demo4 {
private static void p(Object o){
System.out.println(o);
}
public static void main(String[] args) {
//[abc]指abc中的其中一个字母
p("a".matches("[abc]"));
//[^abc]指除了abc之外的字符
p("1".matches("[^abc]"));
//a~z或A~Z的字符, 以下三个均是或的写法
p("A".matches("[a-zA-Z]"));
p("A".matches("[a-z|A-Z]"));
p("A".matches("[a-z[A-Z]]"));
//[A-Z&&[REQ]]指A~Z中并且属于REQ其中之一的字符
p("R".matches("[A-Z&&[REQ]]"));
}
}
//输出结果
全部为true
Распознать \s \w \d \
- Далее представлены регулярные выражения из цифр и букв, которые являются наиболее часто используемыми символами в программировании.
о\
- Здесь мы сосредоточимся на самом непонятном
\
, В строке в Java, если вы хотите использовать специальные символы, вы должны добавить к ней префикс, добавив\
Сбежать.
- В качестве примера рассмотрим эту строку
"老师大声说:"同学们,快交作业!""
, Если мы не экранировали символ, конец открывающей двойной кавычки должен быть в说:"
Здесь, но нам нужно использовать двойные кавычки в нашей строке, поэтому нам нужно использовать escape-символы
- Строка после использования управляющих символов
"老师大声说:\"同学们,快交作业!\""
, чтобы можно было правильно определить наше первоначальное намерение.
- Точно так же, если мы хотим использовать в строке
\
, также должен предшествовать\
, представленный в строке как"\\"
- Итак, как выразить в регулярном выражении соответствие
\
Что ж, ответ"\\\\"
.
- Рассмотрим отдельно: Так как регулярное выражение выражает
\
Его также нужно экранировать, поэтому предыдущий\\
Представляет escape-символ в регулярном выражении\
, Назад\\
значит в регулярном выражении\
сами по себе, взятые вместе для представления в регулярном выражении\
.
- Если это кажется немного запутанным, см. код ниже.
public class Demo5 {
private static void p(Object o){
System.out.println(o);
}
public static void main(String[] args) {
/**
* \d A digit: [0-9] 数字
* \D A non-digit: [^0-9] 非数字
* \s A whitespace character: [ \t\n\x0B\f\r] 空格
* \S A non-whitespace character: [^\s] 非空格
* \w A word character: [a-zA-Z_0-9] 数字字母和下划线
* \W A non-word character: [^\w] 非数字字母和下划线
*/
// \\s{4}表示4个空白符
p(" \n\r\t".matches("\\s{4}"));
// \\S表示非空白符
p("a".matches("\\S"));
// \\w{3}表示数字字母和下划线
p("a_8".matches("\\w{3}"));
p("abc888&^%".matches("[a-z]{1,3}\\d+[%^&*]+"));
// 匹配 \
p("\\".matches("\\\\"));
}
}
//输出结果
全部为true
Граничная обработка
-
^
В квадратных скобках это означает отрицание[^]
, если не в скобках, означает начало строки.
public class Demo6 {
private static void p(Object o){
System.out.println(o);
}
public static void main(String[] args) {
/**
* ^ The beginning of a line 一个字符串的开始
* $ The end of a line 字符串的结束
* \b A word boundary 一个单词的边界, 可以是空格, 换行符等
*/
p("hello sir".matches("^h.*"));
p("hello sir".matches(".*r$"));
p("hello sir".matches("^h[a-z]{1,3}o\\b.*"));
p("hellosir".matches("^h[a-z]{1,3}o\\b.*"));
}
}
Упражнение: сопоставьте пустые строки с адресами электронной почты
- Когда я получаю статью, как мне определить, сколько в ней пустых строк? Регулярные выражения можно использовать для простого сопоставления, обратите внимание, что пустые строки могут включать пробелы, символы табуляции и т. д.
p(" \n".matches("^[\\s&&[^\n]]*\\n$"));
- объяснять:
^[\\s&&[^\n]]*
является пробелом, но не символом новой строки,\\n$
заканчиваться новой строкой
- Ниже приведен соответствующий адрес электронной почты
p("liuyj24@126.com".matches("[\\w[.-]]+@[\\w[.-]]+\\.[\\w]+"));
- объяснять:
[\\w[.-]]+
Подчеркнуть одним или несколькими буквенно-цифровыми символами.
или-
сочинение,@
затем знак @, затем то же самое[\\w[.-]]+
, тогда\\.
совпадение.
, и, наконец, то же самое[\\w]+
Класс сопоставленияmatches()
,find()
иlookingAt()
-
matches()
метод сопоставит всю строку с шаблоном.
-
find()
Это должно начинаться с текущей позиции для соответствия, если строка передается первой,find()
, то текущая позиция является началом строки. Конкретный анализ текущей позиции см. в следующем примере кода.
-
lookingAt()
Метод будет соответствовать с начала строки.
public class Demo8 {
private static void p(Object o){
System.out.println(o);
}
public static void main(String[] args) {
Pattern pattern = Pattern.compile("\\d{3,5}");
String s = "123-34345-234-00";
Matcher m = pattern.matcher(s);
//先演示matches(), 与整个字符串匹配.
p(m.matches());
//结果为false, 显然要匹配3~5个数字会在-处匹配失败
//然后演示find(), 先使用reset()方法把当前位置设置为字符串的开头
m.reset();
p(m.find());//true 匹配123成功
p(m.find());//true 匹配34345成功
p(m.find());//true 匹配234成功
p(m.find());//false 匹配00失败
//下面我们演示不在matches()使用reset(), 看看当前位置的变化
m.reset();//先重置
p(m.matches());//false 匹配整个字符串失败, 当前位置来到-
p(m.find());// true 匹配34345成功
p(m.find());// true 匹配234成功
p(m.find());// false 匹配00始边
p(m.find());// false 没有东西匹配, 失败
//演示lookingAt(), 从头开始找
p(m.lookingAt());//true 找到123, 成功
}
}
в классе Matcherstart()
иend()
- Если матч успешен
start()
используется для возврата позиции начала матча,end()
Используется для возврата позиции после соответствующего конечного символа
public class Demo9 {
private static void p(Object o){
System.out.println(o);
}
public static void main(String[] args) {
Pattern pattern = Pattern.compile("\\d{3,5}");
String s = "123-34345-234-00";
Matcher m = pattern.matcher(s);
p(m.find());//true 匹配123成功
p("start: " + m.start() + " - end:" + m.end());
p(m.find());//true 匹配34345成功
p("start: " + m.start() + " - end:" + m.end());
p(m.find());//true 匹配234成功
p("start: " + m.start() + " - end:" + m.end());
p(m.find());//false 匹配00失败
try {
p("start: " + m.start() + " - end:" + m.end());
}catch (Exception e){
System.out.println("报错了...");
}
p(m.lookingAt());
p("start: " + m.start() + " - end:" + m.end());
}
}
//输出结果
true
start: 0 - end:3
true
start: 4 - end:9
true
start: 10 - end:13
false
报错了...
true
start: 0 - end:3
заменить строку
- Если вы хотите заменить строку, вы должны сначала найти замененную строку, вот новое введение
Matcher
метод в классеgroup()
, который возвращает совпадающую строку.
- Давайте посмотрим на пример, поместите строку
java
Преобразовать в верхний регистр.
public class Demo10 {
private static void p(Object o){
System.out.println(o);
}
public static void main(String[] args) {
Pattern p = Pattern.compile("java");
Matcher m = p.matcher("java Java JAVA JAva I love Java and you");
p(m.replaceAll("JAVA"));//replaceAll()方法会替换所有匹配到的字符串
}
}
//输出结果
JAVA Java JAVA JAva I love Java and you
Обновление: поиск и замена строк без учета регистра
- Чтобы не учитывать регистр при сопоставлении, нам нужно указать нечувствительность к регистру при создании шаблона шаблона.
public static void main(String[] args) {
Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);//指定为大小写不敏感的
Matcher m = p.matcher("java Java JAVA JAva I love Java and you");
p(m.replaceAll("JAVA"));
}
//输出结果
JAVA JAVA JAVA JAVA I love JAVA and you
Повторное обновление: без учета регистра, заменить указанную найденную строку
- Эта демонстрация преобразует найденную строку с нечетным номером в верхний регистр, а строку с четным номером — в нижний.
- будет представлен здесь
Matcher
Мощный метод в классеappendReplacement(StringBuffer sb, String replacement)
, он должен передать StringBuffer для конкатенации строк.
public static void main(String[] args) {
Pattern p = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher("java Java JAVA JAva I love Java and you ?");
StringBuffer sb = new StringBuffer();
int index = 1;
while(m.find()){
//m.appendReplacement(sb, (index++ & 1) == 0 ? "java" : "JAVA"); 较为简洁的写法
if((index & 1) == 0){//偶数
m.appendReplacement(sb, "java");
}else{
m.appendReplacement(sb, "JAVA");
}
index++;
}
m.appendTail(sb);//把剩余的字符串加入
p(sb);
}
//输出结果
JAVA java JAVA java I love JAVA and you ?
группировка
- Давайте начнем с проблемы, посмотрите на следующий код
public static void main(String[] args) {
Pattern p = Pattern.compile("\\d{3,5}[a-z]{2}");
String s = "123aa-5423zx-642oi-00";
Matcher m = p.matcher(s);
while(m.find()){
p(m.group());
}
}
//输出结果
123aa
5423zx
642oi
- где регулярное выражение
"\\d{3,5}[a-z]{2}"
Представляет от 3 до 5 чисел, за которыми следуют две буквы, а затем выводит каждую совпадающую строку.
- Как это сделать, если вы хотите напечатать число в каждой совпадающей строке.
- Прежде всего, вы можете подумать о сопоставлении совпадающих строк, но это слишком хлопотно, механизм группировки может помочь нам сгруппировать в регулярных выражениях.
- Указываем использование () для группировки, здесь мы делим буквы и цифры на группы
"(\\d{3,5})([a-z]{2})"
- затем звоню
m.group(int group)
Номер группы можно передать в метод
- Обратите внимание, что номер группы начинается с 0, а группа 0 представляет все регулярное выражение. После 0 каждая открывающая скобка соответствует группе слева направо в регулярном выражении. В этом выражении первая группа является числом, первые 2 группы - буквы.
public static void main(String[] args) {
Pattern p = Pattern.compile("(\\d{3,5})([a-z]{2})");//正则表达式为3~5个数字跟上两个字母
String s = "123aa-5423zx-642oi-00";
Matcher m = p.matcher(s);
while(m.find()){
p(m.group(1));
}
}
//输出结果
123
5423
642
Практика 1. Сканирование адресов электронной почты на веб-страницах (сканеры)
- Предположим, у нас есть несколько высококачественных ресурсов, и мы планируем поделиться ими с пользователями сети, поэтому мы идем в Tieba и отправляем сообщение с электронными письмами для отправки ресурсов.Неожиданно пользователи сети настолько воодушевлены, что оставили почти сотню электронных писем. Но копировать и отправлять их по одному слишком утомительно, рассмотрим программную реализацию.
- Я не буду расширять здесь часть отправки электронных писем и сосредоточусь на использовании регулярных выражений, которые научились перехватывать все адреса электронной почты с веб-страниц.
- Сначала получите html-код сообщенияПросто найди его, щелкни, чтобы прыгнуть, Щелкните правой кнопкой мыши в браузере, чтобы сохранить html-файл
- Далее посмотрите на код:
public class Demo12 {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("C:\\emailTest.html"));
String line = "";
while((line = br.readLine()) != null){//读取文件的每一行
parse(line);//解析其中的email地址
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(br != null){
try {
br.close();
br = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void parse(String line){
Pattern p = Pattern.compile("[\\w[.-]]+@[\\w[.-]]+\\.[\\w]+");
Matcher m = p.matcher(line);
while(m.find()){
System.out.println(m.group());
}
}
}
//输出结果
2819531636@qq.com
2819531636@qq.com
2405059759@qq.com
2405059759@qq.com
1013376804@qq.com
...
Практика 2: апплет статистики кода
- Последний практический случай: подсчитайте, сколько строк кода, сколько строк комментариев и сколько пустых строк в проекте.Вы могли бы также сделать статистику по проектам, которые вы сделали, и обнаружить, что вы написали тысячи строк кода. код по незнанию.народ...
- Я выбрал проект на гитхабе, это небольшой проект, написанный на чистой java, что удобно для статистики.Нажмите, чтобы прыгнуть
- Ниже приведен конкретный код.Помимо использования регулярных выражений для оценки пустых строк, API класса String используется для оценки строк кода и строк комментариев.
public class Demo13 {
private static long codeLines = 0;
private static long commentLines = 0;
private static long whiteLines = 0;
private static String filePath = "C:\\TankOnline";
public static void main(String[] args) {
process(filePath);
System.out.println("codeLines : " + codeLines);
System.out.println("commentLines : " + commentLines);
System.out.println("whiteLines : " + whiteLines);
}
/**
* 递归查找文件
* @param pathStr
*/
public static void process(String pathStr){
File file = new File(pathStr);
if(file.isDirectory()){//是文件夹则递归查找
File[] fileList = file.listFiles();
for(File f : fileList){
String fPath = f.getAbsolutePath();
process(fPath);
}
}else if(file.isFile()){//是文件则判断是否是.java文件
if(file.getName().matches(".*\\.java$")){
parse(file);
}
}
}
private static void parse(File file) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
String line = "";
while((line = br.readLine()) != null){
line = line.trim();//清空每行首尾的空格
if(line.matches("^[\\s&&[^\\n]]*$")){//注意不是以\n结尾, 因为在br.readLine()会去掉\n
whiteLines++;
}else if(line.startsWith("/*") || line.startsWith("*") || line.endsWith("*/")){
commentLines++;
}else if(line.startsWith("//") || line.contains("//")){
commentLines++;
}else{
if(line.startsWith("import") || line.startsWith("package")){//导包不算
continue;
}
codeLines++;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != br){
try {
br.close();
br = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//输出结果
codeLines : 1139
commentLines : 124
whiteLines : 172
Жадный режим и нежадный режим
- После двух реальных сражений я считаю, что все освоили базовое использование регулярных выражений.Следующее представляет жадный режим и нежадный режим.Глядя на официальный API, мы обнаруживаем, что
Pattern
Класс имеет следующие определения:
Greedy quantifiers 贪婪模式
X? X, once or not at all
X* X, zero or more times
X+ X, one or more times
X{n} X, exactly n times
X{n,} X, at least n times
X{n,m} X, at least n but not more than m times
Reluctant quantifiers 非贪婪模式(勉强的, 不情愿的)
X?? X, once or not at all
X*? X, zero or more times
X+? X, one or more times
X{n}? X, exactly n times
X{n,}? X, at least n times
X{n,m}? X, at least n but not more than m times
Possessive quantifiers 独占模式
X?+ X, once or not at all
X*+ X, zero or more times
X++ X, one or more times
X{n}+ X, exactly n times
X{n,}+ X, at least n times
X{n,m}+ X, at least n but not more than m times
- Эти три режима выражают одно и то же значение. В предыдущем объяснении мы все использовали жадный режим. Так в чем же разница между двумя другими режимами? Объясняется следующим примером кода.
public static void main(String[] args) {
Pattern p = Pattern.compile(".{3,10}[0-9]");
String s = "aaaa5bbbb6";//10个字符
Matcher m = p.matcher(s);
if(m.find()){
System.out.println(m.start() + " - " + m.end());
}else {
System.out.println("not match!");
}
}
//输出结果
0 - 10
- Регулярное выражение означает 3~10 символов плюс число.При сопоставлении в жадном режиме система сначала проглотит 10 символов, затем проверит, является ли последний символ числом, и обнаружит, что символов больше нет, поэтому выплюнет символ, снова сопоставить число, успешно сопоставить, получить
0-10
.
- Ниже демо нежадного режима (неохотно, неохотно)
public static void main(String[] args) {
Pattern p = Pattern.compile(".{3,10}?[0-9]");//添加了一个?
String s = "aaaa5bbbb6";
Matcher m = p.matcher(s);
if(m.find()){
System.out.println(m.start() + " - " + m.end());
}else {
System.out.println("not match!");
}
}
//输出结果
0 - 5
- В нежадном режиме сначала проглатываются только 3 (минимум 3), а затем судят, является ли следующий числом, и по результату нет, после проглатывания символа продолжают судить, является ли последний числом. число, результат есть, вывод
0-5
- Наконец, демонстрируется эксклюзивный режим, который обычно делается только в погоне за эффективностью и используется реже.
public static void main(String[] args) {
Pattern p = Pattern.compile(".{3,10}+[0-9]");//多了个+
String s = "aaaa5bbbb6";
Matcher m = p.matcher(s);
if(m.find()){
System.out.println(m.start() + " - " + m.end());
}else {
System.out.println("not match!");
}
}
//输出结果
not match!
- Эксклюзивный режим будет проглатывать 10 символов за раз, а затем определять, является ли последний числом, и не будет продолжать проглатывать или выдавать символ независимо от того, было ли совпадение успешным или нет.
Заканчивать
- Пусть регулярные выражения принесут вам более приятный опыт программирования.