Lời giới thiệu
Cuốn sách này nói về gì
1: Giới thiệu
Lịch sử của Perl
Mục đích của Perl
Những điều có sẵn
Hỗ trợ
Các khái niệm cơ bản
Dạo qua Perl
Bài tập
2:Dữ liệu vô hớng
Dữ liệu vô hớng là gì?
Số
Xâu
Toán tử
Biến vô hớng
Toán tử trên biến vô hớng
<STDIN> nh giá trị vô hớng
Đa ra với print()
Giá trị undef
Bài tập
3: Dữ liệu mảng và danh sách
Mảng là gì?
Biểu diễn hằng kí hiệu
Biến
Toán tử
Vô hớng và ngữ cảnh mảng
<STDIN> nh một mảng
Nội suy biến thiên của mảng
Bài tập
4: Cấu trúc điều khiển
Đối
Biến cục bộ trong hàm
Bài tập
9: Các cấu trúc điều khiển
Toán tử last
Toán tử next
Toán tử redo
Khối có nhãn
Bộ sửa đổi biểu thức
&&, || và ?: xem nh các cấu trúc điều khiển
Bài tập
10: Giải quyết tệp và kiểm thử tệp
Tớc hiệu tệp là gì?
Mở và đóng một tớc hiệu tệp
Một chút tiêu khiển: die()
Dùng tớc hiệu tệp
Kiểm tra tệp -x
Toán tử stat() và lstat()
Dùng _Filehandle
Bài tập
11: Định dạng
Định dạng là gì?
Xác định một định dạng
Gọi một định dạng
Nói thêm về Fieldholder
Định dạng đỉnh trang
Đổi giá trị mặc định cho định dạng
Bài tập
12: Thâm nhập danh mục
Đi vòng quanh cây danh mục
Lấy mật hiệu và thông tin nhóm
Đóng và mở gói dữ liệu nhị phân
Lấy thông tin mạng
Lấy thông tin khác
Bài tập
17: Thao tác cơ sở dữ liệu ngời dùng
Cơ sở dữ liệu DBM và mảng DBM
Mở và đóng mảng DBM
Dùng mảng DBM
Cơ sở dữ liệu thâm nhập ngẫu nhiên chiều dài cố định
Cơ sở dữ liệu chiều dài biến thiên (văn bản)
Bài tập
18: Chuyển các ngôn ngữ khác sang Perl
Chuyển Chơng trình awk sang Perl
Chuyển Chơng trình sed sang Perl
Bài tập
A: Trả lời bài tập
B: Cơ sở về nối mạng
Mô hình chỗ cắm
Khách mẫu
Bộ phục vụ mẫu
C: Những chủ đề cha đề cập tới
Bộ gỡ lỗi
Dòng lệnh
Các toán tử khác
Nhiều, nhiều hàm nữa
Nhiều, nhiều biến đặt sẵn nữa
Xâu ở đây
return (từ chơng trình con)
Toán tử eval (và s///e)
dụng 80 trang, một cuốn sách của Nutshell 400 trang, một nhóm tin Usenet với 40
nghìn thuê bao, và bây giờ là đoạn giới thiệu nhẹ nhàng này.
Larry vẫn là ngời bảo trì duy nhất, làm việc trên Perl ngoài giờ khi kết thúc
công việc thờng ngày của mình. Và Perl thì vẫn phát triển.
Một cách đại thể thì lúc mà cuốn sách này đạt tới điểm dừng của nó, Larry sẽ
đa ra bản Perl mới nhất, bản 5.0, hứa hẹn có một số tính năng thờng hay đợc yêu
cầu, và đợc thiết kế lại từ bên trong trở ra. (Larry bảo tôi rằng không còn mấy
dòng lệnh từ lần đa ra trớc, và số ấy cứ ngày càng ít đi mỗi ngày.) Tuy nhiên, cuốn
sách này đã đợc thử với Perl bản 4.0 (lần đa ra gần đây nhất khi tôi viết điều này).
Mọi thứ ở đây đều sẽ làm việc với bản 5.0 và các bản sau của Perl. Trong thực tế,
chơng trình Perl 1.0 vẫn làm việc tốt với những bản gần đây, ngoại trừ một vài
Trong chơng này:
Lịch sử Perl
Mục đích của Perl
Có sẵn
Hỗ trợ
Các khái niệm cơ bản
Dạo qua về Perl
thay đổi lạ cần cho sự tiến bộ.
Mục đích của Perl
Perl đợc thiết kế để trợ giúp cho ngời dùng UNIX với những nhiệm vụ thông
dụng mà có thể rất nặng nề hay quá nhậy cảm với tính khả chuyển đối với trình
vỏ, và cũng quá kì lạ hay ngắn ngủi hay phức tạp để lập trình trong C hay một
ngôn ngữ công cụ UNIX nào khác.
Một khi bạn trở nên quen thuộc với Perl, bạn có thể thấy mình mất ít thời gian
để lấy đợc trích dẫn trình vỏ (hay khai báo C) đúng, và nhiều thời gian hơn để đọc
tin trên Usenet và đi trợt tuyết trên đồi; vì Perl là một công cụ lớn tựa nh chiếc đòn
bẩy. Các cấu trúc chặt chẽ của Perl cho phép bạn tạo ra (với tối thiểu sự om sòm
nhặng sị) một số giải pháp có u thế rất trần lặng hay những công cụ tổng quát.
Cũng vậy, bạn có thể lôi những công cụ này sang công việc tiếp, vì Perl là khả
còn nhiều hơn nữa vào lúc bạn đọc cuốn sách này. Vị trí chính xác và sự có sẵn
của những bản Perl này thì biến động, cho nên bạn phải hỏi quanh (trên nhóm tin
Usenet chẳng hạn) để có đợc thông tin mới nhất. Nếu bạn hoàn toàn không biết gì,
thì một bản cũ của Perl đã có trên đĩa phần mềm CD-ROM UNIX Power Tools,
của Jerry Peek, Tim OReilly và Mike Loukides (OReilly & Associates/ Random
House Co., 1993).
Hỗ trợ
Perl là con đẻ của Larry Wall, và vẫn đang đợc anh ấy nâng niu. Báo cáo lỗi và
yêu cầu nâng cao nói chung đều đợc sửa chữa trong các lần đa ra sau, nhng anh ấy
cũng chẳng có nghĩa vụ nào để làm bất kì cái gì với chúng cả. Tuy thế Larry thực
sự thích thú nghe từ tất cả chúng ta, và cũng làm việc thực sự để thấy Perl đợc
dùng trên qui mô thế giới. E-mail trực tiếp cho anh ấy nói chung đều nhận đợc trả
lời (cho dù đấy chỉ đơn thuần là máy trả lời email của anh ấy), và đôi khi là sự đáp
ứng con ngời.
ích lợi hơn việc viết th trực tiếp cho Larry là nhóm hỗ trợ Perl trực tuyến toàn
thế giới, liên lạc thông qua nhóm tin Usenet comp.lang.perl. Nếu bạn có thể gửi
email trên Internet, nhng cha vào Usenet, thì bạn có thể tham gia nhóm này bằng
cách gửi một yêu cầu tới , yêu cầu sẽ tới một ngời
có thể nối bạn với cửa khẩu email hai chiều trong nhóm, và cho bạn những hứong
dẫn về cách làm việc.
Khi bạn tham gia một nhóm tin, bạn sẽ thấy đại loại có khoảng 30 đến 60 th
mỗi ngày (vào lúc bản viết này đợc soạn thảo) trên đủ mọi chủ đề từ câu hỏi của
ngời mới bắt đầu cho tới vấn đề chuyển chơng trình phức tạp và vấn đề giao diện,
và thậm chí cả một hay hai chơng trình khá lớn.
Nhóm tin gần nh đợc những chuyên gia Perl điều phối. Phần lớn thời gian, câu
hỏi của bạn đều có sự trả lời trong vòng vài phút khi bài tin bạn tới tủ nối Usenet
chính. Bạn hãy thử mức độ hỗ trợ từ nhà sản xuất phần mềm mình a chuộng về
việc cho không này! Bản thân Larry cũng đọc về nhóm khi thời gian cho phép, và
đôi khi đã xen các bài viết có thẩm quyền vào để chấm dứt việc cãi nhau hay làm
sáng tỏ một vấn đề. Sau rốt, không có Usenet, có lẽ không thể có chỗ để dễ dàng
thêm việc phải làm. Hãy hỏi ngời cài đặt Perl về điều này. Các thí dụ trong sách
này giả sử rằng bạn dùng cơ chế thông thờng này.
Perl là một ngôn ngữ phi định dạng kiểu nh C - khoảng trắng giữa các hiệu bài
(những phần tử của chơng trình, nh print hay +) là tuỳ chọn, trừ phi hai hiệu bài đi
với nhau có thể bị lầm lẫn thành một hiệu bài khác, trong trờng hợp đó thì khoảng
trắng thuộc loại nào đó là bắt buộc. (Khoảng trắng bao gồm dấu cách, dấu tab,
dòng mới, về đầu dòng hay kéo giấy.) Có một vài cấu trúc đòi hỏi một loại khoảng
trắng nào đó ở chỗ nào đó, nhng chúng sẽ đợc trỏ ra khi ta nói tới chúng. Bạn có
thể giả thiết rằng loại và khối lợng khoảng trắng giữa các hiệu bài là tuỳ ý trong
các trờng hợp khác.
Mặc dầu gần nh tất cả các chơng trình Perl đều có thể đợc viết tất cả trên một
dòng, một cách điển hình chơng trình Perl cũng hay đợc viết tụt lề nh chơng trình
C, với những phần câu lệnh lồng nhau đợc viết tụt vào hơn so với phần bao quanh.
Bạn sẽ thấy đầy những thí dụ chỉ ra phong cách viết tụt lề điển hình trong toàn bộ
cuốn sách này.
Cũng giống nh bản viết về vỏ, chơng trình Perl bao gồm tất cả các câu lệnh
perl về tệp đợc lấy tổ hợp chung nh mọt trình lớn cần thực hiện. Không có khái
niệm về trình chính main nh trong C.
Chú thích của Perl giống nh chú thích của lớp vỏ (hiện đại). Bất kì cái gì nằm
giữa một dấu thăng (#) tới cuối dòng đều là một chú thích. Không có khái niệm về
chú thích trên nhiều dòng nh C.
Không giống hầu hết các lớp vỏ (nhng giống nh awk và sed), bộ thông dịch
Perl phân tích và biên dịch hoàn toàn chơng trình trớc khi thực hiện nó. Điều này
có nghĩa là bạn không bao giờ nhận đợc lỗi cú pháp từ chơng trình một khi chơng
trình đã bắt đầu chạy, và cũng có nghĩa là khoảng trắng và chú thích biến mất và
sẽ không làm chậm chơng trình. Trong thực tế, giai đoạn biên dịch này bảo đảm
việc thực hiện nhanh chóng của các thao tác Perl một khi nó đợc bắt đầu, và nó
cung cấp động cơ phụ để loại bỏ C nh một ngôn ngữ tiện ích hệ thống đơn thuần
dựa trên nền tảng là C đợc biên dịch.
Việc biên dịch này không mất thời gian - sẽ là phi hiệu quả nếu một chơng
*
.
Khi bạn gọi chơng trình này, phần lõi sẽ gọi bộ thông dịch Perl, phân tích câu
toàn bộ chơng trình (hai dòng, kể cả dòng chú thích đầu tiên) và rồi thực hiện
dạng đã dịch. Thao tác đầu tiên và duy nhất là thực hiện toán tử print, điều này gửi
*
Dấu chấm phẩy có thể bỏ đi khi câu lệnh này là câu lệnh cuối của một khối hay tệp hay eval.
đối của nó ra lối ra. Sau khi chơng trình đã hoàn tất, thì tiến trình Perl ra, cho lại
một mã ra thành công cho lớp vỏ.
Hỏi câu hỏi và nhớ kết quả
Ta hãy thêm một chút phức tạp hơn. Từ Xin chào mọi ngời là một sự đụng
chạm lạnh nhạt và cứng rắn. Ta hãy làm cho chơng trình gọi bạn theo tên bạn. Để
làm việc này, ta cần một chỗ giữ tên, một cách hỏi tên, và một cách nhận câu trả
lời.
Một loại đặt chỗ giữ giá trị (tựa nh một tên) là biến vô hớng. Với chơng trình
này, ta sẽ dùng biến vô hớng $name để giữ tên bạn. Chúng ta sẽ đi chi tiết hơn
trong Chơng 2, Dữ liệu vô hớng, về những gì mà biến này có thể giữ, và những gì
bạn có thể làm với chúng. Hiện tại, giả sử rằng bạn có thể giữ một số hay xâu (dãy
các kí tự) trong biến vô hớng.
Chơng trình này cần hỏi về tên. Để làm điều đó, ta cần một cách nhắc và một
cách nhận cái vào. Chơng trình trớc đã chỉ ra cho ta cách nhắc - dùng toán tử print.
Và cách để nhận một dòng từ thiết bị cuối là với toán tử <STDIN>, mà (nh ta sẽ
dùng nó ở đây) lấy một dòng cái vào. Ta gán cái vào này cho biến $name. Điều
này cho ta chơng trình:
print Tên bạn là gì? : ;
$name = <STDIN> ;
Giá trị của $name tại điểm này có một dấu dòng mới kết thúc (Randal có trong
Randal\n). Để vứt bỏ điều đó, chúng ta dùng toán tử chop(), toán tử lấy một biến vô
hớng làm đối duy nhất và bỏ đi kí tự cuối từ giá trị xâu của biến:
chop($name);
là đợc thực hiện - nếu biểu thức là đúng, đó là khối thứ nhất, nếu không thì đó là
khối thứ hai.
Đoán từ bí mật
Nào, vì chúng ta đã có một tên nên ta hãy để cho một ngời chạy chơng trình
đoán một từ bí mật. Với mọi ngời ngoại trừ Randal, chúng ta sẽ để cho chơng trình
cứ hỏi lặp lại để đoán cho đến khi nào ngời này đoán đợc đúng. Trớc hết ta hãy
xem chơng trình này và rồi xem giải thích:
#! /usr/bin/perl
$secretword = llama; # từ bí mật
print Tên bạn là gì? ;
$name = <STDIN> ;
chop($name);
if ($name eq Randal) {
print Xin chào, Randal! May quá anh ở đây!\n;
} else {
print Xin chào, $name!\n ; # chào thông th ờng
print Từ bí mật là gì? ;
$guess = <STDIN>;
chop($guess);
while ($guess ne $secrectword) {
print Sai rồi, thử lại đi. Từ bí mật là gì?;
$guess = <STDIN>;
chop($guess);
}
}
Trớc hết, ta định nghĩa từ bí mật bằng việc đặt nó vào trong biến vô hớng khác,
$secretword. Sau khi đón chào, một ngời (không phải Randal) sẽ đợc yêu cầu (với
một câu lệnh print khác) đoán chữ. Lời đoán rồi đợc đem ra so sánh với từ bí mật
bằng việc dùng toán tử ne, mà sẽ cho lại đúng nếu các xâu này không bằng nhau
(đây là toán tử logic ngợc với toán tử eq). Kết quả của việc so sánh sẽ kiểm soát
$name = <STDIN> ;
chop($name);
if ($name eq Randal) {
print Xin chào, Randal! May quá anh ở đây!\n;
} else {
print Xin chào, $name!\n; # chào thông thờng
print Từ bí mật là gì? ;
$guess = <STDIN>;
chop($guess);
$i = 0; # thử từ này trớc hết
$correct = có thể; # từ đoán có đúng hay không?
while ($correct eq $guess) { # cứ kiểm tra đến khi biết
if ($words[$i] eq $guess) { # đúng không
$correct = có; # có
} elsif ($i < 2) { # cần phải xét thêm từ nữa?
$i = $i + 1; # nhìn vào từ tiếp lần sau
} else # hết rồi, thế là hỏng
print Sai rồi, thử lại đi. Từ bí mật là gì?;
$guess = <STDIN>;
chop($guess);
$i = 0; # bắt đầu kiểm tra từ đầu lần nữa
}
} # kết thúc của while not correct
} # kết thúc của not Randal
Bạn sẽ để ý rằng chúng ta đang dùng biến vô hớng $correct để chỉ ra rằng
chúng ta vẫn đang tìm ra mật hiệu đúng, hay rằng chúng ta không tìm thấy.
Chơng trình này cũng chỉ ra khối elsif của câu lệnh if-then-else. Không có lệnh
nào tơng đơng nh thế trong C hay awk - đó là việc viết tắt của khối else cùng với
một điều kiện if mới, nhng không lồng bên trong cặp dấu ngoặc nhọn khác. Đây
chính là cái rất giống Perl để so sánh một tập các điều kiện trong một dây chuyền
tham khảo này là oyster, tơng tự nh điều ta đã làm trớc đây với mảng khác. Cũng
nh trớc đây, khoá có thể là bất kì biểu thức nào, cho nên đặt $person với betty và
tính $words{$person} cũng cho oyster.
Gắn tất cả những điều này lại ta đợc chơng trình nh thế này:
#! /usr/bin/perl
%words = (fred, camel, barney, llama,
betty, oyster, wilma, oyster) ;
print Tên bạn là gì? ;
$name = <STDIN> ;
chop($name);
if ($name eq Randal) {
print Xin chào, Randal! May quá anh ở đây!\n;
} else {
print Xin chào, $name!\n; # chào thông thờng
$secretword = $words{$name}; # lấy từ bí mật
print Từ bí mật là gì? ;
$guess = <STDIN>;
chop($guess);
while ($correct ne $secretwords) {
print Sai rồi, thử lại đi. Từ bí mật là gì? ;
$guess = <STDIN>;
chop($guess);
} # kết thúc của while
} # kết thúc của not Randal
Bạn hãy chú ý nhìn vào từ bí mật. Nếu không tìm thấy từ này thì giá trị của
$secretword sẽ là một xâu rỗng
*
, mà ta có thể kiểm tra liệu ta có muốn xác định
một từ bí mật mặc định cho ai đó khác không. Đây là cách xem nó:
[... phần còn lại của chơng trình đã bị xoá...]
trong biểu thức chính qui không phải là một kí tự khác. Điều này làm thay đổi
biểu thức chính qui thành /^randal\b/i, mà có nghĩa là randal tại đầu xâu, không có
kí tự hay chữ số nào theo sau, và chấp nhận cả hai kiểu chữ hoa thờng.
Khi gắn tất cả lại với phần còn lại của chơng trình thì nó sẽ giống nh thế này:
#! /usr/bin/perl
%words = (fred, camel, barney, llama,
betty, oyster, wilma, oyster) ;
print Tên bạn là gì? ;
$name = <STDIN> ;
chop($name);
if ($name =~ /^randal\b/i ) {
print Xin chào, Randal! May quá anh ở đây!\n;
} else {
print Xin chào, $name!\n; # chào thông thờng
$secretword = $words{$name}; # lấy từ bí mật
if ($secretword eq ) { # ấy, không thấy
$secretword = đồ cáu kỉnh; # chắc chắn, sao không là vịt?
}
print Từ bí mật là gì? ;
$guess = <STDIN>;
chop($guess);
while ($correct ne $secretwords) {
print Sai rồi, thử lại đi. Từ bí mật là gì?;
$guess = <STDIN>;
chop($guess);
} # kết thúc của while
} # kết thúc của not Randal
Nh bạn có thể thấy, chơng trình này khác xa với chơng trình đơn giản Xin
chào, mọi ngời, nhng nó vẫn còn rất nhỏ bé và làm việc đợc, và nó quả làm đợc tí
chút với cái ngắn xíu vậy. đây chính là cách thức của Perl.
các kí tự để tìm và một danh sách các kí tự để thay thế chúng. Với thí dụ của ta, để
đặt nội dung của $name thành chữ thờng, ta dùng:
$name =~ tr/A-Z/a-z/;
Các dấu sổ chéo phân tách các danh sách kí tự cần tìm và cần thay thế. Dấu
gạch ngang giữa A và Z thay thế cho tất cả các kí tự nằm giữa, cho nên chúng ta
có hai danh sách, mỗi danh sách có 26 kí tự. Khi toán tử tr tìm thấy một kí tự từ
một xâu trong danh sách thứ nhất thì kí tự đó sẽ đợc thay thế bằng kí tự tơng ứng
trong danh sách thứ hai. Cho nên tất cả các chữ hoa A trở thành chữ thờng a, và cứ
nh thế
*
.
Gắn tất cả lại với mọi thứ khác sẽ cho kết quả trong:
#! /usr/bin/perl
%words = (fred, camel, barney, llama,
betty, oyster, wilma, oyster) ;
print Tên bạn là gì? ;
$name = <STDIN> ;
chop($name);
$original_name = $name; # cất giữ để chào mừng
$name =~ s/\W.*//; # bỏ mọi thứ sau từ đầu
$name =~ tr/A-Z/a-z/; # mọi thứ thành chữ thờng
if ($name eq randal ) {
print Xin chào, Randal! May quá anh ở đây!\n;
} else {
print Xin chào, $original_name!\n; # chào thông thờng
$secretword = $words{$name}; # lấy từ bí mật
if ($secretword eq ) { # ấy, không thấy
$secretword = đồ cáu kỉnh; # chắc chắn, sao không là vịt?
}
print Từ bí mật là gì? ;
sánh một từ bí mật với từ đã biết). Chúng ta có thể làm điều này cho rõ ràng, hoặc
có thể bởi vì một ngời đang viết phần cao cấp còn ngời khác thì viết (hay đã viết)
phần chi tiết.
Perl cung cấp các chơng trình con có tham biến và giá trị cho lại. Một chơng
trình con đợc xác định một khi nào đó trong chơng trình, và rồi có thể đợc dùng
lặp đi lặp lại bằng việc gọi từ bên trong bất kì biểu thức nào.
Với chơng trình nhỏ nhng tăng trởng nhanh của chúng ta, ta hãy tạo ra một ch-
ơng trình con tên là &good_word (tất cả các tên chơng trình con đều bắt đầu với
một dấu và &) mà nhận một tên đã sạch và một từ đoán, rồi cho lại true nếu từ đó
là đúng, và cho lại false nếu không đúng. Việc xác định chơng trình con đó tựa
nh thế này:
sub good_word {
local($somename, $someguess) = @_; # tên tham biến
$somename =~ s/\W.*//; # bỏ mọi thứ sau từ đầu
$somename =~ tr/A-Z/a-z/; # mọi thứ thành chữ thờng
if ($somename eq randal) { # không nên đoán
1; #giá trị cho lại là true
} elsif (($words{$somename} || đồ cáu kỉnh) eq $someguess) {
1; # giá trị cho lại là true
} else {
0; # cho lại giá trị false
}
}
Trớc hết, việc định nghĩa ra một chơng trình con bao gồm một từ dành riêng
sub đi theo sau là tên chơng trình con (không có dấu và &) tiếp nữa là một khối
mã lệnh (đợc định biên bởi dấu ngoặc nhọn). Định nghĩa này có thể để vào bất kì
đâu trong tệp chơng trình, nhng phần lớn mọi ngời thích để chúng vào cuối.
Dòng đầu tiên trong định nghĩa đặc biệt này là một phép gán làm việc sao các
giá trị của hai tham biến của chơng trình này vào hai biến cục bộ có tên
$somename và $someguess. (local() xác định hai biến là cục bộ cho chơng trình
print Xin chào, Randal! May quá anh ở đây!\n;
} else {
print Xin chào, $original_name!\n; # chào thông thờng
print Từ bí mật là gì? ;
$guess = <STDIN>;
chop($guess);
while ( ! &good_word($name, $guess)) {
print Sai rồi, thử lại đi. Từ bí mật là gì?;
$guess = <STDIN>;
chop($guess);
}
}
[ ... thêm vào định nghĩa của &good_word ở đây ...]
Chú ý rằng chúng ta đã quay trở lại với biểu thức chính qui để kiểm tra Randal,
vì bây giờ không cần kéo một phần tên thứ nhất và chuyển nó thành chữ thờng,
chừng nào còn liên quan tới chơng trình chính.
Sự khác biệt lớn là chu trình while có chứa &good_word. Tại đây, chúng ta thấy
một lời gọi tới chơng trình con, truyền cho nó hai đối, $name và $guess. Bên trong
chơng trình con này, giá trị của $somename đợc đặt từ tham biến thứ nhất, trong tr-
ờng hợp này là $name. Giống thế, $someguess đợc đặt từ tham biến thứ hai,
$guess.
Giá trị do chơng trình con cho lại (hoặc 1 hoặc 0, nhớ lại định nghĩa đã nêu tr-
ớc đây) là âm với toán tử tiền tố ! (phép phủ định logic). Nh trong C, toán tử này
cho lại đúng nếu biểu thức đi sau là sai, và ngợc lại. Kết quả của phép phủ định
này sẽ kiểm soát chu trình while. Bạn có thể đọc điều này là trong khi không phải
là từ đúng.... Nhiều chơng trình Perl viết tốt đọc rất giống tiếng Anh, đa lại cho
bạn một chút tự do với Perl hay tiếng Anh. (Nhng bạn chắc chắn không đoạt giải
Pulitzer theo cách đó.)
Chú ý rằng chơng trình con này giả thiết rằng giá trị của mảng %words đợc ch-
ơng trình chính đặt. Điều này không đặc biệt là hay, nhng chẳng có gì so sánh đợc
một vài lần ôn lại cuộc đi dạo này), tôi có thể thay đổi nơi cất giữ danh sách từ,
hay thậm chí định dạng của danh sách.
Định dạng đợc chọn bất kì cho danh sách từ là một khoản mục trên một dòng,
với tên và từ, luân phiên. Cho nên, với cơ sở dữ liệu hiện tại của chúng ta, chúng ta
có cái tựa nh thế này:
fred
camel
barney
llama
betty
oyster
wilma
oyster
Toán tử open() tạo ra một tớc hiệu tệp có tên WORDSLIST bằng cách liên kết
nó với một tệp mang tên wordslist trong danh mục hiện tại. Lu ý rằng tớc hiệu tệp
không có kí tự là lạ phía trớc nó nh ba kiểu biến vẫn có. Cũng vậy, tớc hiệu tệp nói
chung là chữ hoa - mặc dầu chúng không nhất thiết phải là nh thế - bởi những lí do
sẽ nêu chi tiết về sau.
Chu trình while đọc các dòng từ tệp wordslist (qua tớc hiệu tệp WORDSLIST)
mỗi lần một dòng. Mỗi dòng đều đợc cất giữ trong biến $name. Khi đạt đến cuối
tệp thì giá trị cho lại bởi toán tử <WORDSLIST> là xâu rỗng
*
, mà sẽ sai cho cho
trình while, và kết thúc nó. Đó là cách chúng ta đi ra ở cuối.
Mặt khác, trờng hợp thông thờng là ở chỗ chúng ta đã đọc một dòng (kể cả
dấu dòng mới) vào trong $name. Trớc hết, ta bỏ dấu dòng mới bằng việc dùng toán
tử chop(). Rồi, ta phải đọc dòng tiếp để lấy từ bí mật, giữ nó trong biến $word. Nó
*
Về mặt kĩ thuật thì đấy lại là undef, nhng cũng đủ gần cho thảo luận này
nữa cũng phải bỏ dấu dòng mới đi.
chop ($name);
$word = <WORDSLIST>;
chop($word);
$words{$name} = $word;
}
close (WORDSLIST);
}
sub good_word {
local($somename, $someguess) = @_; # tên tham biến
$somename =~ s/\W.*//; # bỏ mọi thứ sau từ đầu
$somename =~ tr/A-Z/a-z/; # mọi thứ thành chữ thờng
if ($somename eq randal) { # không nên đoán
1; #giá trị cho lại là true
} elsif (($words{$somename} || đồ cáu kỉnh) eq $someguess) {
1; # giá trị cho lại là true
} else {
0; # cho lại giá trị false
}
}
Bây giờ nó bắt đầu trông giống một chơng trình trởng thành hoàn toàn. Chú ý
đến dòng thực hiện đợc đầu tiên là lời gọi tới &init_words. Không có tham biến nào
đợc truyền cả, cho nên chúng ta đợc tự do bỏ đi dấu ngoặc tròn. Cũng vậy, giá trị
cho lại không đợc dùng trong tính toán thêm, thì cũng là tốt vì ta đã không cho lại
điều gì đáng để ý. (Giá trị của close() thông thờng là đúng.)
Toán tử open() cũng đợc dùng để mở các tệp đa ra, hay mở chơng trình nh tệp
(đã đợc biểu diễn ngắn gọn). Tuy thế, việc vét hết về open() sẽ đợc nêu về sau
trong cuốn sách này, trong Chơng 10, Tớc hiệu tệp và kiểm tra tệp.
Đảm bảo một lợng an toàn giản dị
Danh sách các từ bí mật phải thay đổi ít nhất một lần mỗi tuần! ông Trởng
ban danh sách từ bí mật kêu lên. Thôi đợc, chúng ta không thể buộc danh sách này
Cảnh báo ai đó khi mọi việc đi sai
Ta hãy xem ta có thể làm cho hệ thống bị sa lầy thế nào khi ta gửi một mẩu th
điện tử mỗi lần cho một ai đó đoán từ bí mật của họ không đúng. Ta cần sửa đổi
mỗi chơng trình con &good_word (nhờ có tính mô đun) vì ta có tất cả thông tin
ngay đây.
Th sẽ đợc gửi cho bạn nếu bạn gõ địa chỉ th của riêng mình vào chỗ mà chơng
trình nói Địa chỉ bạn ở đây. Đây là điều ta phải làm - ngay trớc khi trả 0 về từ
chơng trình con, ta tạo ra một tớc hiệu tệp mà thực tại là một tiến trình (mail),
giống nh:
sub good_word {
local($somename, $someguess) = @_; # tên tham biến
$somename =~ s/\W.*//; # bỏ mọi thứ sau từ đầu
$somename =~ tr/A-Z/a-z/; # mọi thứ thành chữ thờng
if ($somename eq randal) { # không nên đoán
1; #giá trị cho lại là true
} elsif (($words{$somename} || đồ cáu kỉnh) eq $someguess) {
1; # giá trị cho lại là true
} else {
open(MAIL, |mail Địa_chỉ_bạn_ở_đây);
print MAIL tin xấu: $somename đã đoán $someguess\n;
0; # cho lại giá trị false
}
}
Câu lệnh mới thứ nhất ở đây là open(), mà có một kí hiệu đờng ống (|) trong
tên tệp. Đây là một chỉ dẫn đặc biệt rằng ta đang mở một chỉ lệnh thay vì một tệp.
Vì đờng ống là tại chỗ bắt đầu của chỉ lệnh nên ta đang mở một chỉ lệnh để ta có
thể ghi lên nó. (nếu bạn đặt đờng ống tại cuối thay vì đầu thì bạn có thể đọc cái ra
của chỉ lệnh.)
Câu lệnh tiếp, print, chỉ ra rằng một tớc hiệu tệp giữa từ khoá print và giá trị đợc
in ra chọn tớc hiệu tệp đó làm cái ra, thay vì STDOUT
while ($name = <WORDSLIST>) {
chop ($name);
$word = <WORDSLIST>;
chop($word);
$words{$name} = $word;
}
}
close (WORDSLIST);
}
}
Trớc hết, tôi đã bao một chu trình while mới quanh phần lớn chơng trình con
của bản cũ. Điều mới ở đây là toán tử <*.secret>. Điều này đợc gọi là núm tên tệp,
bởi lí do lịch sử. Nó làm việc rất giống <STDIN>, ở chỗ mỗi lần đợc thâm nhập
tới, nó đều cho lại giá trị tiếp: tên tệp kế tiếp sánh với mẫu của vỏ, trong trờng hợp
này là *.secret. Khi không có tên tệp thêm nữa đợc cho lại thì núm tên tệp cho xâu
rỗng
*
.
Cho nên nếu danh mục hiện tại có chứa fred.secret và barney.secret, thì
$filename là barney.secret ở bớc đầu qua chu trình while (tên tới theo trật tự sắp của
*
Lại undef lần nữa