Python đọc và ghi tràn ngăn xếp tập tin

Mẹo hôm nay không hẳn là về một phiên bản mới hấp dẫn của ASP. NET Core hoặc bất cứ thứ gì tương tự. Tôi thường tìm kiếm trên Google những câu hỏi "đơn giản" như cách viết văn bản vào tệp một cách nhanh nhất và hiệu quả. Hầu hết các kết quả là các bài đăng trên blog bằng cách sử dụng. NET 2 làm ví dụ hoặc câu trả lời của StackOverflow từ năm 2010. Điểm chung cho tất cả các mẫu là chỉ các dòng mã thiết yếu được thêm vào và không tập trung vào các khía cạnh quan trọng như xử lý lỗi được đánh dấu. Bài đăng này là nỗ lực của tôi để tổng hợp những gì tôi đã học được trong những năm qua

Tập tin. ViếtTất cả văn bản

Ví dụ đầu tiên về việc ghi một chuỗi vào một tệp đang sử dụng phương thức

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
0. Lý do tôi đề cập đến điều này như một ví dụ đầu tiên, bởi vì đó là điều mà 95% [có thể nhiều hơn?] mọi người đang làm. Và tôi hiểu tại sao vì tôi đã làm điều này trong nhiều năm. Hãy nhìn vào sự đơn giản của phương pháp này

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];

Ví dụ tạo ra một tệp có tên

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
1 với nội dung của biến
var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
2. Cách tiếp cận này có thể phù hợp với các tệp văn bản nhỏ hơn, nhưng không phải là lựa chọn mặc định khi xử lý lượng dữ liệu lớn hơn

Hãy tạo một kịch bản cho phần còn lại của bài đăng, nơi sẽ lưu trữ một lượng lớn dữ liệu và sử dụng

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
3 để ghi dữ liệu vào một tệp

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];

Trong ví dụ này, tôi tạo 1 triệu dòng và lưu trữ chúng trong tệp

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
1. Trên máy của tôi, mã mất khoảng 100 ms. Điều đó khá ấn tượng đối với hầu hết các kịch bản, nhưng tôi chắc rằng chúng ta có thể làm tốt hơn

Tập tin. Tạo văn bản

Trường hợp phương thức

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
3 viết mọi thứ cùng một lúc, bạn có thể cần phải viết theo lô. Hãy sử dụng lại
var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
6 từ trước để minh họa cách thực hiện điều này bằng cách sử dụng phương pháp
var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
7

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
0

Phương thức

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
8 trả về một đối tượng
var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
9 mà chúng ta có thể sử dụng để xuất bản nhiều dòng vào một tệp hoặc thậm chí ghi vào tệp một
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
00 cùng một lúc. Khi loại bỏ
var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
9, bộ đệm được chuyển sang luồng bên dưới và xử lý tệp được giải phóng. Bạn có thể gọi phương thức
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
02 theo cách thủ công để có được kết quả tương tự

thêm văn bản

Để thay thế cho các phương thức trước đó hoặc nếu bạn chỉ cần nối thêm văn bản vào một tệp hiện có, thì cũng có các phương thức nối thêm có sẵn dưới dạng các phương thức tĩnh

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
6

Cả hai phương thức nối thêm văn bản dưới dạng một chuỗi hoặc nhiều dòng trong một lần ghi. Bạn có thể chia dòng thành nhiều lần lặp lại bằng phương pháp

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
03

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
8

Lưu ý chỉ gọi

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
03 một lần trong suốt thời gian phải ghi nội dung vào tệp. Đôi khi tôi thấy mã như thế này

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
0

Như chúng ta sẽ thấy ở phần sau của bài đăng này, điều này sẽ rất nặng về hiệu năng do tệp được chạm nhiều lần trên đĩa

Tập hồ sơ

Tôi đã nhìn thấy nó rất nhiều lần. Khách hàng phàn nàn rằng giao diện người dùng "treo" khi nhấp vào thứ gì đó liên quan đến việc đọc hoặc ghi vào một tệp lớn. Bạn có thể biết nơi này sẽ đi, phải không?

Một lựa chọn tốt để ghi không đồng bộ vào một tệp từ. NET đang sử dụng lớp FileStream. Hãy chuyển ví dụ từ trước sang sử dụng FileStream. Đầu tiên, một ví dụ đồng bộ

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
1

Trong ví dụ này, tôi bọc

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
05 trong một cách sử dụng, điều này sẽ [không ngạc nhiên] loại bỏ luồng sau khi thực hiện xong với nó. Bạn chủ yếu sử dụng phương thức
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
06, phương thức này ghi một hoặc nhiều byte vào luồng bên dưới. Bạn có thể gọi phương thức
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
06 nhiều lần, đây là một lựa chọn tốt khi cần lặp lại một thứ gì đó như bản ghi cơ sở dữ liệu và ghi từng bản ghi vào cùng một tệp

Sử dụng

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
05 thường nhanh hơn các phương pháp từ hai ví dụ trước. Khi lưu trữ dữ liệu được tạo cho bài đăng này, sử dụng FileStream nhanh hơn khoảng 20% ​​so với các ví dụ khác. Và thậm chí còn nhanh hơn khi chạy trên. NET lõi

Bây giờ cho phiên bản không đồng bộ

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
6

Lưu ý cách tôi đã thêm

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
09 [tham số ____160] làm tham số cuối cùng cho hàm tạo
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
05. Điều này báo cho
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
05 sử dụng IO không đồng bộ. Sau đó, tôi gọi phương thức
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
63 thay vì phương thức
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
06 từ ví dụ trước. Ngoài ra, hãy nhớ
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
65 phương pháp

Khi xem hiệu suất của mã không đồng bộ trong ví dụ trước, hiệu suất tương ứng với hai ví dụ đầu tiên. Vậy tại sao lại sử dụng async

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
05 khi chậm hơn so với đồng bộ?

Hiệu suất

Sử dụng đúng phương pháp cho công việc là rất quan trọng, thay vì chỉ ném mình vào câu trả lời đầu tiên từ StackOverflow. Để giới thiệu sự khác biệt về hiệu suất của các phương pháp được liệt kê trong bài đăng này, tôi sẽ sử dụng việc thêm vào một tệp vì đó là tình huống mà tôi thường thấy mọi người mắc sai lầm. Tôi đã hiển thị mã nhưng hãy tóm tắt lại với yêu cầu nối thêm 10.000 dòng vào một tệp

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
5

Như bạn có thể thấy từ mã, tôi đã thực hiện 4 lần thử khác nhau để ghi 10.000 dòng vào một tệp. Kết quả trông như thế này trên máy của tôi

Tập tin. AppendAllText nhiều lần85837File. AppendAllLines 1 lần9File. AppendText nhiều lần51888File. Nối thêm văn bản 1 lần9

Không có gì đáng ngạc nhiên, việc chạm vào các tệp nhiều lần bằng cách gọi

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
67 nhiều lần hoặc nhúng
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
68 vào trong
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
69 thay vì cách khác sẽ dẫn đến chi phí rất lớn về mặt hiệu suất

NET Core/. MẠNG 5

Chúng tôi đã chạm vào một thời gian ngắn. NET Core/. NET 5 trong một trong các ví dụ trước. Tôi vui mừng thông báo với bạn rằng tất cả các API chúng tôi đã sử dụng trong bài đăng đều có chung chữ ký trong. NET Core/. MẠNG 5. Điều này có nghĩa là bạn có thể chuyển dự án của mình sang. NET Core/. NET 5 và biên dịch lại tất cả các ví dụ ở trên mà không có bất kỳ lỗi biên dịch nào

Khi chạy các ví dụ, tất cả mã chạy nhanh hơn khoảng 5-10 ms. sự khác biệt giữa. NET Full Framework và cái mới. NET tất nhiên sẽ phụ thuộc vào phần cứng của bạn

xử lý ngoại lệ

Một khía cạnh thường bị bỏ qua [nhưng rất quan trọng] của việc ghi vào tệp là xử lý ngoại lệ. Rất nhiều thứ có thể sai khi tương tác với hệ thống tệp. Có hai nhóm ngoại lệ bạn cần phải lo lắng

  1. Vấn đề với đường dẫn
  2. Sự cố khi ghi vào đĩa

Trước khi đào sâu vào từng nhóm, hãy thêm xử lý ngoại lệ vào ví dụ về

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
0

var sb = new StringBuilder[];
var lines = Enumerable
    .Range[1, 1000000]
    .Select[i => $"Line number {i}"]
    .ToList[];
lines.ForEach[i => sb.AppendLine[$"Line number {i}"]];

var content = sb.ToString[];
File.WriteAllText["output.txt", content];
0
Xin lưu ý rằng việc triển khai luồng kiểm soát bằng cách sử dụng các ngoại lệ được coi là một thông lệ không tốt. Mặc dù mã ở trên dùng làm ví dụ tốt, nhưng bạn có thể muốn kiểm tra các tình huống phổ biến như không tìm thấy thư mục trước khi thử ghi vào tệp

Vấn đề với đường dẫn

Có nhiều vấn đề khác nhau có thể khiến ngoại lệ xảy ra khi xử lý tệp. Tôi đã viết blog về một vài trong số này ở đây. hệ thống gỡ lỗi. IO. FileNotFoundException - Nguyên nhân, cách khắc phục và Gỡ lỗi Hệ thống. UnauthorizedAccessException [thường theo sau bởi. Truy cập vào đường dẫn bị từ chối]

Bạn phải luôn bao bọc việc viết mã vào một tệp trong quá trình thử bắt và quyết định điều gì sẽ xảy ra nếu các trường hợp ngoại lệ sau xảy ra

  • var writeMe = "File content";
    File.WriteAllText["output.txt", writeMe];
    
    81
  • var writeMe = "File content";
    File.WriteAllText["output.txt", writeMe];
    
    82

Một số trường hợp ngoại lệ có thể được triển khai dưới dạng tự động sửa lỗi bằng mã của bạn [như tạo một thư mục bị thiếu và ghi lại vào tệp], trong khi những trường hợp khác sẽ dẫn đến một thông báo cho người dùng hoặc một thông báo tường trình được ghi vào nhật ký của bạn

Sự cố khi ghi vào đĩa

Các ngoại lệ khác giải quyết các vấn đề trong khi ghi nội dung vào tệp. Sau khi bạn biết rằng tệp có thể được tạo và thư mục chứa tệp mới tồn tại và quy trình có đủ quyền truy cập, một số lượng lớn các lỗi vẫn có thể xảy ra

Hầu hết các vấn đề xảy ra trong quá trình ghi thực tế, được đưa ra dưới dạng

var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
83.
var writeMe = "File content";
File.WriteAllText["output.txt", writeMe];
84 dù sao cũng là lớp cơ sở cho hầu hết các trường hợp ngoại lệ liên quan đến tệp, vì vậy hãy đảm bảo nắm bắt từng lớp con để có thể xử lý các tình huống khác nhau cho phù hợp

Bài trước

Thực tiễn tốt nhất về xử lý ngoại lệ C#

Bài tiếp theo

Các bản sửa lỗi và ngoại lệ phổ biến trong Entity Framework và SQL Server

elmah. io. Ghi nhật ký lỗi và Giám sát thời gian hoạt động cho các ứng dụng web của bạn

Bài đăng trên blog này được mang đến cho bạn bởi elmah. io. elmah. io là ghi nhật ký lỗi, giám sát thời gian hoạt động, theo dõi triển khai và nhịp tim dịch vụ cho bạn. NET và ứng dụng JavaScript. Ngừng dựa vào người dùng của bạn để thông báo cho bạn khi có điều gì đó không ổn hoặc đào qua hàng trăm MB tệp nhật ký trải rộng trên các máy chủ. với elmah. io, chúng tôi lưu trữ tất cả thông điệp tường trình của bạn, thông báo cho bạn qua các kênh phổ biến như email, Slack và Microsoft Teams, đồng thời giúp bạn sửa lỗi nhanh chóng

Python có thể đọc và ghi tệp không?

Python cung cấp nhiều phương thức khác nhau để đọc và ghi vào tệp trong đó mỗi chức năng hoạt động khác nhau . Một điều quan trọng cần lưu ý là chế độ hoạt động của tệp. Để đọc một tệp, bạn cần mở tệp ở chế độ đọc hoặc ghi. Trong khi ghi vào một tệp bằng Python, bạn cần mở tệp ở chế độ ghi.

Có thể tràn ngăn xếp trong Python không?

Nhiều thao tác trong Python thực hiện một số loại lệnh gọi ở cấp độ C. Hầu hết trong số này sẽ tiếp tục tiêu thụ ngăn xếp C và sẽ dẫn đến ngoại lệ StackOverflow nếu xảy ra đệ quy không kiểm soát được

Làm thế nào một tập tin có thể được mở cho cả đọc và viết?

Có nhiều chế độ mở file. .
r - mở tệp ở chế độ đọc
w - mở hoặc tạo tệp văn bản ở chế độ ghi
a - mở tệp ở chế độ chắp thêm
r+ - mở tệp ở cả chế độ đọc và ghi
a+ - mở tệp ở cả chế độ đọc và ghi
w+ - mở tệp ở cả chế độ đọc và ghi

Chủ Đề