Biểu thức chính qui(mẫu đối sánh) có thể được dùng để chặt một xâu thành các trường. Toán tử split() thực hiện điều này còn toán tử join() lại có thể dính các mẩu lại với nhau.
7.1 Hàm split()
Hàm split() nhận một biểu thức chính qui và một xâu rồi tìm tất cả mọi sự xuất hiện của biểu thứcchính qui bên trong xâu này (dường như bạn đã thực hiện toán tử s///g). Các bộ phận của xâu không sánh với biểu thức chính qui sẽ được cho lại lần lượt như một danh sách các giá trị. Chẳng hạn, sau đây là một cách phân tích các thành tố /etc/passwd:
$line = “merlyn::118:10:Randal:/home/merlyn:/usr/bin/perl”; @fields = split(/:/,$line); # chặt $line ra, dùng : làm dấu định biên # bây giờ @field là (“merlyn”, “”, “118”, “10”, “Randal”,
# “/home/merlyn”,”/usr/bin/perl”)
Lưu ý rằng trường thứ hai rỗng trở thành một xâu rỗng. Nếu bạn không muốn điều này, hãy đối sánh tất cả các hai chấm trong một lần phân tách:
@fields = split(/:+/, $line);
Điều này sẽ sánh cả hai dấu hai chấm đi kèm, cho nên sẽ không có trường thứ hai rỗng nữa. Một xâu thông dụng để chặt biến $_, và biến thành mặc định là:
$_ = “xâu nào đó”;
@words = split(/ /); # hệt như @words = split(/ /, $_);
Lưu ý rằng đối với việc chặt này, các khoảng cách liên tiếp trong xâu cần chặt sẽ gây ra các trường không (xâu rỗng) trong kết quả. Một khuôn mẫu tốt hơn sẽ là / +/, hay một cách lí tưởng /\s+/, mà sẽ đối sánh một hay nhiều kí tự khoảng trắng. Trong thực tế, khuôn mẫu này là khuôn mẫu mặc định, cho nên nếu bạn định chặt biến $_ theo các khoảng trắng, thì bạn có thể dùng tất cả các mặc định
và đơn thuần nói:
@words = split; # hệt như @words = split(/\s+/, $_);
Các trường theo sau rỗng không trở thành một phần của danh sách. Điều này nói chung không cần quan tâm - một giải pháp giống thế này:
$line = “merlyn::118:10:Randal:/home/merlyn:/usr/bin/perl”; ($name, $password, $uid,$gid,$gcos,$home,$shell) =
split(/:/, $line); # chặt $line ra bằng cách dùng : làm dấu định biên
sẽ đơn thuần cho $shell một giá trị không (undef) nếu dòng này không đủ dài, hay nếu nó chứa các giá trị rỗng trong trường cuối. (Các trường phụ thì im lặng bị bỏ qua, vì việc gán danh sách làm việc theo cách đó.)
7.2 Hàm join()
Hàm join() nhận một danh sách các giá trị và gắn chúng lại với nhau dùng xâu gắn giữa từng phần tử danh sách. Nó trông tựa như thế này:
$bigstring = join($glue, @list);
Chẳng hạn, để xây dựng lại dòng mật hiệu, bạn hãy thử một cách kiểu như: $outline = join(“:”, @fields);
Lưu ý rằng xâu gắn không phải là biểu thức chính qui - chỉ là một xâu bình thường gồm không hay nhiều kí tự.
INDEX A – LIST MÃ NGUỒN BÀI TẬP TẦN SUẤT TỪ
trong văn bản đó và tần suất tương ứng của mỗi từ.
1.List mã nguồn bằng ngôn ngữ Perl
1.1 Chương trình sử dụng List
###############################################
# #
# # # Chuong trinh : Doc vao tep van ban, # # dau ra danh sach tat ca # # cac tu cung tan suat. #
# Ten file : LKTU.PL #
# Dich (UNIX) : chmod +x lktu.pl # # (WIN) : perl lktu.pl # # Chay : LKTU <TEN_TEP_VAN_BAN> # # Han che : Khong coi tat ca cac # # ki tu dac biet co trong tu. # # # ############################################### #!/usr/local/bin/perl
if ($ARGV[0] eq "") {
print " Ban nen dung lenh : lktu <ten_tep_van_ban>\n"; print (" Bay gio hay cho ten tep : ");
$tentep = <STDIN>;
chop($tentep);
} else {
$tentep = $ARGV[0]; }
unless (open(TEP, $tentep)) {
if (!(-e $tentep)) {
die (" Tep $tentep khong ton tai.\n");
} elsif (!(-r $tentep)) {
die (" Khong the doc duoc file $tentep.\n"); } else {
die (" Tep $tentep khong the mo duoc. \n"); }
}
# Tao danh sach lien ket rong, su dung danh sach lien ket de chua tu
$header = "";
while ($line = <TEP>) {
# Thay tat ca cac ki tu dac biet bang dau cach
$line =~ s/[\W]/ /g;
# Thay tat ca cac chu so bang dau cach
$line =~ s/[\d]/ /g;
# Xoa bo tat ca cac khoang trang o dau va cuoi xau
$line =~ s/^\s+|\s+$//g;
@words = split(/\s+/, $line);
foreach $tu (@words) {
# Bo cac dau cham cau
$tu =~ s/[\.,;:?-]$//g;
# Chuyen tat ca cac chu trong tu thanh chu cai thuong
$tu =~ tr/A-Z/a-z/;
# Cho chu cai dau thanh chu hoa
$tu = ucfirst($tu);
# Chuyen vao trong danh sach
&them_tu($tu); }
close (TEP); &in_kq;
$tentepkq = $tentep;
$tentepkq =~ s/\..*//g;
$tentepkq .= ".kqu";
print " Ket qua nay duoc luu tru trong tep $tentepkq\n"; &ghi_ra_tep;
sub them_tu {
local($tu) = @_; local($pointer);
# Neu danh sach rong thi them vao phan tu dau tien
if ($header eq "") { $header = $tu; $mang_tu{$tu} = ""; $dem{$tu} +=1; return; }
# Neu tu trung voi tu cua phan tu dau tien trong danh sach
if ($header eq $tu){
$dem{$tu} +=1; return;
}
# Neu tu them vao se la phan tu dau tien trong danh sach
if ($header gt $tu) { $mang_tu{$tu} = $header; $header = $tu; $dem{$tu} +=1; return; }
# Tim cho thich hop de them tu vao danh sach
$pointer = $header;
while ($mang_tu{$pointer} ne "" &&
$mang_tu{$pointer} lt $tu) {
$pointer = $mang_tu{$pointer}; }
# Neu tu them vao da co
if ($tu eq $mang_tu{$pointer}){ $dem{$tu} +=1; return; } $mang_tu{$tu} = $mang_tu{$pointer}; $mang_tu{$pointer} = $tu; $dem{$tu} +=1; } sub in_kq { local ($pointer);
print ("\nDanh sach cac tu trong tep $tentep :\n\n");
print ("-" x80); print ("| Tu TS " x 4); print ("-" x80); $pointer = $header; while ($pointer ne "") { printf ("| %-14s",$pointer); printf ("%3d ",$dem{$pointer});
print ("\n","-" x 80,"\n");
sub ghi_ra_tep {
local ($st, $st1, $pointer);
open(TEPKQ, ">$tentepkq") || die "Nhung khong mo duoc de ghi";
print TEPKQ ("\n Danh sach cac tu trong tep $tentep :\n\n");
print TEPKQ ("-" x 93 ,"\n"); print TEPKQ ("| Tu TS " x 4, "|\n"); print TEPKQ ("-" x 93,"\n"); $pointer = $header; $st = 0; while ($pointer ne "") { $st++;
printf TEPKQ ("| %-17s",$pointer); printf TEPKQ ("%3d ",$dem{$pointer});
if ($st % 4 == 0) {
print TEPKQ ("|\n"); }
$pointer = $mang_tu{$pointer}; }
# Ve not cac duong ke con thieu
$st1 = $st % 4; if ($st1 != 0) { while ($st1 != 4) { printf TEPKQ ("%-23s","|"); $st1++; } print TEPKQ ("|\n"); } print TEPKQ ("-" x 93,"\n");
print TEPKQ (" Tong so co : $st tu."); }
Phần cuối:
2. Tham khảo chương trình C
Yêu cầu: giả thiết người dùng đã có hai file dạng text:
- TAILIEU.TXT: file chứa nội dung văn bản, được cấu trúc theo trang
- TUKHOA.TXT: file chứa các chuỗi từ khoá cần tìm kiếm, mỗi chuỗi từ khoá được đặt trên một dòng.
<Chuỗi từ khoá>: <Danh sách các trang xuất hiện chuỗi từ khoá phân cách nhau bằng dấu phảy>
VD: ĐHBK: 2,19,32
Trường hợp chuỗi từ khoá không xuất hiện trong bất cứ trang nào thì không ghi số hiệu trang. Đồng thời không phân biệt chữ hoa chữ thường, không phân biệt số dấu cách giữa các từ, VD: ta có chuỗi từ khoá cần tìm sau:
“Trường Đại học Bách Khoa Hà Nội” thì chuỗi trong tệp văn bản:
“ TRường Đại Học BáCH KHOA Hà Nội “
vẫn được coi là tương đương với chuỗi từ khoá trên. Và cả trường hợp khi chuỗi từ khoá nằm trên 2 dòng khác nhau thì vẫn được coi là tìm thấy.
Với yêu cầu như trên nếu thực hiện bằng C sẽ có mã nguồn như sau: #include <stdio.h> #include <conio.h> #include <string.h> #include <alloc.h> #define DEFAULT #define MAX_LINE_LEN 256 #define MAX_KW_LEN 50 #define LINES_PER_PAGE 25
#define MALLOC(x) ((x *)malloc(sizeof(x))) typedef char Keyword[MAX_KW_LEN]; typedef struct ResultNode{
int pageNum; // Page Number
struct ResultNode *next; };
typedef struct KeywordNode{ char keyword[MAX_KW_LEN];
struct ResultNode *firstRN; // First Result Node struct ResultNode *lastRN; // Last Result Node struct KeywordNode *next;
FILE *sf,*kf,*df; // Source/Keyword/Destination File char scfile[80],kwfile[80],idfile[80];
struct KeywordNode *head; // Point to keyword list void Init(void);
int GetPageNum(int lineNum); void Standardlize(char *st);
void MakeKeywordList(struct KeywordNode **); void SearchKeyword(struct KeywordNode *); void MakeIndexFile(struct KeywordNode *); void FreeHeap(struct KeywordNode *);
/*---*/ void main() { Init(); MakeKeywordList(&head); SearchKeyword(head); MakeIndexFile(head); FreeHeap(head);
printf("The index file is available."); } /*---*/ void Init(void) { #ifdef DEFAULT strcpy(scfile,"TAILIEU.TXT"); strcpy(kwfile,"TUKHOA.TXT"); strcpy(idfile,"CHISO.TXT"); #else
printf("Enter your source file: "); gets(scfile); printf("Enter your keyword file: "); gets(kwfile); printf("Enter your index file: "); gets(idfile); #endif
/*---*/ int GetPageNum(int lineNum)
{ return(lineNum/(LINES_PER_PAGE+1)+1); } /*---*/ void Standardlize(char *st) { int i,t,k;
// Remove unexpected spaces t=0; k=0; while(st[k]!='\0'){ while((st[k]==' ')&&(st[k+1]==' ')) k++; st[t]=st[k]; t++; k++; } st[t]='\0';
strlwr(st); // Not differ from lower and upper characters }
/*---*/ void MakeKeywordList(struct KeywordNode **head) {
struct KeywordNode *end,*newNode; char keyword[MAX_KW_LEN];
char *ch; // Point to \n char of the keyword
kf=fopen(kwfile,"rt"); while(!feof(kf)){
fgets(keyword,MAX_KW_LEN,kf); Standardlize(keyword);
ch=strchr(keyword,'\n'); *ch='\0'; // Delete \n char newNode=MALLOC(struct KeywordNode);
if(newNode==NULL) break;
newNode->firstRN=NULL; newNode->lastRN=NULL; if(*head==NULL) *head=newNode; end->next=newNode; end=newNode; } end->next=NULL; fclose(kf); } /*---*/ void SearchKeyword(struct KeywordNode *head)
{
char bkLine[MAX_LINE_LEN]; // Backward Line = the first line char fwLine[MAX_LINE_LEN]; // Forward Line = the second line char pSt[MAX_LINE_LEN*2]; // Parsing String
int lineNum; // Line Number
int pageNum; // Page Number
struct KeywordNode *keyNode; struct ResultNode *newNode; lineNum=0; sf=fopen(scfile,"rt"); bkLine[0]='\0'; while(!feof(sf)){ fgets(fwLine,MAX_LINE_LEN,sf); strcpy(pSt,bkLine); strcat(pSt,fwLine); // pSt=bkLine+fwLine pSt[strlen(bkLine)-1]=' '; Standardlize(pSt); lineNum++; for(keyNode=head;keyNode!=NULL;keyNode=keyNode->next) if(strstr(pSt,keyNode->keyword)!=NULL){ pageNum=GetPageNum(lineNum);
newNode=(struct ResultNode *)malloc(sizeof(struct ResultNode)); if(!newNode){ fclose(sf); return; } newNode->pageNum=pageNum; newNode->next=NULL; keyNode->firstRN=keyNode->lastRN=newNode; } else if(keyNode->lastRN->pageNum!=pageNum){ newNode=MALLOC(struct ResultNode); if(!newNode){ fclose(sf); return; } newNode->pageNum=pageNum; newNode->next=NULL; keyNode->lastRN->next=newNode; keyNode->lastRN=newNode; } } strcpy(bkLine,fwLine); } fclose(sf); } /*---*/ void MakeIndexFile(struct KeywordNode *head)
{
struct KeywordNode *keyNode; struct ResultNode *reNode; df=fopen(idfile,"wt");
for(keyNode=head;keyNode!=NULL;keyNode=keyNode->next){ if(keyNode->firstRN!=NULL) fprintf(df,"%s:",keyNode->keyword); for(reNode=keyNode->firstRN;reNode!=NULL;reNode=reNode->next)
if(reNode==keyNode->lastRN) fprintf(df,"%d\n",reNode->pageNum); else fprintf(df,"%d,",reNode->pageNum); } fclose(df); } /*---*/ void FreeHeap(struct KeywordNode *head)
{
struct KeywordNode *ktemp; struct ResultNode *rtemp1,*rtemp2;
for(ktemp=head;ktemp!=NULL;ktemp=head){ head=head->next; for(rtemp1=ktemp->firstRN,rtemp2=rtemp1;rtemp2!=NULL;rtemp2=rtemp1){ rtemp1=rtemp1->next; free(rtemp2); } free(ktemp); } }
INDEX B - NHÚNG PERL VÀO TRONG C VÀ CẤP BỘ NHỚ ĐỘNG CHO PERL
1. Nhúng Perl vào trong C
1.1 Giới thiệu
Perl và C đều là những ngôn ngữ lập trình phổ biến. Mỗi ngôn ngữ có những điểm mạnh và điểm yếu riêng. C là một ngôn ngữ lập trình vạn năng, có tính thích nghi khả chuyển cao, cho phép can thiệp sâu vào hệ thống. Hơn nữa tốc độ biên dịch cũng như tốc độ chạy của các ứng dụng viết bằng C khá nhanh và ổn định. Trong khi Perl mặc dù chạy chậm hơn,nhưng lại rất mạnh ở xử lý văn bản, điều mà ngôn ngữ C không hỗ trợ nhiều. Hơn nữa 80% người bắt đầu học Perl đã từng làm quen với C hoặc C++. Chính vì thế việc kết hợp hai ngôn ngữ này trong một số điều kiện sẽ đem lại hiệu quả cao.
Tuy nhiên có một điều cần chú ý là việc nhúng Perl trong C và ngược lại chỉ thực hiện được trên LINUX(theo Perl 5 Unleashed). Và có thể cần dùng nhiều bộ nhớ hơn, và thỉnh thoảng làm chậm ứng dụng.
1.2 Chương trình mẫu
Một chương trình C có nhúng Perl phải gồm các hàm cơ bản như mẫu dưới đây:
Giải thích:
+ Dòng 1,2 : Để việc nhúng Perl trong C, trước hết ta cần gọi thêm 2 tệp header là EXTERN.h và
perl.h . Cả 2 tệp này đều đi kèm theo bộ cài Perl
+ Dòng 3: Có thể có nhiều module viết bằng Perl (các Perl Interpreters) trong 1 chương trình C . Tất cả các trình thông dịch này đều được lưu trữ trong một cấu trúc gọi là PerlInterpreter. Trong chương trình trên dùng 1 trình thông dịch(interpreter), và được trỏ bởi con trỏ my_perl.
+ Dòng 5: khi thực hiện nhúng Perl thì trong hàm main của C bắt buộc phải có tham số argc, argv(tham số đầu tiên trong dòng lệnh trong UNIX hay DOS ), env.
+ Dòng 7: hàm perl_alloc() cấp phát bộ nhớ cho Perl Interpreter(điều này hay gặp khi ta dùng tới con trỏ). Ngược lại ở dòng 12, hàm perl_free() sẽ giải phóng bộ nhớ.
+ Dòng 8, hàm perl_construct() khởi đầu việc gọi trình thông dịch Perl và tất cả các cấu trúc dữ liệu liên quan. Dòng 11 kết thúc việc gọi trình thông dịch Perl bằng hàm perl_destruct()
+ Dòng 10:Các chương trình Perl được đưa vào một bộ thông dịch, bộ thông dịch này chuyển đổi mã chương trình bạn viết thành mã byte(được thực hiện trước khi nó được chạy) và sau đó chạy nó. Hàm
perl_run() sẽ khởi đầu việc này.
1.3 Ví dụ 1
Giả thiết rằng ta có đoạn script search.pl như sau:
Hàm search_files nhận 2 tham số là các xâu. Với thủ tục này, ta có 1 số cách để gọi nó trong chương trình C.
Chương trình ex.c sẽ gọi chương trình con search_files viết bằng Perl ở trên để tìm từ “struct” trong tất cả các file header của C.
Hãy chú ý dòng lệnh:
perl_call_argv(“search_file”,G_DISCARD,my_argv);
thay vì gọi perl_run, ta gọi search_file bằng hàm perl_call_argv(),và dùng G_DISCARD để loại bỏ tất cả các kết quả trả về. Ngoài ra còn có 1 số cách gọi chương trình Perl trong C nữa, chẳng hạn thay vì dùng hàm perl_call_argv() , ta có thể dùng:
perl_call_va ("search_files",
NULL);
Để biên dịch chương trình trên ta gõ vào console của LINUX các lệnh sau:
% gcc -o ex -I/usr/local/lib/perl5/i586-linux/5.004/CORE \ -L/usr/local/lib/perl5/i586-linux/5.004/CORE \ -Dbool=char -DHAS_BOOL \ ex.c -lperl -lm
Kết quả thu được có dạng sau:
av.h[10]: struct xpvav { cop.h[58]: struct cop {
cop.h[60]: char * cop_label; /* label for this construct */ cop.h[75]: struct block_sub {
cop.h[98]: { struct block_sub cxsub; ...
Cách biên dịch trên có vẻ phức tạp và khó nhớ. Để thuận tiện cho việc nhúng Perl vào trong C, ta sử dụng 1 module chuyên hỗ trợ việc này. Đó là ExtUtils::Embed .
Với chương trình ex.c ta sẽ biên dịch nó như sau:
% cc -c xsinit.c `perl -MExtUtils::Embed -e ccopts` % cc -c ex.c `perl -MExtUtils::Embed -e ccopts` % cc -o ex ex.o xsinit.o `perl -MExtUtils::Embed -e ldopts