File , Thư mục và IO phần 3

Chia sẻ: Nghia Bui Tuan | Ngày: | Loại File: PDF | Số trang:8

0
69
lượt xem
18
download

File , Thư mục và IO phần 3

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Bạn cần đọc dữ liệu từ một file mà không phải dừng quá trình thực thi mã lệnh của bạn. Kỹ thuật này thường được sử dụng khi file được lưu trữ trong một nơi có tốc độ truy xuất chậm (chẳng hạn một đĩa mạng)

Chủ đề:
Lưu

Nội dung Text: File , Thư mục và IO phần 3

  1. 1.1 Đọc file một cách bất đồng bộ Bạn cần đọc dữ liệu từ một file mà không phải dừng quá trình thực thi mã lệnh của bạn. Kỹ thuật này thường được sử dụng khi file được lưu trữ trong một nơi có tốc độ truy xuất chậm (chẳng hạn một đĩa mạng). Tạo một lớp để đọc file một cách bất đồng bộ. Bắt đầu đọc một khối dữ liệu bằng phương thức FileStream.BeginRead, và truyền phương thức callback. Khi callback được kích hoạt, gọi FileStream.EndRead để truy xuất dữ liệu, xử lý nó, và đọc khối dữ liệu kế tiếp với BeginRead. FileStream hỗ trợ hoạt động bất đồng bộ thông qua phương thức BeginRead và EndRead. Sử dụng các phương thức này, bạn có thể đọc một khối dữ liệu trên một trong các tiểu trình do thread-pool cung cấp mà không cần sử dụng trực tiếp các lớp tiểu trình trong không gian tên System.Threading. Khi đọc file một cách bất đồng bộ, bạn cần xác định kích thước khối dữ liệu trong một lần đọc. Tùy trường hợp, bạn có thể muốn đọc một khối dữ liệu nhỏ (ví dụ, chép từng khối một sang file khác) hoặc khối dữ liệu tương đối lớn (ví dụ, bạn cần một lượng thông tin nhất định trước khi xử lý việc gì đó). Bạn chỉ định kích thước khối khi gọi BeginRead, và truyền một bộ đệm để chứa dữ liệu. Vì BeginRead và EndRead cần truy xuất nhiều mẩu thông tin giống nhau (chẳng hạn FileStream, bộ đệm, kích thước khối,...), bạn nên đóng gói mã lệnh đọc file bất đồng bộ trong một lớp. Với lớp AsyncProcessor trong ví dụ dưới đây, phương thức công khai StartProcess bắt đầu quá trình đọc bất đồng bộ. Mỗi khi quá trình đọc hoàn tất, OnCompletedRead được kích hoạt và khối dữ liệu được xử lý. Nếu còn dữ liệu trong file, một quá trình đọc bất đồng bộ mới sẽ được khởi chạy. Kích thước khối bộ nhớ là 2 KB (2048 byte). using System; using System.IO; using System.Threading; public class AsyncProcessor { private Stream inputStream; // Kích thước mỗi khối dữ liệu là 2 KB. private int bufferSize = 2048; public int BufferSize { get {return bufferSize;} set {bufferSize = value;}
  2. } // Bộ đệm chứa dữ liệu. private byte[] buffer; public AsyncProcessor(string fileName) { buffer = new byte[bufferSize]; // Mở file, truyền giá trị true để hỗ trợ truy xuất bất đồng bộ. inputStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true); } public void StartProcess() { // Bắt đầu quá trình đọc bất đồng bộ. inputStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnCompletedRead), null); } private void OnCompletedRead(IAsyncResult asyncResult) { // Một khối đã được đọc. Truy xuất dữ liệu. int bytesRead = inputStream.EndRead(asyncResult); // Nếu không đọc được byte nào, stream đang ở cuối file. if (bytesRead > 0) { // Tạm dừng để giả lập việc xử lý dữ liệu. Console.WriteLine("\t[ASYNC READER]: Read one block."); Thread.Sleep(TimeSpan.FromMilliseconds(20)); // Bắt đầu đọc khối kế tiếp. inputStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(OnCompletedRead), null); }else {
  3. // Kết thúc. Console.WriteLine("\t[ASYNC READER]: Complete."); inputStream.Close(); } } } Ví dụ dưới sử dụng AsyncProcessor để đọc một file có kích thước 1 MB. public class AsynchronousIO { public static void Main() { // Tạo file thử nghiệm có kích thước 1MB. FileStream fs = new FileStream("test.txt", FileMode.Create); fs.SetLength(1000000); fs.Close(); // Bắt đầu xử lý bất đồng bộ trên một tiểu trình khác. AsyncProcessor asyncIO = new AsyncProcessor("test.txt"); asyncIO.StartProcess(); // Cùng thời điểm này, thực hiện công việc khác. // Ở đây chúng ta sẽ lặp trong 10 giây. DateTime startTime = DateTime.Now; while (DateTime.Now.Subtract(startTime).TotalSeconds < 10) { Console.WriteLine("[MAIN THREAD]: Doing some work."); // Tạm dừng để giả lập một thao tác tốn nhiều thời gian. Thread.Sleep(TimeSpan.FromMilliseconds(100)); } Console.WriteLine("[MAIN THREAD]: Complete."); Console.ReadLine(); // Xóa file thử. File.Delete("test.txt"); } } Và đây là kết xuất khi chạy ứng dụng thử nghiệm:
  4. [MAIN THREAD]: Doing some work. [ASYNC READER]: Read one block. [ASYNC READER]: Read one block. [MAIN THREAD]: Doing some work. [ASYNC READER]: Read one block. [ASYNC READER]: Read one block. [ASYNC READER]: Read one block. [ASYNC READER]: Read one block. [MAIN THREAD]: Doing some work. [ASYNC READER]: Read one block. [ASYNC READER]: Read one block. [ASYNC READER]: Read one block. ... 1.2 Tìm file phù hợp một biểu thức wildcard Bạn cần xử lý nhiều file có điểm chung, dựa vào biểu thức lọc như *.dll hay mysheet20??.xls. Sử dụng phiên bản nạp chồng của phương thức System.IO.DirectoryInfo. GetFiles nhận một biểu thức lọc và trả về một mảng các đối tượng FileInfo. Các đối tượng DirectoryInfo và Directory đều cho phép dò trong thư mục hiện hành để tìm các file phù hợp với một biểu thức lọc. Các biểu thức này thường sử dụng các ký tự wildcard như ? và *. Bạn cũng có thể sử dụng kỹ thuật tương tự để lấy các thư mục phù hợp với một mẫu nhất định bằng phương thức nạp chồng DirectoryInfo.GetDirectories. Ví dụ dưới đây sẽ lấy tên của tất cả các file trong một thư mục phù hợp với một biểu thức lọc. Thư mục và biểu thức lọc được truyền qua dòng lệnh. using System; using System.IO; public class WildcardTest { private static void Main(string[] args) { if (args.Length != 2) { Console.WriteLine( "USAGE: WildcardTest [directory] [filterExpression]"); return; }
  5. DirectoryInfo dir = new DirectoryInfo(args[0]); FileInfo[] files = dir.GetFiles(args[1]); // Hiển thị tên và kích thước file. foreach (FileInfo file in files) { Console.Write("Name: " + file.Name + " "); Console.WriteLine("Size: " + file.Length.ToString()); } Console.ReadLine(); } } Nếu muốn tìm trong thư mục con, bạn cần sử dụng đệ quy. Nhiều mục trong chương sử dụng kỹ thuật đệ quy để xử lý file, chẳng hạn 9.3 và 9.4. 1.3 Kiểm tra hai file có trùng nhau hay không Bạn cần so sánh nội dung của hai file và xác định chúng có trùng nhau hay không. Tính mã băm của mỗi file bằng lớp System.Security.Cryptography.HashAlgorithm rồi so sánh các mã băm. Có nhiều cách để so sánh nhiều file. Ví dụ, có thể xét một phần của file xem có giống nhau, hoặc đọc cả file so sánh từng byte. Cả hai cách trên đều đúng, nhưng trong một số trường hợp, sử dụng mã băm thuận tiện hơn. Một giải thuật băm sinh ra một dạng nhị phân đặc trưng (với kích thước nhỏ, thường khoảng 20 byte) cho file. Có khả năng hai file khác nhau có cùng mã băm, nhưng khả năng này hầu như không xảy ra. Thực tế, cả những thay đổi nhỏ nhất (chẳng hạn, chỉ thay đổi một bit của file nguồn) cũng có 50% khả năng thay đổi các bit của mã băm. Do đó, mã băm thường được sử dụng để phát hiện dữ liệu bị sửa đổi (mã băm sẽ được đề cập chi tiết hơn trong chương 14). Để tạo một mã băm, trước hết bạn phải tạo một đối tượng HashAlgorithm bằng phương thức tĩnh HashAlgorithm.Create. Sau đó gọi HashAlgorithm.ComputeHash để nhận một mảng byte chứa mã băm. Ví dụ dưới đây đọc hai tên file từ đối số dòng lệnh và kiểm tra hai file này có trùng nhau hay không: using System; using System.IO; using System.Security.Cryptography;
  6. public class CompareFiles { private static void Main(string[] args) { if (args.Length != 2) { Console.WriteLine("USAGE: CompareFiles [fileName] " + [fileName]"); return; } Console.WriteLine("Comparing " + args[0] + " and " + args[1]); // Tạo đối tượng băm. HashAlgorithm hashAlg = HashAlgorithm.Create(); // Tính mã băm cho file thứ nhất. FileStream fsA = new FileStream(args[0], FileMode.Open); byte[] hashBytesA = hashAlg.ComputeHash(fsA); fsA.Close(); // Tính mã băm cho file thứ hai. FileStream fsB = new FileStream(args[1], FileMode.Open); byte[] hashBytesB = hashAlg.ComputeHash(fsB); fsB.Close(); // So sánh mã băm. if (BitConverter.ToString(hashBytesA) == BitConverter.ToString(hashBytesB)) { Console.WriteLine("Files match."); }else { Console.WriteLine("No match."); } Console.ReadLine(); }
  7. } Các mã băm được so sánh bằng cách chuyển chúng thành chuỗi. Bạn cũng có thể duyệt qua mảng và so sánh từng byte. Cách này nhanh hơn một ít, nhưng việc chuyển 20 byte thành chuỗi không tốn nhiều chi phí nên không cần thiết. 1.4 Thao tác trên đường dẫn file Bạn cần lấy một phần đường dẫn file hoặc kiểm tra một đường dẫn file có ở dạng chuẩn hay không. Xử lý đường dẫn bằng lớp System.IO.Path. Bạn có thể sử dụng Path.GetFileName để lấy tên file từ đường dẫn, Path.ChangeExtension để thay đổi phần mở rộng của đường dẫn, và Path.Combine để tạo đường dẫn đầy đủ mà không cần quan tâm thư mục của bạn đã có ký tự phân cách thư mục(\) hay chưa. Thường khó thao tác với các đường dẫn file vì có vô số cách để mô tả một thư mục. Ví dụ, bạn có thể sử dụng đường dẫn tuyệt đối (C:\Temp), đường dẫn UNC (\\MyServer\\MyShare\Temp), hoặc một trong các đường dẫn tương đối (C:\Temp\MyFiles\..\ hay C:\Temp\MyFiles\..\..\Temp). Cách dễ nhất để xử lý các đường dẫn file là sử dụng các phương thức tĩnh của lớp Path để bảo đảm có thông tin đúng. Ví dụ, đoạn mã sau trích tên file từ một đường dẫn file: string filename = @"..\System\MyFile.txt"; filename = Path.GetFileName(filename); // filename bây giờ là "MyFile.txt". Và đoạn mã sau sử dụng Path.Combine để thêm tên file vào đường dẫn thư mục: string filename = @"..\..\myfile.txt"; string fullPath = @"c:\Temp"; filename = Path.GetFileName(filename); fullPath = Path.Combine(fullPath, filename); // fullPath bây giờ là "c:\Temp\myfile.txt". Cách này có ưu điểm là ký tự phân cách thư mục (\) sẽ tự động được thêm vào đường dẫn nếu cần thiết. Lớp Path cũng cung cấp các phương thức hữu ích sau đây để thao tác trên thông tin đường dẫn: • ChangeExtension—thay đổi phần mở rộng của file. Nếu phần mở rộng mới không được chỉ định, phần mở rộng hiện tại sẽ bị xóa.
  8. • GetDirectoryName—trả về thông tin của các thư mục nằm giữa ký tự phân cách thư mục (\) đầu và cuối. • GetFileNameWithoutExtension—tương tự như GetFileName, nhưng bỏ phần mở rộng. • GetFullPath—không có tác dụng đối với đường dẫn tuyệt đối, và nó sử dụng thư mục hiện hành để đổi một đường dẫn tương đối thành đường dẫn tuyệt đối. Ví dụ, nếu C:\Temp\ là thư mục hiện hành, gọi GetFullPath cho file test.txt sẽ trả về C:\Temp\ test.txt. • GetPathRoot—trả về chuỗi chứa thư mục gốc (ví dụ, "C:\"). Đối với đường dẫn tương đối, nó trả về tham chiếu rỗng. • HasExtension—trả về true nếu đường dẫn kết thúc với phần mở rộng. • IsPathRooted—trả về true nếu đường dẫn là tuyệt đối, false nếu đường dẫn là tương đối. Trong hầu hết trường hợp, một ngoại lệ sẽ bị ném nếu bạn truyền đường dẫn không hợp lệ cho một trong các phương thức này (chẳng hạn, đường dẫn có chứa các ký tự không hợp lệ). Tuy nhiên, những đường dẫn không hợp lệ do chứa các ký tự wildcard sẽ không làm sinh ra ngoại lệ.
Đồng bộ tài khoản