Khái niệm:
Kiểu con trỏ là kiểu cơ sở dùng lưu địa chỉ bộ nhớ đã cấp phát cho một đối tượng dữ liệu khác.
Biến con trỏ có kiểu cơ sở T là biến mà giá trị của nó là địa chỉ vùng nhớ đã cấp phát cho một biến kiểu T (có thể là biến con trỏ) hoặc là giá trị NULL để chỉ định biến con trỏ chưa chứa địa chỉ vùng nhớ nào.
Khai báo kiểu con trỏ: typedef kiểucơsởT *tênkiểucontrỏ;
Kiểucơsở: là kiểu đã định nghiã trước, cũng có thể là kiểu con trỏ.
Kiểu con trỏ void: là kiểu con trỏ có thể chứa địa chỉ của bất kỳ kiểu nào. Nhưng khi sử dụng phải ép về 1 kiểu dữ liệu cụ thể.
Khai báo biến con trỏ:
Mẫu 1: tênkiểucontrỏ tênbiếncontrỏ;
Mẫu 2: kiểucơsởT *tênbiếncontrỏ;
Ví dụ: typedef int *intpointer;
intpointer pt;
hay int *pt;
Cấu trúc lưu trữ: Biến con trỏ được lưu trữ trong vùng nhớ cấp phát động (Heap) của chương trình. Kích thước của biến con trỏ tùy thuộc quy ước số byte địa chỉ trong từng mô hình bộ nhớ của từng ngôn ngữ lập trình
- Trong Pascal: 4 bytes(2 bytes địa chỉ Segment + 2 bytes địa chỉ offset)
- Trong C: 2 bytes cho con trỏ NEAR (chỉ lưu offset); 4 byte cho con trỏ FAR
Các thao tác trên biến con trỏ:
- Gán 1 địa chỉ cho biến con trỏ:
tênbiếncontrỏ = &tênbiếncầnlấyđịachỉ;
tênbiếncontrỏ = NULL;
Ví dụ: Chứa địa chỉ của mảng 1 chiều: int *P , A[10]; --> P = A
Chứa địa chỉ của mảng 2 chiều: float *P, B[3][4]; --> P = (float*) B
Chứa địa chỉ của 1 biến cấu trúc: struct HocSinh *P, hs; --> P = &hs
- o Truy xuất nội dung 1 biến do biến con trỏ trỏ đến: *tênbiếncontrỏ
- o Phép toán số học trên kiểu con trỏ:
pt + i ==> Cộng địa chỉ vùng nhớ lưu trong pt với i*sizeof(T)
pt1 – pt2 ==> số phần tử có kích thước sizeof(T) giữa 2 địa chỉ.
- o Phép so sánh con trỏ: so sánh địa chỉ lưu trong 2 biến con trỏ: ==, !=
Các cấu trúc dữ liệu đã xét, lưu trữ và xử lý các mục dữ liệu ở bộ nhớ trong. Do đó có thể truy nhập đến chúng rất nhanh. Tuy nhiên dung lượng lưu trữ của bộ nhớ trong có thể quá nhỏ đối với những danh sách dữ liệu lớn, như danh sách nhân viên và không thể lưu trữ lâu dài. Những nhóm dữ liệu như vậy phải được lưu trữ ở bộ nhớ ngoài, gọi là tập tin (file).
Có 2 loại tập tin :
- File văn bản
- File có cấu trúc truy nhập ngẫu nhiên (File nhị phân)
• Khi ghi, 1 ký tự xuống dòng ‘\n’ được chuyển thành 2 ký tự CR (Carriage Return_13) và LF (Line Feed_10).
• Khi đọc 2 ký tự liên tiếp CR và LF trên file chỉ cho ta 1 ký tự LF.
Quy tắc làm việc với tập tin:
- Khai báo biến tập tin
- Mở tập tin
- Truy xuất tập tin
- Đóng tập tin
Trong C, việc truy xuất đến 1 file trên đĩa có thể thực hiện bằng các hàm trong STDIO.H thông qua biến con trỏ file.
Khai báo biến con trỏ file: FILE *TrỏFile;
Mỗi file đều được đánh dấu kết thúc bởi ký tự có mã 26. Khi sử dụng các hàm để đọc File, nếu gặp cuối File thì hàm sẽ trả về giá trị –1, có tên là EOF.
Mở file: TrỏFile = fopen (char *têntậptin, char *kiểu);
• Têntậptin: Bao gồm cả đường dẫn. Nếu có lỗi sẽ cho giá trị NULL.
• Kiểu: Kết hợp bởi các mã sau nhằm khai báo kiểu tập tin và cách thức truy xuất tập tin.
Kiểu Ý nghiã
“b” Mở tập tin kiểu nhị phân
“t” Mở tập tin kiểu văn bản
“r” Mở để đọc. Nếu không có File sẽ báo lỗi
“r+” Mở để sửa
“w” Mở file mới để ghi. Nếu file đã có sẽ bị xóa
“w+” Mở file mới đọc hoặc ghi.
“a” Mở file để ghi thêm vào cuối file
“a+” Mở file để đọc ghi. Nếu file cũ thì nối thêm,nếu không thì tạo mới
Ví dụ:
FILE *fp;
fp = fopen(“C:\\DSHS.DBF”, “wb”);
if ( fp == 0 )
{
perror(“Loi Mo File:”);
exit(1);
}
Đóng file:
- int fclose (FILE *fp); Nếu có lỗi hàm cho EOF ngược lại cho giá trị 0.
- int fcloseall ( ); Nếu có lỗi cho EOF nếu không cho số file được đóng.
File văn bản
- Ghi dữ liệu từ biến ra file văn bản:
- int putc ( int ch, FILE *fp); Ghi lên file 1 ký tự có mã m = ch % 256.
- int fputc (int ch, FILE *fp); như trên
int main(int n, char * a[]) //n là số đối số trên dòng lệnh kể cả tên chương trình a[0]
{ FILE *fp; char c; int sobyte=0;
if (n != 2) { puts("Loi cu phap: "); exit(1); }
fp = fopen(a[1], "wt");
while (( c = getchar()) != EOF) //hàm getchar() trả về EOF khi có lỗi hoặc ấn F6
{ putc( c, fp); sobyte++; }
printf("\n\t 1 file copy \t\t %d bytes ", sobyte);
fclose(fp); }
{ FILE *fp; char c; int sobyte=0;
if (n != 2) { puts("Loi cu phap: "); exit(1); }
fp = fopen(a[1], "wt");
while (( c = getchar()) != EOF) //hàm getchar() trả về EOF khi có lỗi hoặc ấn F6
{ putc( c, fp); sobyte++; }
printf("\n\t 1 file copy \t\t %d bytes ", sobyte);
fclose(fp); }
Chú thích: Biên dịch thành file EXE, trở về dấu nhắc Dos: Taofile D:\LuuTru\Thoca.txt
- int fputs (const char *Str, FILE *fp); Ghi chuỗi Str vào file.
Ví dụ:
int main(int n, char * a[]) //n là số đối số trên dòng lệnh kể cả tên chương trình a[0]
{ FILE *fp; char st[80]; int sobyte=0;
if (n != 2) { puts("Loi cu phap: "); exit(1); }
fp = fopen(a[1], "wt");
while ( gets(st)) != NULL) //hàm gets(st) trả về NULL khi có lỗi hoặc ấn F6
{ fputs( st, fp); fputs("\n", fp);
sobyte+=strlen(st);
}
printf("\n\t 1 file copy \t\t %d bytes ", sobyte);
fclose(fp);
}
{ FILE *fp; char st[80]; int sobyte=0;
if (n != 2) { puts("Loi cu phap: "); exit(1); }
fp = fopen(a[1], "wt");
while ( gets(st)) != NULL) //hàm gets(st) trả về NULL khi có lỗi hoặc ấn F6
{ fputs( st, fp); fputs("\n", fp);
sobyte+=strlen(st);
}
printf("\n\t 1 file copy \t\t %d bytes ", sobyte);
fclose(fp);
}
- int fprintf (FILE *fp, const char *dk, các biểu thức);
Ví dụ: fprintf(fp,”so dinh %d \n”, n);
Ví dụ: Tạo file văn bản chứa một bản nhạc: Lời, tần số, thời gian nghỉ.
Ví dụ: Tạo file lưu bảng lượng giác của các góc từ 1 đến 89
- Đọc dữ liệu vào biến bộ nhớ:
- int getc (FILE *fp) hoặc int fgetc (FILE *fp)
Chú ý: Trong kiểu văn bản,
- Hàm đọc 1 lượt cả 2 mã 13, 10 và chỉ trả về mã 10
- Khi đọc ký tự kết thúc file (26) hàm trả về EOF
Ví dụ: Đếm số từ xuất hiện trong 1 file văn bản.
#include #include #include
#include #include
void main()
{ FILE *fp; char ch, Tu[8], fin[30];int i ;
clrscr(); textmode(C40); strcpy(fin,"d:\\thu.cpp");
fp = fopen(fin,"rt");
if (fp == NULL)
{ printf("Khong tim thay File: %s ", fin); getch(); exit(1);
}
while (!feof(fp))
{ memset(Tu,0,8);
while ((ch = getc(fp)) != EOF) //Tim ky tu dau tu
if (!isspace(ch))
{
Tu[0] = ch;
break;
}
if (Tu[0] != 0)
{ i = 1;
while ((ch = getc(fp)) != EOF)
if (isspace(ch)) { break; }
else Tu[i++] = ch;
printf("\n%s",Tu);
}
}
fclose(fp);
}
#include
void main()
{ FILE *fp; char ch, Tu[8], fin[30];int i ;
clrscr(); textmode(C40); strcpy(fin,"d:\\thu.cpp");
fp = fopen(fin,"rt");
if (fp == NULL)
{ printf("Khong tim thay File: %s ", fin); getch(); exit(1);
}
while (!feof(fp))
{ memset(Tu,0,8);
while ((ch = getc(fp)) != EOF) //Tim ky tu dau tu
if (!isspace(ch))
{
Tu[0] = ch;
break;
}
if (Tu[0] != 0)
{ i = 1;
while ((ch = getc(fp)) != EOF)
if (isspace(ch)) { break; }
else Tu[i++] = ch;
printf("\n%s",Tu);
}
}
fclose(fp);
}
- char *fgets (char * Str, int n, FILE *fp);
Việc đọc kết thúc khi:
- Đọc hết n-1 ký tự;
- Gặp dấu xuống dòng (cặp mã 13 10): khi đó mã 10 (‘\n’) được ghi vào xâu kết quả
- Gặp dấu kết thúc file
Hàm sẽ thêm ký tự '\0' vao cuối chuỗi.
Ví dụ: Đọc file lượng giác bằng hàm fgets vào một mảng và cho phép sử dụng các phím mũi tên để duyệt trên danh sách trên màn hình.
- int fscanf (FILE *fp, const char *dk, địa chỉ các biến);
Ví dụ: Đọc file DAGIAC.DAT chứa toạ độ các đỉnh của đa giác. Dòng đầu ghi số đỉnh các dòng sau mỗi dòng ghi tọa độ của 1 đỉnh của đa giác.
typedef struct pointtype Polygol[10];
Polygol P;
void main(void)
{
FILE *fp;
char c; int n,i;
fp = fopen("c:\\dagiac.dat","rt");
fscanf(fp,"%d",&n);
printf("%d\n",n);*/
for (i=0; i<n; i++)
fscanf(fp,"%d %d",&P[i].x,&P[i].y);
fclose(fp);
}
Polygol P;
void main(void)
{
FILE *fp;
char c; int n,i;
fp = fopen("c:\\dagiac.dat","rt");
fscanf(fp,"%d",&n);
printf("%d\n",n);*/
for (i=0; i<n; i++)
fscanf(fp,"%d %d",&P[i].x,&P[i].y);
fclose(fp);
}
Trường hợp không biết trước số đỉnh của đa giác:
i = 0;
while (fscanf(fp,"%d %d",&P[i].x, &P[i].y) != EOF)
printf("%d %d\n",P[i].x,P[i].y);
File truy nhập ngẫu nhiên
Tập tin truy nhập ngẫu nhiên thường dùng lưu trữ các mục dữ liệu cùng kiểu dưới dạng nhị phân như trong bộ nhớ. Cho phép truy nhập tuần tự các mục dữ theo thứ tự lưu trữ và có thể truy nhập trực tiếp đến một mục nào đó trong File bằng cách đặc tả vị trí của nó.
a. Ghi các mục dữ liệu vào File:
- int fwrite(void *ptr, int sizeofItem, int n, FILE *fp);
Ví dụ: Tạo File lưu trữ hồ sơ nhân viên.
b. Đọc các mục dữ liệu trên File:
- int fread (void *ptr, int sizeofItem, int n, FILE *fp);
Ví dụ: Liệt kê danh sách nhân viên.
Thuật toán: Duyệt tập tin
(1) Mở file để đọc
(2) Trong khi còn đọc được N nhân viên (đọc chừng 20 nhân viên)
while (N = fread(&nv, sizeof(NhanVien), 20, fp)) > 0)
(3) In N nhân viên
(4) Đóng file
void main()
{ FILE *f; item e; int N; const char *fname="e:\\DS.DBF";
f = fopen(fname, "rb");
while (N = fread(&e, sizeof(item), 1, f)) > 0 )
{ for (int i=1; i<=N; i++) printf("%6d", e.k);
getch();
}
fclose(f);
}
{ FILE *f; item e; int N; const char *fname="e:\\DS.DBF";
f = fopen(fname, "rb");
while (N = fread(&e, sizeof(item), 1, f)) > 0 )
{ for (int i=1; i<=N; i++) printf("%6d", e.k);
getch();
}
fclose(f);
}
c. Kiểm tra cuối file:
- int feof (FILE *fp);
Ví dụ: while (1)
{ fread(&e,sizeof(Item), 1, fp);
if ( feof( f )) break;
}
d. Vị trí con trỏ byte:
- long ftell (FILE *fp);
e. Di chuyển đầu đọc File:
- void rewind (FILE *fp); Chuyển đầu đọc về đầu file.
- int fseek (FILE *fp, long sốbyte, int vịtríxuấtphát); chuyển đầu đọc từ vị trí xuất phát qua 1 số byte về hướng cuối file (sốbyte>0) hay hướng đầu file (sốbyte<0).>
+ SEEK_SET hay 0: xuất phát từ đầu file
+ SEEK_CUR hay 1: xuất phát từ vị trí hiện hành
+ SEEK_END hay 2: xuất phát từ cuối file.
Ví dụ : Hàm tính số byte trên file (đối với file văn bản ký tự xuống dòng được tính 2 còn file nhị phân tính 1)
long filesize(FILE *stream) { long curpos, length;
curpos = ftell(stream);
fseek(stream, 0L, SEEK_END);
length = ftell(stream);
fseek(stream, curpos, SEEK_SET);
return length;
}
curpos = ftell(stream);
fseek(stream, 0L, SEEK_END);
length = ftell(stream);
fseek(stream, curpos, SEEK_SET);
return length;
}
f. Đánh dấu vị trí đầu đọc ghi:
- int fgetpos (FILE *fp, fpos_t *vịtrí);
- int fsetpos(FILE * fp, fpos_t *vịtrí);
h. Thêm một số mục dữ liệu vào tập tin:
Thuật toán:
(1) Mở File để thêm
(2) Nhập dữ liệu vào biến bộ nhớ e
(3) Nếu (có nhập) thì ghi biến nhớ vào File
(4) Lặp lại (2) trong khi còn nhập.
(5) Đóng File
i. Sửa Dữ liệu:
Thuật toán:
(1) Mở file để đọc và ghi
(2) Nhập thông tin nhận dạng mục cần sửa
(3) Dời đầu đọc đến mục cần sửa
(4) Ghi nhận vị trí đầu đọc
(5) Đọc mục cần sửa vào biến nhớ
(6) Hiển thi thông tin và cho Nhập dữ liệu mới
(7) Dời đầu đọc về lại vị trí đã ghi nhận
(8) Ghi dữ liệu vào File
j. Hủy một mục dữ liệu:
Phương án 1:
Thuật toán:
(1) Đọc các phần tử không bị hủy trong tập tin chính vào một tập tin tạm.
Item e;(2) Đổi tên tập tin tạm thành tập tin chính
ftemp = fopen(“??”, “wb”)
while (! feof( f ) )
{ fread( &e, sizeof(Item), 1, f );
if (e.khóa != Khóa_cần_tìm)
fwrite(&e, sizeof(Item), 1, ftemp);
}
(2.1) Nếu có file ?.BAK thì xóa nó
remove(“ ?.BAK”);
(2.2) Đổi tên ? thành ?.BAK // rename( “? “, “?.BAK”)
(2.3) Đổi tên ?? thành ? // rename( “ ?? “, “?”)
k. Đổi tên / di chuyển file :
- int rename (const char *OldFile, const char *NewFile);
l. Xóa tập tin:
- int remove (const char * path);
Phương án 2:
Tổ chức thêm vùng tin để đánh dấu sự loại bỏ tạm thời.
(1) Tìm và đánh dấu các phần tử cần loại bỏ.
• Tìm phần tử cần hủy
• Nếu tìm thấy thì e.dauloaibo = 1
• Ghi trở lại vào File
fseek( f , (long) –sizeof(Item), SEEK_CUR);
fwrite( &e, sizeof(Item), 1, f );
(2) Sau đó mới copy các phần tử không đánh dấu loại bỏ qua tập tin mới.
Ưu điểm:
- An toàn dữ liệu
- Tiết kiệm thời gian
- Có thể khôi phục lại các phần tử đã được đánh dấu loại bỏ tạm thời.
m. Trộn File:
Giả sử, có 2 file chứa các mẫu tin, mỗi mẫu tin chứa những thông tin khác nhau về một đối tượng cần quản lý (Nhân viên). Và hiện đang lưu trữ theo thứ tự của một trường khóa nào đó (MsNV).
Để Trộn 2 file đã sắp thứ tự theo khóa của mẫu tin sao cho file nhận được cũng sắp thứ tự có thể dùng thuật toán sau:
Thuật toán:
input: File1, File2 đã sắp thứ tự
output: File3 cũng sắp thứ tự
Bắt đầu:
(1) Mở File1 và File2 để đọc; File3 để ghi
(2) Đọc phần tử X trong File1 và phần tử Y trong File2
(3) Lặp lại các bước sau cho đến khi kết thúc File1 hay File2
Nếu ( Khóa(X) < Khóa(Y) ) thì
Ghi X vào File3
Đọc mẫu tin mới của X trong File1
Ngược lại
Ghi Y vào File3
Đọc mẫu tin mới của Y trong File2
(4) Nếu chưa kết thúc trong File1, sao tất cả các phần tử còn lại từ File1 sang File3. Nếu chưa kết thúc trong File2, sao tất cả các phần tử còn lại từ File2 sang File3.
(5) Đóng các file
Kết thúc.
No comments:
Post a Comment