17 Thao tác cơ sở dữ liệu ngời dùng

Một phần của tài liệu Perl và các khái niệm cơ bản (Trang 133 - 139)

Thao tác cơ sở dữ liệu ngời dùng

Cơ sở dữ liệu DBM và mảng DBM

Phần lớn các hệ thống UNIX đều có một th viện chuẩn gọi là DBM. Th viện này cung cấp một tiện nghi quản trị cơ sở dữ liệu đơn giản mà cho phép các chơng trình đợc cất giữ một tuyển tập các cặp khoá-giá trị trong một cặp tệp đĩạ Những tệp này duy trì các giá trị trong cơ sở dữ liệu giữa những lần gọi chơng trình có dùng cơ sở dữ liệu và những chơng trình này có thể bổ sung các giá trị mới, cập nhật các giá trị hiện có, hay xoá các giá trị cũ.

Th viện DBM thì khá đơn giản, nhng lại sẵn có, một số chơng trình hệ thống đã dùng nó cho những nhu cầu khá giản dị của chúng. Chẳng hạn, chơng trình gửi th Berkeley (và các biến thể cùng suy dẫn của nó) cất giữ cơ sở dữ liệu biệt hiệu (ánh xạ của địa chỉ th vào nơi nhận) nh một cơ sở dữ liệu DBM. Phần mềm tin Usenet phủ cập nhất cũng dùng cơ sở dữ liệu DBM để giữ dấu vết cac bài báo hiện tại và mới xem gần đấỵ

Perl cung cấp việc thâm nhập vào cùng cơ chế DBM này thông qua một phơng tiện còn thông minh hơn: mảng kết hợp có thể đợc kết hợp với cơ sở dữ liệu DBM qua một tiến trình tơng tự nh mở một tập. Mảng kết hợp này (còn gọi là mảng

DBM) vậy rồi đợc dùng để thâm nhập và sửa đổi cơ sở dữ liệu DBM. Việc tạo ra

một phần tử mới trong mảng này làm thay đổi ngay lập tức cơ sở dữ liệu DBM. Việc xoá một phần tử sẽ xoá giá trị khỏi cơ sở dữ liệu DBM. Và cứ nh thế.

Kích cỡ, số lợng và loại khoá cùng giá trị trong cơ sở dữ liệu DBM thì có hạn chế, và một mảng DBM có cùng những hạn chế đó. Bạn hãy xem lidbm về chi tiết. Nói chung, nếu bạn giữ cả khoá và giá trị xuống khoảng 1000 kí tự bất kì hay ít hơn thì có lẽ là OK.

Mở và đóng mảng DBM

Để kết hợp một cơ sở dữ liệu DBM với một mảng DBM, bạn hãy dùng toán tử dbmopen() , trông nh thế này:

dbmopen(%ARRAYNAME, “dbmfilename”, $mode);

Tham biến %ARRAYNAME là một mảng kết hợp của Perl. (Nếu mảng này đã có giá trị thì các giá trị đó đều bị bỏ đị) Mảng kết hợp này trở thành đợc nối với cơ sở dữ liệu DBM có tên dbmfilename, thông thờng đợc cất giữ trên đĩa nh một cặp tệp có tên dbmfilenam.dir và dbmfilenamẹpag. Bất kì tên mảng kết hợp hợp pháp nào cũng đều có thể dùng đợc, mặc dầu các tên mảng chỉ toàn chữ hoa thì đợc dùng điển hình do sự tơng tự với tớc hiệu tệp.

các tệp đó cần đợc tạo rạ Con số này điển hình đợc xác định theo hệ tám: giá trị thờng hay dùng nhất là 0644 cho phép mọi ngời chỉ đọc, còn riêng ngời chủ thì có phép đọc-ghị Nếu các tệp này đã tồn tại thì tham biến này không có tác dụng. Chẳng hạn:

dbmopen(%FRED, “mydatabase”, 0644); # mở %FRED lên mydatabase

Lời gọi này kết hợp mảng kết hợp %FRED với các tệp đĩa mydatabasẹdir và

mydatabasẹpag trong danh mục hiện tạị Nếu các tệp này cha tồn tại thì chúng đ-

ợc tạo ra với mốt 0644.

Giá trị cho lại từ dbmopen() là đúng nếu cơ sở dữ liệu có thể mở đợc hay tạo ra đợc, và là sai trong trờng hợp ngợc lại, hệt nh việc gọi open(). Nếu bạn không muốn các tệp này đợc tạo ra thì hãy dùng giá trị $mode của undef. Chẳng hạn:

dbmode (%A, “/etc/xx”, undef) || die “không mở đợc DBM /etc/xx”;

Trong trờng hợp này, nếu tệp /etc/xx.dir và /etc/xx.pag không thể mở đợc thì lời gọi dbmopen() sẽ cho lại sai, thay vì cố gắng tạo ra các tệp nàỵ

Mảng DBM vẫn còn mở trong suốt lúc thực hiện chơng trình. Khi chơng trình kết thúc thì sự kết hợp sẽ kết thúc. Bạn cũng có thể phá vỡ sự kết hợp theo cách thức tơng tự nh việc đóng tớc hiệu tệp, bằng việc dùng toán tử dbmclose():

dbmclose(%A);

Giống nh close(), dbmclose() cho lại sai nếu điều gì đó đi saị

Dùng mảng DBM

Một khi cơ sở dữ liệu đã đợc mở thì các tham khảo tới mảng DBM sẽ đợc ánh xạ vào các tham khảo cơ sở dữ liệụ Việc thay đổi hay bổ sung giá trị vào mảng này tạo ra cho các mục tơng ứng lập tức đợc ghi vào tệp đĩạ Chẳng hạn, một khi %FRED đợc mở trong thí dụ trớc, thì ta có thể thêm vào, xoá bỏ hay thâm nhập vào cơ sở dữ liệu, giống nh thế này:

$FRED{“fred”} = “bedrok”; # tạo ra (cập nhật) một phần tử delete $FRED{“barney”); # bỏ một phần tử của csdl foreach $key (keys %FRED) { # đi qua mọi giá trị

print “$key có giá trị của $FRED{$key}\n”; }

Chu trình cuối phải nhòm qua toàn bộ tệp đĩa hai lần: một lần để thâm nhập vào khoá, và lần thứ hai để tra cứu các giá trị từ khoá. Nếu chúng ta nhòm qua một mảng DBM thì nói chung sẽ hiệu quả về đĩa hơn là dùng toán tử each(), chỉ qua một bớc:

while ( ($key, $value) = each (%FRED)) { print “$key có giá trị của $value\n”; }

Nếu bạn đang thâm nhập vào cơ sở dữ liệu DBM hệ thống, nh các cơ sở dữ liệu đã đợc tạo ra bởi sendmail hay tin Usenet, thì bạn phải biết rằng các chơng trình nói chung đều gắn thêm cái đuôi kí tự NUL (“\0”) vào cuối của các xâu nàỵ

Các trình th viện DBM không cần cái NUL này (chúng giải quyết dữ liệu nhị phân bằng cách đếm byte chứ không phải là xâu kết thúc bằng NUL), và do vậy NUL đ- ợc cất giữ nh một phần của dữ liệụ Do đó bạn phải gắn kí tự NUL vào cuối dữ liệu của mình và bỏ NUL khỏi cuối của giá trị cho lại để cho dữ liệu có nghĩạ Chẳng hạn, để tra cứu merlyn trong cơ sở dữ liệu biệt hiệu, bạn hãy thử một điều gì đó kiểu:

dbmopen(%ALI, “/etc/aliases”, undef) || die “Không biệt hiệủ”; $value = $ALI{“merlyb\0”}; # lu ý NUL đợc thêmvào

chop($value); #loại bỏ NUL đợc thêm vào

print “Th của Randal đợc gắn đầu cho: $value\n”; # in kết quả

Bản UNIX của bạn có thể đa cơ sở dữ liệu biệt hiệu vào /usr/lib thay vì /etc. Bạn phải lục lọi để tìm rạ

Cơ sở dữ liệu thâm nhập ngẫu nhiên chiều dài cố định

Một dạng khác của dữ liệu bền bỉ là các tệp đĩa hớng bản ghi với chiều dài cố định. Trong lợc đồ này, dữ liệu bao gồm một số các bản ghi với chiều dài nhu nhaụ Việc đánh số cho các bản ghi thì không quan trọng hay hay đợc xác định bởi một lợc đồ định chỉ số nào đó.

Chẳng hạn, chúng ta có thể có một loạt các bản ghi mà trong đó dữ liệu có 40 kí tự của họ, một kí tự tên đệm, 40 kí tự tên, và rồi một số nguyên hai byte cho tuổị Mỗi bản ghi vậy là có chiều dài 83 bytẹ Néu ta đọc tất cả dữ liệu trong cơ sở dữ liệu thì ta phải đọc từng chùm 83 byte cho tới khi đến cuốị Nếu ta muốn đi tới bản ghi thứ năm thì ta phải nhẩy qua 4 lầm 83 byte (332 byte) và đọc trực tiếp bản ghi thứ năm.

Perl hỗ trợ cho các chơng trình dùng kiểu tệp đĩa nh thế. Có đôi điều cần biết thêm bên cạnh những điều bạn đã biết:

1. Mở tệp đĩa cho cả đọc và ghi

2. Chuyển quanh tệp này tới một vị trí bất kì

3. Lấy dữ liệu theo độ dài thay vì theo dòng mới tiếp 4. Ghi dữ liệu lên theo các khối chiều dài cố định

Toán tử open() nhận một dấu cộng bổ sung trớc đặc tả hớng vào/ra để chỉ ra rằng tệp thực sự đợc mở cho cả đọc và ghị Chẳng hạn:

open(A, “+<b”); # mở tệp b đọc/ghi (lỗi nếu không có tệp) open(C, “+>d”); # tạo ra tệp d, với thâm nhập đọc/ghi

open(E, “+>>f”); # mở hay tạo tệp f với việc thâm nhập đọc/ghi

Lu ý rằng tất cả những điều ta đã làm đều mới chỉ là bổ sung thêm dấu cộng vào hớng vào/rạ

Một khi ta đã thu đợc việc mở tệp thì ta cần di chuyển quanh nó. Bạn làm điều này với toán tử seek(), cũng nhận cùng ba tham biến nh trình th viện fseek(). Tham biến thứ nhất là tớc hiệu tệp; tham biến thứ hai là cho khoảng chênh, đợc diễn giải

đi kèm với tham biến thứ bạ Thông thờng, bạn muốn tham biến thứ ba là không để cho tham biến thứ hai chọn đợc vị trí tuyệt đối cho lần đọc tiếp hay ghi tiếp lên tệp. Chẳng hạn, đi tới bản ghi thứ năm trên tớc hiệu tệp NAMES (nh đợc mô tả ở trên), bạn có thể làm điều này:

seek(NAMES, 4*83, 0);

Một khi con trỏ tệp đã đợc định vị lại thì việc đa vào hay đa ra tiếp sẽ bắt đầu từ đó. Với việc đa ra, hãy dùng toán tử print. nhng phải chắc chắn rằng dữ liệu bạn viết là đúng chiều dàị Để thu đợc đúng chiều dài, chúng ta có thể gọi tới toán tử pack():

print NAMES pack(“A40Â40s”, $first, $miđle, $last, $age);

Bộ xác định pack() đó cho 49 kí tự đối với $first, một kí tự cho $miđle, 40 kí tự nữa cho $last và số nguyên ngắn (2 byte) cho $age. Điều này tính thành 83 byte chiều dài, và sẽ ghi tại vị trị tệp hiện tại đó.

Cuối cùng, chúng ta cần lấy ra một bản ghi đặc biệt. Mặc dầu toán tử <NAMES> cho lại tất cả dữ liệu từ vị trí hiện tại cho tới dòng mới tiếp, điều đó lại không đúng; dữ liệu đợc giả thiết là chỉ trải trên 83 kí tự và có thể lại không có dòng mới ở đúng chỗ. Thay vì thế, ta dùng toán tử read(), trông và cách làm việc thì hệt nh lời gọi hệ thống UNIX tơng ứng:

$count = read(NAMES, $buf, 83);

Tham biến thứ nhất của read() là tức hiệu tệp. Tham biến thứ hai là biến vô h- ớng mà giữ dữ liệu sẽ đợc đọc. Tham biến thứ ba cho số byte cần đọc. Giá trị cho lại từ read() là số byte thực tế đã đọc - điển hình là cùng số byte đợc yêu cầu trừ phi tớc hiệu tệp không đợc mở hay bạn quá gần tới cuối tệp.

Một khi bạn đã có dữ liệu 83 kí tự thì chỉ cần chặt nó ra thành các thành phần bằng toán tử unpac():

($first, $miđle, $last, $age) = unpack(“A40A40s”, $buf);

L ý rằng các xâu định dạng cho pack và unpack là nh nhaụ Phần lớn các chơng trìnhcất giữ xâu này trong một biến từ đầu trong chơng trình, và thậm chí còn tính chiều dài của bản ghi bằng việc dùng pack() thay vì bỏ rải rác các hằng 83 ở mọi nơi:

$names = “A40AA40s”;

$names_length = length(pack($names)) ; # có thể là 83

Cơ sở dữ liệu (văn bản) chiều dài thay đổi

Nhiều cơ sở dữ liệu hệ thống (và có lẽ phần lớn cơ sở dữ liệu do ngời dùng tạo ra) đều là các chuỗi dòng văn bản con ngời đọc đợc,với một bản ghi trên mỗi dòng. Chẳng hạn, tệp mật hiệu cha một dòng cho mỗi ngời trên hệ thống, và tệp máy chủ chứa một dòng cho mỗi tên máy chủ.

Rất thờng là những cơ sở dữ liệu này đợc cập nhật bằng các trình soan thảo văn bản đơn giản. Việc cập nhật một cơ sở dữ liệu nh vậy bao gồm việc đọc nó tất

cả vào một vùng trung gian (hoặc trong bộ nhớ hoặc trên đĩa khác), thực hiện những thay đổi cần thiết, rồi hoặc ghi kết quả ngợc trở lại tệp nguyên thuỷ hoặc tạo ra một tệp mới với cùng tên sau khi đã xoá hay đổi tên bản cũ. Bạn có thể coi việc này nh bớc sao chép - dữ liệu đợc sao từ cơ sở dữ liệu nguyên gốc sang một bản mới của cơ sở dữ liệu ấy, tiến hành tahy đổi trong khi sao chép.

Perl hỗ trợ cho việc soạn thảo kiểu sao chép này trên các cơ sở dữ liệu hớng dòng bằng cách dùng việc soạn thảo tại chỗ. Soạn thảo tại chỗ là việc sửa đổi cách thức toán tử hình thoi (<>) đọc dữ liệu từ một danh sách các tệp đợc xác định trong dòng lệnh. Thông thờng nhất thì mốt soạn thảo này đợc thâm nhập tới bằng việc đặt đối dòng lệnh -i, nhng chúng ta cũng có thể đặt lẫy cho mốt soạn thảo tại chỗ từ bên trong một chơng trình, nh đợc biểu thị trong thí dụ sau đâỵ

Để đặt lẫy cho mốt soạn thảo tại chỗ, bạn hãy đặt một giá trị vào trong biến vô hớng $^I. Giá trị của biến này là quan trọng, và sẽ đợc thảo luận ngay đâỵ

Khi toán tử hình thoi đợc sử dụng và $^I có một giá trị (thay vì undef), thì các bớc đợc đánh dấu ##INPLACE## trong đoạn mã sau đây sẽ đợc thêm vào danh sách các hành động ngầm định mà toán tử hình thoi nhận:

$ARGV = shift @ARGV; open(ARGV, “<$ARGV”);

rename($ARGV, “$ARGV$^I”); ## INLACE ## unlink($ARGV); ## INPLACE ##

open(ARGVOUT, “>$ARGV”); ## INPLACE ## select(ARGVOUT) ; ## INPLACE ##

Hiệu quả là ở chỗ việc đọc từ toán tử hình thoi lấy từ tệp cũ, còn việc ghi lên t- ớc hiệu tệp ngầm định thì lại chuyển sang một bản sao mới của tệp nàỵ Tệp cũ vẫn còn trong tệp dự phòng, mà chính là tớc hiệu tệp với phần hậu tố bằng giá trị của biến $^I. (Cũng có một chút ảo thuật để sao chép các bit ngời chủ và phép dùng từ tệp cũ sang tệp mớị) Những bớc này đợc lặp lại mỗi lần một tệp mới đợc rút ra từ mảng @ARGV.

Các giá trị điển hình cho $^I là những cái nh .bak hay ~, để tạo ra các tệp dự phòng rất giống với trình soạn thảo tạo rạ Một giá trị kì lạ nhng có ích cho $^I là xâu rỗng, “”, cái gây ra việc tệp cũ bị xoá sạch sau khi việc soạn thảo hoàn tất. Điều không may là nếu hệ thống hay chơng trình bị hỏng trong khi thực hiện ch- ơng trình bạn thì bạn sẽ mất tất cả dữ liệu cũ, cho nên chúng tôi chỉ khuyến cáo điều này cho những ngời bạo dạn, dại dột hay tin cậỵ

Sau đây là cách thay đổi việc đăng nhập của ai đó vào /bin/sh bằng cách sửa đổi tệp mật hiệu:

@ARGV = (“/etc/passwd”); # nhồi vào toán tử hình thoi $^I = “.bak”; # ghi /etc/passwd.bak để an toàn

while (<>) { # chu trình chính, mỗi lần cho một dòng của /etc/passwd s#L[^;]*$#:/bin/sh#; # đổi vỏ thành /bin/sh

print; # gửi ra ARGVOUT: bản mới /etc/passwd }

Nh bạn có thể thấy, chơng trình này khá đơn giản. Trong thực tế, cùng chơng trình này có thể đợc sinh ra toàn bộ với một vài đối dòng lệnh nh

perl -p -ịbak -e ‘s#:[^:] ‘*$#:/bin/sh#’ /etc/passwd

Chuyển mạch -p đóng ngoặc nhọn chơng trình bạn với chu trình while có chứa một câu lệnh print. Chuyển mạch -i đặt một giá trị vào trong biến $^I. Chuyển mạch -e xác định ra đối sau đây nh một phần của chơng trình Perl đối với thân chu trình; và đối cuối cùng cho giá trị khởi đầu cho @ARGV.

Các đối dòng lệnh đợc thảo luận rất chi tiết trong sách con lừa về perl.

Bài tập

1. Hãy tạo ra một chơng trình để mở cơ sở dữ liệu sendmail và in ra tất cả các mục.

2. Hãy tạo ra hai chơng trình: một đọc dữ liệu hình thoi, chặt nó ra thành các từ, rồi cập nhật một tệp DBM có ghi số lần xuất hiện của từng từ; và chơng trình kia thì mở tệp DBN và cho hiển thị kết quả đợc sắp xếp theo số đếm giảm dần. Hãy chạy chơng trình thứ nhất trên vài tệp rồi xem liệu chơng trình thứ hai có nhặt ra số đếm đúng không.

Một phần của tài liệu Perl và các khái niệm cơ bản (Trang 133 - 139)

Tải bản đầy đủ (PDF)

(139 trang)