Ngôn Ngữ Lập Trình C#
}// end Main
}// end class
}// end namespace
Ví dụ minh họa 10.8 bắt đầu bằng việc tạo một chuỗi đơn giản để tìm kiếm như sau:
string string1 = “10:20:30 127.0.0.0 Dolphin.net”;
Chuỗi này có thể được tìm thấy trong nội dung của các tập tin log ghi nhận các thông tin ở
web server hay từ các kết quả tìm kiếm được trong cơ sở dữ liệu. Trong ví dụ đơn giản này có
ba cột, một cột đầu tiên ghi nhận thời gian, cột thứ hai ghi nhận địa chỉ IP, và cột thứ ba ghi
nhận địa chỉ web. Mỗi cột được ngăn cách bởi khoảng trắng. Dĩ nhiên là trong các ứng dụng
thực tế ta phải giải quyết những vấn đề phức tạp hơn nữa, chúng ta có thể cần phải thực hiện
việc tìm kiếm phức tạp hơn và sử dụng nhiều ký tự ngăn cách hơn nữa.
Trong ví dụ này, chúng ta mong muốn là tạo ra một đối tượng Regex để tìm kiếm chuỗi con
yêu cầu và phân chúng vào trong ba nhóm: time, địa chỉ IP, và địa chỉ web. Biểu thức quy tắc
ở đây cũng khá đơn giản, do đó cũng dễ hiểu.
Ở đây chúng ta quan tâm đến những ký tự tạo nhóm như:
<?<time>
Dấu ngoặc đơn dùng để tạo nhóm. Mọi thứ giữa dấu ngoặc mở trước dấu ? và dấu ngoặc
đóng (trong trường hợp này sau dấu +) được xác định là một nhóm. Chuỗi ?<time> định ra
tên của nhóm và liên quan đến tất cả các chuỗi ký tự được so khớp theo biểu thức quy tắc (\d|
\:)+)\s. Biểu thức này có thể được diễn giải như: “một hay nhiều con số hay những dấu :
theo sau bởi một khoảng trắng”.
Tương tự như vậy, chuỗi ?<ip> định tên của nhóm ip, và ?<site> là tên của nhóm site.
Tiếp theo là một tập hợp được định nghĩa để nhận tất cả các chuỗi con được so khớp như
sau:
MatchCollection theMatches = theReg.Matches( string1 );
Vòng lặp foreach được dùng để lấy ra các chuỗi con được tìm thấy trong tập hợp.
Nếu chiều dài Length của Match là lớn hơn 0, tức là tìm thấy thì chúng ta sẽ xuất ra chuỗi
được tìm thấy:
Console.WriteLine(“\ntheMatch: {0}”, theMatch.ToString());
Và kết quả của ví dụ là:
theMatch: 10:20:30 127.0.0.0 Dolphin.net
Sau đó chương trình lấy nhóm time từ tập hợp nhóm của Match và xuất ra màn hình bằng các
lệnh như sau:
Console.WriteLine(“time: {0}”, theMatch.Groups[“time”]);
Kết quả là :
Time: 10:20:30
Tương tự như vậy với nhóm ip và site:
Xử Lý Chuỗi
298
.
.
Ngôn Ngữ Lập Trình C#
Console.WriteLine(“IP: {0}”, theMatch.Groups[“ip”]);
// hiển thị địa chỉ web site
Console.WriteLine(“site: {0}”, theMatch.Groups[“site”]);
Ta nhận được kết quả:
IP: 127.0.0.0
Site: Dolphin.net
Trong ví dụ 10.8 trên thì tập hợp Match chỉ so khớp duy nhất một lần. Tuy nhiên, nó có thể so
khớp nhiều hơn nữa trong một chuỗi. Để làm được điều này, chúng ta có thể bổ sung chuỗi
tìm kiếm được lấy từ trong một log file như sau:
String string1 = “10:20:30 127.0.0.0 Dolphin.net ” +
“10:20:31 127.0.0.0 Mun.net ” +
“10:20:32 127.0.0.0 Msn.net ”;
Chuỗi này sẽ tạo ra ba chuỗi con so khớp được tìm thấy trong MatchCollection. Và kết quả ta
có thể thấy được là:
theMatch: 10:20:30 127.0.0.0 Dolphin.net
Time: 10:20:30
IP: 127.0.0.0
site: Dolphin.net
theMatch: 10:20:31 127.0.0.0 Mun.net
Time: 10:20:31
IP: 127.0.0.0
Site: Mun.net
theMatch: 10:20:32 127.0.0.0 Msn.net
time: 10:20:32
IP: 127.0.0.0
Site: Msn.net
Trong ví dụ này phần bổ sung, thì theMatches chứa ba đối tượng Match. Mỗi lần lặp thì các
chuỗi con được tìm thấy (ba lần) và chúng ta có thể xuất ra chuỗi cũng như từng nhóm riêng
bên trong của chuỗi con được tìm thấy.
Sử dụng lớp CaptureCollection
Mỗi khi một đối tượng Regex tìm thấy một chuỗi con, thì môt thể hiện Capture được tạo
ra và được thêm vào trong một tập hợp CaptureCollection. Mỗi một đối tượng Capture thể
hiện một chuỗi con riêng. Mỗi nhóm có một tập hợp các Capture được tìm thấy trong chuỗi
con có liên hệ với nhóm.
Xử Lý Chuỗi
299
.
.
Ngôn Ngữ Lập Trình C#
Thuộc tính quan trọng của đối tượng Capture là thuộc tính Length, đây chính là chiều dài của
chuỗi con được nắm giữ. Khi chúng ta hỏi Match chiều dài của nó, thì chúng ta sẽ nhận được
Capture.Length do Match được dẫn xuất từ Group và đến lượt Group lại được dẫn xuất từ
Capture.
Mô hình kế thừa trong biểu thức quy tắc của .NET cho phép Match thừa hưởng những giao
diện phương thức và thuộc tính của những lớp cha của nó. Theo ý nghĩa này, thì một Group là
một Capture (Group is-a Capture), là một đối tượng Capture đóng gói các ý tưởng về các
nhóm biểu thức. Đến luợt Match, nó cũng là một Group (Match is-a Group), nó đóng gói tất
cả các nhóm biểu thức con được so khớp trong biểu thức quy tắc (Xem chi tiết hơn trong
chương 5: Kế thừa và đa hình).
Thông thường, chúng ta sẽ tìm thấy chỉ một Capture trong tập hợp CaptureCollection; nhưng
điều này không phải vậy. Chúng ta thử tìm hiểu vấn đề như sau, ở đây chúng ta sẽ gặp trường
hợp là phân tích một chuỗi trong đó có nhóm tên của công ty được xuất hiện hai lần. Để
nhóm chúng lại trong chuỗi tìm thấy chúng ta tạo nhóm ?<company> xuất hiện ở hai nơi
trong mẫu biểu thức quy tắc như sau:
Regex theReg = new Regex(@”(?<time>(\d|\:)+)\s” +
@”(?<company>\S+)\s” +
@”(?<ip>(\d|\.)+)\s” +
@”(?<company>\S+)\s”);
Biểu thức quy tắc này nhóm bất cứ chuỗi nào hợp với mẫu so khớp time, và cũng như bất cứ
chuỗi nào theo nhóm ip. Giả sử chúng ta dùng chuỗi sau để làm chuỗi tìm kiếm:
string string1 = “10:20:30 IBM 127.0.0.0 HP”;
Chuỗi này chứa tên của hai công ty ở hai vị trí khác nhau, và kết quả thực hiện chương trình
là như sau:
theMatch: 10:20:30 IBM 127.0.0.0 HP
Time: 10:20:30
IP: 127.0.0.0
Company: HP
Điều gì xảy ra? Tại sao nhóm Company chỉ thể hiện giá trị HP. Còn chuỗi đầu tiên ở đâu hay
là không được tìm thấy? Câu trả lời chính xác là mục thứ hai đã viết chồng mục đầu. Tuy
nhiên, Group vẫn lưu giữ cả hai giá trị. Và ta dùng tập hợp Capture để lấy các giá trị này.
Ví dụ minh họa 10.9: Tìm hiểu tập hợp CaptureCollection.
namespace Programming_CSharp
{
using System;
using System.Text.RegularExpressions;
class Test
Xử Lý Chuỗi
300
.
.
Ngôn Ngữ Lập Trình C#
{
public static void Main()
{
// tạo một chuỗi để phân tích
// lưu ý là tên công ty được xuất
// hiện cả hai nơi
string string1 = “10:20:30 IBM 127.0.0.0 HP”;
// biểu thức quy tắc với việc nhóm hai lần tên công ty
Regex theReg = new Regex(@”(?<time>(\d|\:)+)\s” +
@”(?<company>\S+)\s” +
@”(?<ip>(\d|\ .)+)\s” +
@”(?<company>\S+)\s”);
// đưa vào tập hợp các chuỗi được tìm thấy
MatchCollection theMatches = theReg.Matches( string1 );
// dùng vòng lặp để lấy kết quả
foreach ( Match theMatch in theMatches)
{
if ( theMatch.Length !=0 )
{
Console.WriteLine(“theMatch: {0}”, theMatch.ToString());
Console.WriteLine(“Tme: {0}”, theMatch.Groups[“time”]);
Console.WriteLine(“IP{0}”, theMatch.Groups[“ip”]);
Console.WriteLine(“Company: {0}”, theMatch.Groups[“company”]);
// lặp qua tập hợp Capture để lấy nhóm company
foreach ( Capture cap in theMatch.Groups[“Company”].Captures)
{
Console.WriteLine(“Capture: {0}”, cap.ToString());
}// end foreach
}// end if
}// end foreach
}// end Main
}// end class
}// end namespace
Kết quả:
theMatch: 10:20:30 IBM 127.0.0.0 HP
Time: 10:20:30
IP: 127.0.0.0
Xử Lý Chuỗi
301
.
.
Ngôn Ngữ Lập Trình C#
Company: HP
Capture: IBM
Capture: HP
Trong đoạn vòng lặp cuối cùng:
foreach ( Capture cap in theMatch.Groups[“Company”].Captures)
{
Console.WriteLine(“Capture: {0}”, cap.ToString());
}// end foreach
Đoạn lặp này lặp qua tập hợp Capture của nhóm Company. Chúng ta thử tìm hiểu cách phân
tích như sau. Trình biên dịch bắt đầu tìm một tập hợp cái mà chúng sẽ thực hiện việc lặp.
theMatch là một đối tượng có một tập hợp tên là Groups. Tập hợp Groups có một chỉ mục
đưa ra một chuỗi và trả về một đối tượng Group. Do vậy, dòng lệnh sau trả về một đối tượng
đơn Group:
theMatch.Groups[“company”];
Đối tượng Group có một tập hợp tên là Captures, và dòng lệnh tiếp sau trả về một tập hợp
Captures cho Group lưu giữ tại Groups[“company”] bên trong đối tượng theMatch:
theMatch.Groups[“company”].Captures
Vòng lặp foreach lặp qua tập hợp Captures, và lấy từng thành phần ra và gán cho biến cục
bộ cap, biến này có kiểu là Capture. Chúng ta có thể xem từ kết quả là có hai thành phần
được lưu giữ là : IBM và HP. Chuỗi thứ hai viết chồng lên chuỗi thứ nhất trong nhóm, do vậy
chỉ hiển thị giá trị thứ hai là HP. Tuy nhiên, bằng việc sử dụng tập hợp Captures chúng ta có
thể thu được cả hai giá trị được lưu giữ.
Câu hỏi và trả lời
Câu hỏi 1: Những tóm tắt cơ bản về chuỗi?
Trả lời 1: Chuỗi là kiểu dữ liệu thường được sử dụng nhất trong lập trình. Trong ngôn ngữ
C#, chuỗi được hỗ trợ rất mạnh thông qua các lóp về chuỗi và biểu thức quy tắc. Chuỗi là
kiểu dữ liệu tham chiếu, chứa các ký tự Unicode. Các thao tác trên đối tượng chuỗi không
làm thay đổi giá trị của chuỗi mà ta chỉ nhận được kết quả trả về. Tuy nhiên, C# cung cấp
lớp StringBuilder cho phép thao tác trực tiếp để bổ sung chuỗi.
Câu hỏi 2: Biểu thức quy tắc là gì?
Trả lời 2: Biểu thức quy tắc là ngôn ngữ dùng để mô tả và thao tác văn bản. Một biểu thức
quy tắc thường được áp dụng cho một chuỗi văn bản hay toàn bộ tài liệu nào đó. Kết quả của
việc áp dụng một biểu thức quy tắc là ta nhận được một chuỗi kết quả, chuỗi này có thể là
chuỗi con của chuỗi áp dụng hay có thể là một chuỗi mới được bổ sung từ chuỗi ban đầu.
Câu hỏi 3: Thao tác thường xuyên thực hiện trên một chuỗi là thao tác nào?
Xử Lý Chuỗi
302
.
.