22
Làm việc với JPA trong NetBean 6.1 1. Giới thiệu Trong ví dụ này, chúng ta cùng đề cập đến Java Persistence API (JPA) trong NetBeans 6.1 với 1 project kiểu java application. Ở đây chúng ta không nói đến lý thuyết của JPA, vần đền này sẽ được nói trong 1 bài khác hoặc chúng ta có thể trực tiếp đọc trên internet. Ở đây chúng ta nói nhiều đến làm thế nào để thao tác với JPA trên NetBean. Phần mềm yêu cầu: NetBeans IDE 6.1 JDK version 5.0 hoặc 6.0 MySql 5.0 2. Tạo database trong MySql 5.0 Tạo 1 database trong MySQL5.0 với tên jpaDB. Bạn có thể chạy các lệnh sau MySQL command promt: CREATE DATABASE jpaDB;GRANT ALL ON jpaDB.* TO jpaDB@”%” IDENTIFIED BY “jpaDB”; GRANT ALL ON jpaDB.* TO jpaDB@”localhost” IDENTIFIED BY “jpaDB”; USE jpaDB; 3.Tạo kết nối đến MySQL database trong NetBeans IDE 6.1 Netbean6.1 có sẵn driver kết nối đến MySQL, bạn làm theo các bước sau để kết nối đến MySQL trong NetBean a. Nhấn Ctrl-5 để mở của sổ Service rôi mở rộng phần database như hình

Vietnam Coding

Embed Size (px)

Citation preview

Page 1: Vietnam Coding

Làm việc với JPA trong NetBean 6.1

1. Giới thiệu

Trong ví dụ này, chúng ta cùng đề cập đến Java Persistence API (JPA) trong NetBeans 6.1 với 1 project kiểu java application. Ở đây chúng ta không nói đến lý thuyết của JPA, vần đền này sẽ được nói trong 1 bài khác hoặc chúng ta có thể trực tiếp đọc trên internet. Ở đây chúng ta nói nhiều đến làm thế nào để thao tác với JPA trên NetBean.

Phần mềm yêu cầu:

NetBeans IDE 6.1 JDK version 5.0 hoặc 6.0 MySql 5.0

2. Tạo database trong MySql 5.0

Tạo 1 database trong MySQL5.0 với tên jpaDB. Bạn có thể chạy các lệnh sau MySQL command promt:

CREATE DATABASE jpaDB;GRANT ALL ON jpaDB.* TO jpaDB@”%” IDENTIFIED BY “jpaDB”;GRANT ALL ON jpaDB.* TO jpaDB@”localhost” IDENTIFIED BY “jpaDB”;

USE jpaDB;

3.Tạo kết nối đến MySQL database trong NetBeans IDE 6.1

Netbean6.1 có sẵn driver kết nối đến MySQL, bạn làm theo các bước sau để kết nối đến MySQL trong NetBean

a. Nhấn Ctrl-5 để mở của sổ Service rôi mở rộng phần database như hình

Page 2: Vietnam Coding

b. Nhấn chuột phải lên MySQL Server, chọn properties, xuất hiện cửa sổ như hình

c. Thông thường các thông số đã có như hình, nếu chưa thì chúng ta nhập cho đủ.

Chú ý: Bạn phải đảm bảo rằng MySQL đã được chạy trên máy tính của bạn nếu không chúng ta sẽ không thể kết nối được đến nó.

d. Sau khi starting Database Server trên máy của bạn, start MySQL Database Server trong NetBean IDE bằng cách nhấn phải chuột lên mục MySQL Server rồi chọn Start.

e. Bây giờ bạn mở rộng mục MySQL Server bạn sẽ thấy các database được hiển thị như hình sau

Page 3: Vietnam Coding

f. Nhấn phải chuột lên database của chúng ta, chọn connect, một của sổ xuất hiện yêu cầu bạn nhập các thông tin

Nhấn OK khi mọi việc sẵn sàng. Bạn sẽ thấy database xuất hiện trên cửa sổ service. Nhấn chuột phải lên database vừa tạo trong của sổ service, chọn connect

g. Bây giờ chúng ta bắt đầu tạo table cho database.Nhấn phải chuột lên mục Tables, chọn Create Table như hình

Page 4: Vietnam Coding

một cửa sổ xuất hiện và chúng ta dùng nó để tạo 1 bảng có tên users như hình

Kết quả trong cửa sổ Service

4. Tạo JavaApplication trong NetBeans 6.1

Bây giờ chúng ta tạo 1 java application trong NetBean, đặt tên cho project là MyJPAApplication

Tiếp theo chúng ta tạo 1 lớp Entity ánh xạ vào bảng User

Page 5: Vietnam Coding

Chọn database như hình sau

Page 6: Vietnam Coding

Bạn có thể xem được danh sách các bảng của database trong menu Available Tables. Chọn bảng User rồi nhấn nút Add để chuyển bảng đang chọn sang danh sách “Selected Tables”.

Đổi tên lớp thành UserGenEntity rồi nhấn nút “Create Persistence Unit” như hình

Trong wizard “Create Persistant Unit”, đổi tùy chọn “Table Generation Strategy” thành

Page 7: Vietnam Coding

Create

Ta được như hình sau:

Nhấn Finish để kết thúc wizard. Bạn có thể thấy lớp UserGenEntity trong cửa sổ Project đồng thời bạn sẽ thấy các thư viện TopLink được thêm vào trong mục Libraries như hình sau:

Page 8: Vietnam Coding

Bạn cũng nên thêm phương thức constructor copy cho lớp UserGenEntity như sau

public UserGenEntity(int id,String fn,String ln) {this.id=id;this.firstname=fn;this.lastname=ln;}

5. Code cho ứng dụng JPA

Thêm vào ứng dụng chúng ta lớp UserManager.java với nội dung code được cho sau đây:

package com.myJpa;

import java.util.List;import javax.persistence.EntityManager;import javax.persistence.EntityManagerFactory;import javax.persistence.Query;

public class UserManager {

private EntityManager em;

Page 9: Vietnam Coding

public UserManager(EntityManagerFactory emf) { em = emf.createEntityManager(); }

public void createUser(UserGenEntity user) { em.getTransaction().begin(); em.persist(user); em.getTransaction().commit(); }

public UserGenEntity searchById(Integer id) { return em.find(UserGenEntity.class, id); }

public void updateUser(UserGenEntity user) { em.getTransaction().begin(); em.merge(user); em.getTransaction().commit(); }

public void removeUser(UserGenEntity user) { em.getTransaction().begin(); em.remove(user); em.getTransaction().commit(); }

public List getAll() { Query query = em.createQuery("select a from UserGenEntity a"); List list = query.getResultList(); return list; }

public void close() { em.close(); }}

Code trên gồm các phương thức sau

createUser — Tạo 1 user database. searchById — Tìm kiếm 1 User theo ID. updateUser — update thông tin của user từ bảng removeUser — xóa user khỏi danh sách. getAll — lấy tất cả users từ bảng

6. Chạy thử ứng dụng JPA

Bây giờ chúng ta có thể viết ứng dụng được rồi. Nhưng đừng quên thêm MySQL Driver vào project của bạn bằng cách nhấn phải chuột lên mục libraries trong của sổ project, chọn Add library. Cửa sổ sau xuất hiện và bạn phải chọn driver cho MySQl

Page 10: Vietnam Coding

Nhấn OK.Bây giờ ta tạo 1 lớp mới với 1 hàm main để test ứng dụng. Code như sau

package com.myJpa;

import java.util.List;import javax.persistence.EntityManager;import javax.persistence.EntityManagerFactory;import javax.persistence.Persistence;

public class TestJPA {

private UserManager um; private EntityManager em; private EntityManagerFactory emf; private static final UserGenEntity user1 = new UserGenEntity(1, "Nguyen Van", "Tèo"); private static final UserGenEntity user2 = new UserGenEntity(2, "Thân Thị", "Đẹt");

protected void setUp() throws Exception { emf = Persistence.createEntityManagerFactory("MyJPAApplicationPU"); em = emf.createEntityManager(); um = new UserManager(emf); }

Page 11: Vietnam Coding

protected void close() throws Exception { um.close(); em.close(); emf.close(); }

public void test() { um.createUser(user1); UserGenEntity user = um.searchById(1); System.out.println("After creation of user in table"); System.out.println("Firstname: " + user.getFirstname()); System.out.println("Lastname: " + user.getLastname());

user.setFirstname("Trần Anh"); um.updateUser(user);

user = um.searchById(1); System.out.println("After update."); System.out.println("Firstname: " + user.getFirstname()); System.out.println("Lastname: " + user.getLastname());

System.out.println("Adding one more record"); um.createUser(user2);

List list = um.getAll(); System.out.println("Number of users: " + list.size()); }

public static void main(String args[]) { System.out.println("Inside TestJPA main"); TestJPA testJPA = new TestJPA(); try { testJPA.setUp(); testJPA.test(); testJPA.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println("End of TestJPA main"); }}

Kết quả sau khi thực thi ứng dụng:

init:deps-jar:compile:run:Inside TestJPA main[TopLink Info]: 2008.10.24 10:08:52.938–ServerSession(32019423)–TopLink, version: Oracle TopLink Essentials – 2.0.1 (Build b09d-fcs (12/06/2007))[TopLink Info]: 2008.10.24 10:08:53.503–ServerSession(32019423)–file:/C:/Users/Hamy/Documents/

Page 12: Vietnam Coding

NetBeansProjects/MyJPAApplication/build/classes/-MyJPAApplicationPU login successful[TopLink Warning]: 2008.10.24 10:08:53.560–ServerSession(32019423)–Exception [TOPLINK-4002] (Oracle TopLink Essentials – 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.DatabaseExceptionInternal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table ‘user’ already existsError Code: 1050Call: CREATE TABLE user (ID INTEGER NOT NULL, FIRSTNAME VARCHAR(255), LASTNAME VARCHAR(255), PRIMARY KEY (ID))Query: DataModifyQuery()After creation of user in tableFirstname: Tr?n VănLastname: TèoAfter update.Firstname: Tr?n AnhLastname: TèoAdding one more recordNumber of users: 2[TopLink Info]: 2008.10.24 10:08:53.660–ServerSession(32019423)–file:/C:/Users/Hamy/Documents/NetBeansProjects/MyJPAApplication/build/classes/-MyJPAApplicationPU logout successfulEnd of TestJPA mainBUILD SUCCESSFUL (total time: 2 seconds)

Kiểm tra kết quả bằng cách nhấn Ctrl-5 để mở cửa sổ Service, Nhấn chuột phải lên Bảng User của chúng ta rồi chọn View Data, kết quả sẽ có 2 mẫu tin mới vừa thêm vào.

Regular Expressions in java | Biểu thức chính quy trong java

Regular Expressions (Regex) dịch ra tiếng Việt là Biểu thức chính quy. Khái niệm này nằm trong 1 mớ lý thuyết vô cùng đồ sộ và hầm hố ;D. Nhưng ko nên lo lắng, ta có thể hiểu nôm na Regex là 1 cái mẫu (pattern) dùng để mô tả 1 lớp ký tự nào đó.

VD: lazydog là 1 regex. Nó là 1 mẫu đơn giản nhất vì nó so khớp (match) với đoạn text lazydog. 1 match là 1 đoạn text so khớp với mẫu.

VD phức tạp hơn 1 chút: \b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b Đây là mẫu mô tả 1 địa chỉ email. Mẫu này có thể dc dùng để tìm 1 địa chỉ email trong 1 đoạn văn bản, hoặc kiểm tra xem 1 chuỗi có phải là địa chỉ email hợp lệ hay ko.

Regex có thể dc sử dụng với bất kỳ dữ liệu nào mà ta có thể truy cập, thông qua ứng dụng hoặc ngôn ngữ lập trình. Có thể kể đến 1 số ứng dụng xử lý văn bản hỗ trợ regex: PowerGREP, EditPad Pro, RegexBuddy,…

Page 13: Vietnam Coding

Ký tự thông thường (Literal Characters)

Regex cơ bản nhất chính là biểu thức bao gồm 1 ký tự thông thường, VD: a. Nó sẽ so khớp với thực thể đầu tiên của ký tự đó trong chuỗi. VD nếu có chuỗi: LazyDog is a boy, nó sẽ so khớp với ký tự a sau ký tự L. Regex này cũng có thể so khớp với ký tự a thứ 2 nếu ta điều khiển regex engine tiếp tục tìm kiếm sau khi đã so khớp dc 1 lần.

Cũng như vậy, regex dog sẽ so khớp với dog trong chuỗi LazyDog is not a dog. Regex này bao gồm 1 sêri 3 ký tự thông thường. Engine sẽ hiểu biểu thức này là: tìm d, theo sau bởi o, theo sau bởi g.

Chú ý rằng regex engine mặc định phân biệt chữ hoa và chữ thường. Dog ko so khớp với dog.

Ký tự đặc biệt (Special Characters)

Vì ta cần làm nhiều công việc phức tạp hơn là tìm kiếm 1 đoạn văn bản, cho nên phải trưng dụng 1 vài ký tự để làm những nhiệm vụ đặc biệt. Trong cú pháp regex dc thảo luận ở đây, có 11 ký tự mang ý nghĩa đặc biệt: [ \ ^ $ . | ? * + ( ). Chúng dc gọi là các metacharacter.

Nếu cần dùng các ký tự này với ý nghĩa thông thường, ta phải giải phóng nó bằng \. VD nếu cần so khớp 1+1=2, thì regex đúng sẽ là 1\+1=2. Chú ý rằng 1+1=2 cũng là regex đúng, nên sẽ ko báo lỗi, nhưng nó sẽ ko cho ta kết quả như mong muốn. Regex 1+1=2 sẽ so khớp với 111=2 trong chuỗi 123+111=234, vì dấu + ở đây mang ý nghĩa đặc biệt.

Nếu ta quên ko giải phóng ký tự đặc biệt ở những chỗ nó ko dc phép đứng thì sẽ gặp lỗi. VD: +1

Hầu hết các loại cú pháp regex đều coi { như 1 ký tự thông thường, trừ khi nó là 1 phần của toán tử nhắc lại (repetition operator), VD: {1, 3}. Vì vậy ta ko cần giải phóng ký tự này.

Ta chỉ dùng \ để giải phóng các ký tự đặc biệt, còn các ký tự khác thì ko nên, vì \ cũng là 1 ký tự đặc biệt. \ khi kết hợp với 1 ký tự thông thường sẽ có ý nghĩa đặc biệt, VD: \d sẽ so khớp với 1 chữ số từ 0 - 9.

Tất cả các loại cú pháp regex đều cho phép giải phóng 1 ký tự đặc biệt bằng \. Rất nhiều cú pháp khác còn hỗ trợ kiểu giải phóng \Q... \E. Tất cả các ký tự nằm trong cặp \Q và \E sẽ dc coi như ký tự thông thường. VD: \Q*\d+*\E sẽ so khớp với đoạn văn bản *\d+* . Kiểu cú pháp này dc hỗ trợ bởi JGsoft engine, Perl, PCRE, ...

Ký tự đặc biệt và ngôn ngữ lập trình

Khác với trong ngôn ngữ lập trình, trong regex, ký tự ' và " ko phải là ký tự đặc biệt. Vì vậy, ko cần phải giải phóng nó.

Page 14: Vietnam Coding

Trong mã nguồn của 1 chương trình, cần luôn ghi nhớ những ký tự nào dc ngôn ngữ lập trình xử lý đặc biệt. Bởi vì những ký tự này sẽ dc trình biên dịch xử lý trước khi dc engine regex xử lý. VD: regex 1\+1=2 phải dc viết thành 1\\+1=2 trong mã nguồn C++. Trình biên dịch C++ sẽ chuyển \\ thành \ trong chuỗi trên, sau đó nó mới dc chuyển đến regex engine. VD khác: đế so khớp c:\temp, cần dùng regex c:\\temp (vì \t trong regex mang ý nghĩa đặc biệt). Và trong mã nguồn C++, regex này cần dc viết là c:\\\\temp. Đúng là địa ngục ;D. Ko bit trong PHP của chúng ta thì thế nào đây hic :'(.

Ký tự ko in được

Có thể dùng các tổ hợp ký tự đặc biệt để đặt các ký tự ko in dc vào regex.

\t cho ký tự tab (ASCII 0x09)\r cho carriage return (0x0D)\n cho line feed (0x0A).\a (bell, 0x07)\e (escape, 0x1B)\f (form feed, 0x0C)\v (vertical tab, 0x0B).

Chú ý rằng Windows text files sử dụng \r\n để kết thúc dòng, còn UNIX text files sử dụng \n.

Có thể dùng cách này để viết bất kỳ ký tự nào nếu biết mã 16 ASCII của ký tự đó trong bảng mã đang dùng. VD trong bảng mã Latin-1, ký tự copyright có mã 0xA9. Vì thế để tìm ký tự này, ta dùng \xA9.

Hầu hết các loại cú pháp regex còn cho phép sử dụng tổ hợp \cA đến \cZ (c cố định, theo sau bởi 1 chữ cái hoa từ A - Z) để biểu thị ký tự điều khiển. VD \cA biểu thị Control+A. \cM biểu thị Control+M, hay carriage return, giống như \r.

Nếu regex engine hỗ trợ Unicode, ta sử dụng \uFFFF thay cho \xFF để biểu thị 1 ký tự Unicode. VD: mã unicode của ký tự đồng euro là 0x20AC. Để đặt nó vào biểu thức regex, ta dùng \u20AC.

Lớp ký tự (Character Classes - Character Sets)

Lớp ký tự

Sử dụng lớp ký tự, ta sẽ khiến regex engine chỉ chọn ra 1 ký tự để so khớp. Để sử dụng, ta đặt các ký tự cần so khớp vào 2 dấu [ và ]. VD: để so khớp ký tự a hoặc e, ta dùng [ae]. Như vậy biểu thức gr[ae]y sẽ khớp với gray hoặc grey.

Lớp ký tự chỉ so khớp với 1 ký tự đơn. Như vậy gr[ae]y sẽ ko khớp với graay, graey,v.v… Thứ tự các ký tự trong lớp ko quan trọng. Kết quả trả về luôn giống nhau.

Page 15: Vietnam Coding

Để xác định 1 vùng ký tự trong lớp ký tự, ta sử dụng dấu – . VD: [0-9] so khớp với 1 chữ số từ 0 – 9. Có thể sử dụng nhiều vùng ký tự hoặc kết hợp vùng ký tự với ký tự đơn. VD: [0-9a-fA-F] so khớp với 1 chữ số hệ 16, ko phân biệt chữ hoa, thường. [0-9a-fxA-FX] so khớp với 1 chữ số hệ 16 hoặc chữ cái X, ko phân biệt chữ hoa, thường. Cũng như trên, thứ tự các vùng ko quan trọng.

Lớp ký tự phủ định

Đặt dấu ^ sau [ trong lớp ký tự sẽ phủ định lớp ký tự đó. Kết quả là lớp ký tự sẽ so khớp với bất kỳ ký tự nào ko nằm trong lớp ký tự đó. Lớp ký tự phủ định có thể so khớp với cả ký tự line break.

Chú ý rằng lớp ký tự phủ định vẫn phải dc so khớp với 1 ký tự. VD: q[^u] ko phải là “q ko theo sau bởi u” mà là “q theo sau bởi 1 ký tự ko phải u”. Vì vậy nó sẽ ko so khớp với q trong chuỗi Iraq, và sẽ so khớp với q và space trong chuỗi Iraq is a country.

Metacharacter trong lớp ký tự

Trong lớp ký tự, các ký tự mang ý nghĩa đặc biệt hay metacharacter chỉ bao gồm: ] \ ^ -. Các metacharacter nói ở phần trước khi đặt trong lớp ký tự sẽ chỉ dc coi như ký tự thông thường, và do đó ko cần phải giải phóng. VD: để tìm ký tự * hoặc +, ta dùng [+*].

Để đặt ký tự \ vào trong lớp ký tự với nghĩa thông thường, cần giải phóng nó bằng 1 ký tự \ khác. VD: [\\x] sẽ khơp với ký tự \ hoặc x. Các ký tự ] ^ – nếu muốn dùng theo nghĩa thông thường cũng phải dc giải phóng bằng \ hoặc đặt nó ở vị trí mà nó sẽ ko có ý nghĩa đặc biệt. Ta nên dùng cách thứ 2 để biểu thức regex trông dễ nhìn hơn như sau:

Với ^, đặt nó ở bất kỳ chỗ nào trừ vị trí ngay sau [ . VD: [x^] sẽ khớp với x hoặc ^.

Với ], đặt nó ngay sau [ hoặc [^ . VD: []x] sẽ khớp với ] hoặc x. [^]x] sẽ khớp với bất kỳ ký tự nào ko phải là ] hoặc x.

Với -, đặt nó ngay sau [ hoặc [^ , hoặc ngay trước ]. VD: cả [-x] và [x-] đều so khớp với – hoặc x.

Có thể sử dụng tất cả các ký tự ko in dc trong lớp ký tự giống như dùng chúng ngoài lớp ký tự. VD: [$\u20AC] sẽ khớp với $ hoặc ký tự đồng euro (với giả định cú pháp regex đang dùng hỗ trợ unicode).

JGsoft engine, Perl và PCRE còn hỗ trợ kiểu \Q…\E trong lớp ký tự để giải phóng 1 chuỗi ký tự. VD: [\Q[-]\E] sẽ khớp với [ hoặc - hoặc ].

Cú pháp regex của POSIX lại xử lý \ trong lớp ký tự như 1 ký tự thông thường. Đồng nghĩa với việc ta ko thể dùng \ để giải phóng ] ^ -. Để làm việc này ta chỉ còn cách đặt chúng vào các vị trí như trình bày ở trên. Ngoài ra điều này cũng đồng nghĩa với việc các cú pháp tắt (shorthand, VD: \d) ko còn hiệu lực.

Page 16: Vietnam Coding

Lớp ký tự viết tắt (Shorthand Character Classes)

\d là dạng tắt của [0-9].

\w dc gọi là “ký tự từ” (word character). Chính xác những ký tự nào dc khớp với nó thay đổi tuỳ theo mỗi loại cú pháp regex. Trong tất cả các loại cú pháp, nó sẽ bao gồm [A-Za-z]. Trong hầu hết các loại cú pháp, nó cũng bao gồm cả dấu _ và chữ số.

\s dc gọi là “ký tự trắng” (whitespace character). Nó khớp với ký tự nào thì cũng tùy thuộc vào từng loại cú pháp. Trong kiểu cú pháp thảo luận ở đây, nó bao gồm [\t]. Nghĩa là \s sẽ khớp với space hoặc tab. Trong hầu hết cú pháp , nó cũng bao gồm cả ký tự carriage return hoặc line feed, nghĩa là [\t\r\n]. Một số cú pháp khác lại bao gồm thêm cả các ký tự ko in dc hiếm khi dùng như vertical tab hoặc form feed.

Các lớp ký tự viết tắt có thể dc dùng cả trong lẫn ngoài cặp []. VD: \s\d khớp với 1 ký tự trắng theo sau bởi 1 chữ số. [\s\d] khớp với 1 ký tự đơn là 1 ký tự trắng hoặc 1 chữ số. Khi áp dụng vào chuỗi 1 + 2 = 3, regex thứ 1 sẽ khớp với 2 (space và 2), trong khi regex thứ 2 sẽ khớp với 1. [\da-fA-F] khớp với 1 chữ số hệ 16, giống như [0-9a-fA-F].

Lớp ký tự viết tắt phủ định (Negated Shorthand Character Classes)

\D tương đương [^\d]\W tương đương [^\w]\S tương đương [^\s]

Cần thận trọng khi sử dụng dạng viết tắt phủ địng bên trong []. [\D\S] khác với [^\d\s]. Regex thứ 2 sẽ khớp với bất kỳ ký tự nào ko phải là chữ số hoặc ký tự trắng. Còn regex thứ 1 sẽ khớp với bất kỳ ký tự nào ko phải là chữ số hoặc ko phải là ký tự trắng. Và vì chữ số ko phải là ký tự trắng và ký tự trắng ko phải là chữ số cho nên [\D\S] sẽ khớp với bất kỳ ký tự nào, bao gồm cả ký tự trắng và chữ số ;D.

Nhắc lại lớp ký tự (Repeating Character Classes)

Nếu nhắc lại lớp ký tự khi dùng các toán tử nhắc lại ? * + , ta sẽ nhắc lại cả lớp ký tự chứ ko chỉ nhắc lại ký tự mà nó so khớp. VD: regex [0-9]+ sẽ khớp với cả 837 lẫn 222.

Nếu muốn nhắc lại chỉ các ký tự dc so khớp, ta cần dùng tham chiếu ngược (backreferences). ([0-9])\1+ sẽ khớp với 222 chứ ko khớp với 837. Khi áp dùng regex này vào chuỗi 833337, nó sẽ khớp với 3333. Chi tiết hơn thì hồi sau sẽ rõ ;D

Ký tự chấm (Dot)

Ký tự Dot khớp với hầu hết các ký tự

Trong biểu thức regex, dấu . là metacharacter dc sử dụng nhiều nhất, và cũng là ký tự bị sử dụng sai nhiều nhất.

Page 17: Vietnam Coding

Dấu . khớp với 1 ký tự đơn bất kỳ ngoại trừ ký tự newline. Vì vậy, dấu . tương đương với [^\n] (trong UNIX) hoặc [^\r\n] (trong Windows).

Trong Perl, dấu . có thể khớp với cả newline nếu ta dùng chế độ “single-line mode”. Để sử dụng chế độ này, ta thêm s vào sau biểu thức regex, VD: m/^regex$/s;

JavaScript và VBScript ko có chế độ nào hỗ trợ Dot so khớp với các ký tự line break. Vì vậy, để so khớp với bất kỳ ký tự nào ta phải dùng [\s\S] thay cho Dot. [\s\S] so khớp với 1 ký tự là ký tự trắng (bao gồm cả các ký tự line break) hoặc ko phải ký tự trắng, nghĩa là nó so khớp với bất kỳ ký tự nào.

Sử dụng Dot 1 cách tiết kiệm

Dấu . là 1 metacharacter đầy uy lực. Nó có thể khớp với bất kỳ ký tự nào, nhưng cũng có thể khớp với ký tự mà ta ko muốn. Những trường hợp như thế có thế rất khó nhận ra.

Hãy lấy 1 VD đơn giản để minh hoạ: giải sử ta muốn tìm 1 chuỗi ngày tháng năm dưới dạng mm/dd/yy, trong đó dấu phân cách ngày tháng năm ta để người dùng tuỳ chọn. Giải pháp nhanh nhất là \d\d.\d\d.\d\d. Trông có vẻ ổn. Nó sẽ khớp 1 chuỗi kiểu như 02/12/03. Vấn đề là 1 chuỗi kiểu như 02512703 cũng dc coi là 1 ngày hợp lệ với regex trên (chấm thứ 1 khớp với 5, chấm thứ 2 khớp với 7).

Giải pháp tốt hơn là: \d\d[- /.]\d\d[- /.]\d\d. Regex này cho phép – hoặc space hoặc . hoặc / làm dấu phân cách ngày tháng năm. Lưu ý rằng dấu . trong lớp ký tự là 1 ký tự thông thường, do đó ko cần phải giải phóng. Nhưng regex này vẫn chưa hoàn hảo, nó sẽ coi 99/99/99 là 1 ngày hợp lệ.

Giải pháp tiếp theo: [0-1]\d[- /.][0-3]\d[- /.]\d\d. Ổn hơn nhưng vẫn chưa hoàn hảo, nó có thể khớp với 19/39/99.

Chất lượng của regex thế nào tuỳ thuộc vào yêu cầu của bạn. VD nếu muốn thẩm định thông tin nhập vào từ người dùng thì regex phải thật hoàn hảo. Còn nếu phân tích 1 file dữ liệu mà bạn đã biết chắc mã nguồn tạo ra file đó theo cách thức như thế nào thì regex có thể ở mức vừa đủ mà thôi.

Sử dụng lớp ký tự phủ định thay cho Dot

Hãy lấy 1 VD để tìm hiểu tại sao. Giả sử ta cần tìm 1 chuỗi bao bởi ” “. Nghe có vẻ đơn giản như đang giỡn. Chuỗi này có thể có bao nhiêu ký tự tuỳ thích, do đó “.*” có vẻ ổn. Dấu . khớp với bất kỳ ký tự nào, còn dấu * sẽ cho phép . có thể dc nhắc lại bao nhiêu lần tuỳ thích, kể cả 0 lần. Nếu áp dụng regex này vào chuỗi Put a “string” between double quotes, nó sẽ trả về kết quả đúng như mong đợi: “string”. Giờ hãy thử với chuỗi Houston, we have a problem with “string one” and “string two”. Please respond. Và kết quả là “string one” and “string two”, hỏng zồi ;D. Lý do là vì * vốn có bản tính “tham lam” (greedy). Chi tiết thế nào thì hồi sau sẽ rõ. VD này cho thấy ko nên lạm dụng dấu chấm.

Page 18: Vietnam Coding

Trong VD tìm ngày ở trên, ta cải tiến regex bằng cách sử dụng lớp ký tự thay cho dấu chấm. Giờ ta cũng làm như vậy. Ta ko muốn có số lượng bất kỳ các ký tự bất kỳ trong cặp dấu ” “, mà muốn có số lượng bất kỳ các ký tự ko phải là ” hoặc newline trong cặp dấu ” “. Do đó regex đúng sẽ là “[^"\r\n]*”.