CyStack logo
  • Sản phẩm & Dịch vụ
  • Giải pháp
  • Bảng giá
  • Công ty
  • Tài liệu
Vi

vi

Trang chủHướng dẫnHướng dẫn sử dụng Regular Expression trong Java
Java

Hướng dẫn sử dụng Regular Expression trong Java

CyStack blog 7 phút để đọc
CyStack blog20/08/2025
Locker Avatar

Bao Tran

Web Developer

Locker logo social
Reading Time: 7 minutes

Khi tôi mới bắt đầu hành trình lập trình, Regular Expression trong Java thực sự là một thử thách lớn, thậm chí có lúc tôi xem nó là một cơn ác mộng. Tuy nhiên, sau nhiều năm làm việc và nghiên cứu, tôi nhận ra rằng việc nắm vững Regex mang lại sức mạnh đáng kinh ngạc trong việc xử lý văn bản.

Bài viết này được xây dựng với mục tiêu giúp bạn làm chủ Java Regular Expression, biến những khái niệm phức tạp thành công cụ hữu ích trong bộ kỹ năng của bạn. Cá nhân tôi cũng sẽ thường xuyên quay lại đây để làm mới kiến thức về Java Regex của mình.

Regular Expression trong Java

Regular Expression trong Java

Trong Java, Regular Expression (Regex) định nghĩa một mẫu hình (pattern) cho một chuỗi (String). Bạn có thể sử dụng Regex để tìm kiếm, chỉnh sửa hoặc thao tác với văn bản một cách linh hoạt và hiệu quả. Mặc dù khái niệm Regular Expression không đặc trưng cho riêng một ngôn ngữ lập trình nào, nhưng chúng có những khác biệt nhỏ trong cú pháp và cách triển khai giữa các ngôn ngữ. Regex trong Java có nhiều điểm tương đồng nhất với Perl.

Các lớp hỗ trợ Regex trong Java nằm trong gói java.util.regex. Gói này bao gồm ba lớp chính:

  • Pattern: Đối tượng Pattern là phiên bản đã được biên dịch của biểu thức chính quy. Lớp Pattern không có bất kỳ hàm khởi tạo công khai nào. Chúng ta sử dụng phương thức tĩnh công khai compile của nó để tạo đối tượng Pattern bằng cách truyền biểu thức chính quy dưới dạng đối số.
  • Matcher: Matcher là đối tượng engine Regex của Java, có nhiệm vụ so khớp chuỗi đầu vào (input String) với đối tượng Pattern đã được tạo. Tương tự như Pattern, lớp Matcher không có hàm khởi tạo công khai. Chúng ta lấy đối tượng Matcher bằng cách gọi phương thức matcher từ đối tượng Pattern, và truyền chuỗi đầu vào làm đối số. Sau đó, chúng ta sử dụng phương thức matches để trả về kết quả boolean dựa trên việc chuỗi đầu vào có khớp với mẫu Regex hay không.
  • PatternSyntaxException: Lớp PatternSyntaxException được ném ra nếu cú pháp của biểu thức chính quy không chính xác.

Hãy cùng xem xét một ví dụ chương trình Java Regex đơn giản để hiểu rõ hơn về cách các lớp này hoạt động.

package com.journaldev.util;

import java.util.regex.*;

public class PatternExample {

	public static void main(String[] args) {
		Pattern pattern = Pattern.compile(".xx.");
		Matcher matcher = pattern.matcher("MxxY");
		System.out.println("Input String matches regex - "+matcher.matches());
		// bad regular expression
		pattern = Pattern.compile("*xx*");

	}

}

Khi chúng ta chạy chương trình ví dụ Java Regex này, chúng ta nhận được kết quả như sau:

Input String matches regex - true
Exception in thread "main" java.util.regex.PatternSyntaxException: Dangling meta character '*' near index 0
*xx*
^
	at java.util.regex.Pattern.error(Pattern.java:1924)
	at java.util.regex.Pattern.sequence(Pattern.java:2090)
	at java.util.regex.Pattern.expr(Pattern.java:1964)
	at java.util.regex.Pattern.compile(Pattern.java:1665)
	at java.util.regex.Pattern.(Pattern.java:1337)
	at java.util.regex.Pattern.compile(Pattern.java:1022)
	at com.journaldev.util.PatternExample.main(PatternExample.java:13)

Vì Java Regular Expression xoay quanh việc xử lý String, nên từ phiên bản Java 1.4, lớp String đã được mở rộng để cung cấp một phương thức matches giúp thực hiện việc khớp mẫu Regex. Về bên trong, phương thức này sử dụng các lớp PatternMatcher của Java Regex để thực hiện quá trình xử lý, nhưng rõ ràng nó giúp giảm số dòng mã. Lớp Pattern cũng chứa phương thức matches nhận Regex và chuỗi đầu vào làm đối số, sau đó trả về kết quả boolean sau khi so khớp chúng. Vì vậy, đoạn mã dưới đây hoạt động hoàn hảo để so khớp chuỗi đầu vào với một biểu thức chính quy trong Java:

String str = "bbb";
System.out.println("Using String matches method: "+str.matches(".bb"));
System.out.println("Using Pattern matches method: "+Pattern.matches(".bb", str));

Nếu yêu cầu của bạn chỉ đơn giản là kiểm tra xem chuỗi đầu vào có khớp với một mẫu hay không, bạn nên tiết kiệm thời gian và số dòng mã bằng cách sử dụng phương thức matches đơn giản của String. Bạn chỉ nên sử dụng các lớp PatternMatcher khi bạn cần thao tác với chuỗi đầu vào hoặc cần tái sử dụng mẫu nhiều lần.

Lưu ý rằng biểu thức chính quy sẽ được áp dụng lên chuỗi theo chiều từ trái sang phải, và một ký tự trong chuỗi nguồn khi đã được sử dụng cho một lần so khớp thì sẽ không được dùng lại nữa. Ví dụ, Regex "121" sẽ chỉ khớp với chuỗi "31212142121" hai lần dưới dạng "_121____121".

Regular Expression trong Java – các ký hiệu khớp phổ biến

Biểu thức chính quy Mô tả Ví dụ
. Khớp với bất kỳ ký tự đơn nào ("…", "a%") → true("…", ".a") → true("…", "a") → false
^aaa Khớp với chuỗi aaa ở đầu dòng ("^a.c.", "abcd") → true("^a", "ac") → false
aaa$ Khớp với chuỗi aaa ở cuối dòng ("…cd$", "abcd") → true("a$", "a") → true("a$", "aca") → false
[abc] Khớp với một trong các ký tự a, b hoặc c. [] được gọi là lớp ký tự (character class) ("^[abc]d.", "ad9") → true("[ab].d$", "bad") → true("[ab]x", "cx") → false
[abc][12] Khớp với a, b hoặc c theo sau bởi 1 hoặc 2 ("[ab][12].", "a2#") → true("[ab]…[12]", "acd2") → true("[ab][12]", "c2") → false
[^abc] Khi ^ là ký tự đầu tiên trong [], nó phủ định mẫu, tức là khớp với bất kỳ ký tự nào ngoại trừ a, b hoặc c ("[^ab][^12].", "c3#") → true("[^ab]…[^12]", "xcd3") → true("[^ab][^12]", "c2") → false
[a-e1-8] Khớp với khoảng ký tự từ a đến e hoặc từ 1 đến 8 ("[a-e1-3].", "d#") → true("[a-e1-3]", "2") → true("[a-e1-3]", "f2") → false
xx yy Khớp với biểu thức xx hoặc yy

Java Regex Metacharacters

Chúng ta có một số metacharacters (ký tự đặc biệt) trong Java Regex, chúng giống như các “shortcut” cho các mẫu khớp phổ biến.

Biểu thức Mô tả
\\d Bất kỳ chữ số nào, tương đương với [0-9]
\\D Bất kỳ ký tự không phải là chữ số, tương đương với [^0-9]
\\s Bất kỳ ký tự khoảng trắng nào, tương đương với [\\t\\n\\x0B\\f\\r] (bao gồm tab, xuống dòng, v.v.)
\\S Bất kỳ ký tự không phải khoảng trắng nào, tương đương với [^\\s]
\\w Bất kỳ ký tự thuộc từ (word character), tương đương với [a-zA-Z_0-9]
\\W Bất kỳ ký tự không thuộc từ, tương đương với [^\\w]
\\b Ranh giới của một từ (word boundary)
\\B Không phải là ranh giới của một từ (non-word boundary)

Có hai cách để sử dụng các metacharacters như các ký tự thông thường trong biểu thức chính quy:

  • Đặt một dấu gạch chéo ngược (\\\\) phía trước metacharacter.
  • Giữ metacharacter bên trong \\\\Q (bắt đầu trích dẫn) và \\\\E (kết thúc trích dẫn).

Regular Expression trong Java – Quantifiers

Java Regex Quantifiers (ký tự định lượng) chỉ định số lần xuất hiện của một ký tự để so khớp.

Biểu thức chính quy Mô tả
x? x xuất hiện một lần hoặc không xuất hiện
x* x xuất hiện không hoặc nhiều lần
x+ x xuất hiện ít nhất một lần
x{n} x xuất hiện chính xác n lần
x{n,} x xuất hiện ít nhất n lần
x{n,m} x xuất hiện ít nhất n lần nhưng không quá m lần

Quantifiers trong Java Regex có thể được sử dụng với các lớp ký tự (character classes) và cả các nhóm bắt giữ (capturing groups). Ví dụ, [abc]+ có nghĩa là “a, b, hoặc c – một hoặc nhiều lần”. (abc)+ có nghĩa là nhóm “abc” – một hoặc nhiều lần. Bây giờ, chúng ta sẽ thảo luận về Capturing Group.

Regular Expression trong Java – Capturing Groups

Capturing groups trong Java Regular Expression được sử dụng để xử lý nhiều ký tự như một đơn vị duy nhất. Bạn có thể tạo một nhóm bằng cách sử dụng dấu ngoặc đơn (). Phần của chuỗi đầu vào khớp với nhóm bắt giữ sẽ được lưu vào bộ nhớ và có thể được gọi lại bằng Backreference (tham chiếu ngược). Bạn có thể sử dụng phương thức matcher.groupCount() để tìm ra số lượng nhóm bắt trong một mẫu Java Regex. Ví dụ, ((a)(bc)) chứa 3 nhóm bắt giữ: ((a)(bc)), (a)(bc). Bạn có thể sử dụng Backreference trong biểu thức chính quy với một dấu gạch chéo ngược (\\\\) và sau đó là số thứ tự của nhóm cần gọi lại. Các nhóm bắt giữ và Backreferences có thể gây nhầm lẫn, vì vậy hãy cùng hiểu điều này với một ví dụ.

System.out.println(Pattern.matches("(\\\\\\\\w\\\\\\\\d)\\\\\\\\1", "a2a2")); //true
System.out.println(Pattern.matches("(\\\\\\\\w\\\\\\\\d)\\\\\\\\1", "a2b2")); //false
System.out.println(Pattern.matches("(AB)(B\\\\\\\\d)\\\\\\\\2\\\\\\\\1", "ABB2B2AB")); //true
System.out.println(Pattern.matches("(AB)(B\\\\\\\\d)\\\\\\\\2\\\\\\\\1", "ABB2B3AB")); //false

Trong ví dụ đầu tiên, khi runtime, nhóm bắt đầu tiên là (\\\\w\\\\d) sẽ đánh giá thành "a2" khi khớp với chuỗi đầu vào "a2a2" và được lưu vào bộ nhớ. Do đó, \\\\1 đang tham chiếu đến "a2", làm cho kết quả là true. Vì lý do tương tự, câu lệnh thứ hai in ra false. Bạn hãy tự thử phân tích kịch bản này cho câu lệnh 3 và 4 nhé! 🙂 Bây giờ, chúng ta sẽ xem xét một số phương thức quan trọng của các lớp PatternMatcher.

  • Chúng ta có thể tạo một đối tượng Pattern với các cờ (flags). Ví dụ, Pattern.CASE_INSENSITIVE cho phép khớp không phân biệt chữ hoa/thường.
  • Lớp Pattern cũng cung cấp phương thức split(String) tương tự như phương thức split() của lớp String.
  • Phương thức toString() của lớp Pattern trả về chuỗi biểu thức chính quy mà từ đó mẫu này được biên dịch.
  • Các phương thức start()end() của lớp Matcher hiển thị chính xác vị trí mà khớp được tìm thấy trong chuỗi đầu vào.
  • Lớp Matcher cũng cung cấp các phương thức thao tác chuỗi replaceAll(String replacement)replaceFirst(String replacement).

Hãy cùng xem xét các phương thức Java Regex này trong một chương trình ví dụ đơn giản.

package com.journaldev.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExamples {

	public static void main(String[] args) {
		// using pattern with flags
		Pattern pattern = Pattern.compile("ab", Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher("ABcabdAb");
		// using Matcher find(), group(), start() and end() methods
		while (matcher.find()) {
			System.out.println("Found the text \\\\"" + matcher.group()
					+ "\\\\" starting at " + matcher.start()
					+ " index and ending at index " + matcher.end());
		}

		// using Pattern split() method
		pattern = Pattern.compile("\\\\\\\\W");
		String[] words = pattern.split("one@two#three:four$five");
		for (String s : words) {
			System.out.println("Split using Pattern.split(): " + s);
		}

		// using Matcher.replaceFirst() and replaceAll() methods
		pattern = Pattern.compile("1*2");
		matcher = pattern.matcher("11234512678");
		System.out.println("Using replaceAll: " + matcher.replaceAll("_"));
		System.out.println("Using replaceFirst: " + matcher.replaceFirst("_"));
	}

}

Kết quả của chương trình ví dụ Java Regex trên là:

Found the text "AB" starting at 0 index and ending at index 2
Found the text "ab" starting at 3 index and ending at index 5
Found the text "Ab" starting at 6 index and ending at index 8
Split using Pattern.split(): one
Split using Pattern.split(): two
Split using Pattern.split(): three
Split using Pattern.split(): four
Split using Pattern.split(): five
Using replaceAll: _345_678
Using replaceFirst: _34512678

Như vậy là chúng ta đã cùng nhau đi qua các khái niệm cơ bản và nâng cao về Regular Expression trong Java.

Kết luận

Regular Expression trong Java, dù thoạt nhìn có vẻ khó khăn và phức tạp, nhưng nếu bạn dành thời gian làm việc và thực hành với chúng, bạn sẽ nhận ra chúng thực sự dễ học và dễ sử dụng. Nắm vững Regex sẽ mở ra cánh cửa cho bạn thao tác văn bản một cách mạnh mẽ, từ việc xác thực dữ liệu đầu vào, phân tích log, đến xử lý các chuỗi phức tạp trong các dự án thực tế.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất