Mặc dầu bạn có thể nghĩ về số và xâu như những vật rất khác nhau, nhưng Perl dùng chúng gần như là giống nhau, cho nên c húng ta sẽ nghiên c ứu c ả hai. Một giá trị vô hướng c ó thể được tác động bởi các toán tử (giống như phép c ộng hay ghép), và kết quả trả về nói chung
AMBIENT/
Chủ đề:
Nội dung Text: Learning Perl - Giới thiệu qua về Perl part 3
- print "Chao Jenny! Lam bai tap di chu!\n";
} else {
print "Xin chao ban $name!\n"; #chao thuong thuong
print "Tu bi mat la gi? ";
$guess = ;
chop($guess);
while (! good_word($name, $guess)) {
print "Sai roi thu lai di. Tu bi mat la gi? ";
$guess = ;
chop($guess);
} #ket thuc cua while
}
sub good_word {
my ($somename, $someguess) = @_; #lay cac tham so
$somename =~ s/\W.*//; #bo moi thu sau tu dau tien
$somename =~ tr/A-Z/a-z/; #chuyen thanh chu thuong
if ($somename eq "jenny") { #huh, Jenny, khong doan nua
return 1; #tra ve True
} elsif (($words{$somename} || "none") eq $someguess) {
return 1; #tra ve True
} else {
return 0; #tra ve False
}
} #ket thuc good_word
sub init_words {
open(WORDSLIST, "wordslist");
while ($name = ) {
chop($name);
$word = ;
chop($word);
$words{$name} = $word;
}
close(WORDSLIST);
} #ket thuc init_words
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 số nào được truyền c ả, cho nên c húng ta
được tự do bỏ đi dấu ( ) (tuy nhiên tôi không khuyên bạn làm điều này). Cũng vậy, giá trị trả về tử
init_words không được dùng biểu thức nào hết, thì c ũng là t ốt vì ta đã không trả về điều gì đáng kể.
1.6.11 Đảm bảo một lượng an toàn (nhỏ thôi- nhưng an toàn vẫn hơn)
Xếp bạn quyết định: "Danh sách c ác từ bí mật phải thay đổi ít nhất một lần m ỗi tuần". c húng ta
không thể buộc danh sách này khác đi, nhưng c húng ta có t hể ít nhất c ũng đưa ra một c ảnh báo nếu
danh sác h từ bí mật còn c hưa được thay đổi trong hơn một tuần.
Nơi tốt nhất để thực hiện việc kiểm tra là bên trong c hương trình c on init_words . Toán tử - M c ho lại
"tuổi" tính theo ngày từ một tệp hay tước hiệu tệp đã được thay đổi từ lần t rước , c ho nên ta c hỉ c ần
xem liệu giá trị này có lớn hơn 7 hay không đối với t ước hiệu tệp WORDSLIST:
sub init_words {
open(WORDSLIST, "wordslist");
if (-M WORDSLIST > 7) {
die "Rat tiec, danh sach tu cu hon 7 ngay roi!";
}
while ($name = ) {
chop($name);
- chop($name);
$word = ;
chop($word);
$words{$name} = $word;
}
close(WORDSLIST);
}
Giá trị của - M WORDSLIST được so sánh với 7, và nếu lớn hơn, thế thì ta vi phạm điều lệ an toàn rồi!
Tại đây, ta thấy một toán tử mới: toán tử die. die c hỉ làm một c ông việc đơn giản là một thông báo lên
thiết bị xuất và kết thúc c hương trình.
Phần c òn lại c ủa c hương trình vẫn không đổi, c ho nên tôi sẽ không lặp lại nó ở đây. Bên c ạnh việc lấy
số ngày c ủa tệp, ta c ũng c ó thể tìm ra người c hủ của nó, kíc h cỡ, thời gian thâm nhập, và mọi thứ
khác mà UNIX duy trì về tệp. Nhiều điều hơn được trình bầy trong Chương 10.
1.6.12 Cảnh báo ai đó khi mọi việc đi sai
Ta hãy xem ta có thể làm c ho hệ thống bị sa lầy thế nào khi ta gửi một mẩu t hư điện tử mỗi lần c ho
một ai đó đoán từ bí mật c ủa họ không đúng. Ta c ần sửa đổi mỗi c hương trình c on good_word vì ta c ó
tất c ả t hông tin ngay đây. Thư sẽ được gửi c ho bạn nếu bạn cũng c ấp địa c hỉ email của mình vào c hỗ
"dia_c hi_c ua_ban_o_day". Đây là điều ta phải làm - ngay t rước khi trả 0 về từ c hương trình c on, 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 {
my ($somename, $someguess) = @_; #lay cac tham so
$somename =~ s/\W.*//; #bo moi thu sau tu dau tien
$somename =~ tr/A-Z/a-z/; #chuyen thanh chu thuong
if ($somename eq "jenny") { #huh, Jenny, khong doan nua
return 1; #tra ve True
} elsif (($words{$somename} || "none") eq $someguess) {
return 1; #tra ve True
} else {
open(MAIL, "|mail dia_chi_cua_ban_o_day");
print MAIL "tin xau: $somename da doan $someguess\n”;
return 0; #tra ve False
}
} #ket thuc good_word
Câu lệnh mới thứ nhất ở đây là open(), mà có một kí hiệu | t rong tên tệp. Đây là một chỉ dẫn đặc biệt
rằng ta đang mở một lệnh ngoài t hay vì một tệp. Vì nó nằm tại chỗ bắt đầu c ủa lệnh ngoài nên c ó
nghĩa là ta đang mở một lệnh ngoài để ta c ó thể ghi-xuất t hông tin-lên nó (nếu bạn đặt | t ại cuối thay
vì đầu t hì c ó nghĩa là bạn ở lệnh ngoài và đọc -thu tóm kết quả output c ủa lệnh).
Câu lệnh tiếp, print , c ó một tước hiệu tệp (MAIL) giữa từ khoá print v à giá trị được in c ho ta biết là kết
quả s ẽ được ghi lên MAIL t hay vì STDOUT .
Cuối c ùng, ta đóng tước hiệu tệp MAIL, c ũng c ó nghĩa là kết thúc việc gởi dữ liệu ra lệnh ngoài.
Perl c ó thể cũng gọi c ả c ác lệnh với việc kiểm soát chính xác trên danh sác h t ham số, mở c ác tước
hiệu tệp, hay thậm c hí lôi ra c ả bản sao của c hương trình hiện tại, và thực hiện hai (hay nhiều) bản
sao song song. Tất cả những điều này sẽ được mô tả trong Chương 14:Quản lí tiến trình.
1.6.13 Nhiều tệp từ bí mật trong thư mục hiện tại
Ta hãy thay đổi định nghĩa c ủa tên tệp từ bí mật một c hút. Thay vì tệp được đặt tên là wordslist , thì
ta hãy tìm bất kì tệp nào t rong thư mục hiện tại mà c ó tận c ùng là .sec ret . Nếu dùng c ác kịc h bản
shell, bạn có thể sẽ dùng lệnh sau: echo *.secret để thu được một liệt kê ngắn gọn cho tất c ả c ác
tên này. Như lát nữa bạn sẽ thấy, Perl dùng một cú pháp tên c hùm tương t ự. Ta lấy lại định nghĩa
init_words:
sub init_words {
- sub init_words {
while ($filename = ) {
open (WORDSLIST, $filename);
if (-M WORDSLIST
- Ở (*) t a biết 3 điều: tên c ủa tệp (trong $filename), tên một ai đó (trong $name), và từ bí mật c ủa
người đó (trong $word). Sau đây là c hỗ để c húng ta dùng c ông cụ sinh báo c áo của Perl. Ta định nghĩa
một định dạng ở đâu đó trong chương trình (thông t hường gần cuối, giống như chương trình con):
format STDOUT =
@
- Hai dòng cuối c ủa định dạng này c ũng không c hứa t rường nào, c ho nên chúng được sao trực tiếp ra
output. Do vậy định dạng này sinh ra bốn dòng, một trong đó c ó một phần bị thay đổi qua mỗi trang.
Chỉ c ần thêm định nghĩa này vào chương trình trước là nó làm việc . Perl để ý đến định dạng đầu trang
tự động. Perl c ũng c ó c ác trường được c anh giữa hay c ăn lề phải, và hỗ trợ c ho c ả c anh lề 2 bên.
Chúng ta sẽ nói kỹ về vấn đề này trong Chương 11: Định dạng.
1.6.16 Làm "ấn tượng" cho danh sách từ cũ
Khi ta đọc qua các tệp * .sec ret t rong thư mục hiện t ại, ta c ó thể tìm thấy các tệp quá c ũ (ta thường
bỏ qua những tệp này từ t rước ). Ta hãy đi thêm một bước nữa - ta sẽ đổi tên c húng thành
*.sec ret.old để c ho khi nhìn vào ta sẽ thấy ngay tệp những t ệp nào quá c ũ. Sau đây là c ách thức thể
hiện cho c hương trình c on init_words với sửa đổi này:
sub init_words {
while ($filename = ) {
open (WORDSLIST, $filename);
if (-M WORDSLIST
- này là tên thông thường c ho DBM được gọi là lastdb). Các thuộc tính về tệp UNIX được dùng cho hai
tệp này nếu c ác tệp đó phải được tạo ra (khi chúng c hưa có sẵn) là 0666. 0666 này c ó nghĩa là bất kì
ai c ũng có thể đọc hay ghi lên tệp. (bạn xem thêm phần manual c ủa lệnh c homod).
Câu lệnh thứ hai c hỉ ra rằng chúng ta dùng mảng băm đã được ánh xạ này giống hệt như mảng băm
thông thường. Tuy nhiên, việc tạo ra hay c ập nhật một phần tử c ủa mảng sẽ tự động c ập nhật tệp đĩa
tạo nên DBM. Điều này c ho mảng băm c ó một c uộc sống trải bên ngoài lời gọi hiện thời c ủa chương
trình-một sự bền lâu c ủa riêng nó.
Câu lệnh thứ ba ngắt mảng băm ra khỏi DBM, giống hệt t hao tác đóng tệp c lose().
Bạn c ó thể chèn thêm ba c âu lệnh này vào ngay đầu c ác định nghĩa c hương trình c on. Mặc dầu c ác
câu lệnh được c hèn thêm này duy trì c ơ sở dữ liệu là tốt (và t hậm c hí c òn tạo ra nó trong lần ddầu),
chúng ta vẫn không c ó c ác h nào để xem xét thông tin trong đó. Để làm điều đó, ta c ó thể tạo ra một
chương trình chỏ t ác h biệt trông đại thể như thế này:
#!/usr/bin/perl
dbmopen(%last_good, "lastdb", 0666);
foreach $name (sort keys(%last_good) {
$when = $last_good{$name};
$hours = (time - $when) / 3600; #tinh gio da qua
write;
}
dbmclose(%last_good);
format STDOUT =
User @