Có getline
chức năng sử dụng fread
(khối I/O) thay vì fgetc
(ký tự I/O)?C: Đọc một tệp văn bản (có các dòng có độ dài thay đổi) theo dòng fread()/fgets() thay vì fgetc() (khối I/O so với ký tự I/O)
Có hình phạt về hiệu suất để đọc ký tự tệp theo ký tự qua fgetc
. Chúng tôi nghĩ rằng để cải thiện hiệu suất, chúng tôi có thể sử dụng lượt đọc khối qua fread
trong vòng lặp bên trong của getline
. Tuy nhiên, điều này giới thiệu hiệu ứng không mong muốn của việc đọc qua cuối dòng. Ít nhất, điều này sẽ yêu cầu thực hiện getline
để theo dõi phần "chưa đọc" của tệp, yêu cầu sự trừu tượng vượt ra ngoài ngữ nghĩa ANSI C FILE. Đây không phải là điều chúng tôi muốn thực hiện!
Chúng tôi đã lược tả ứng dụng của chúng tôi và hiệu suất chậm bị cô lập với thực tế là chúng tôi đang sử dụng ký tự tệp lớn theo ký tự qua fgetc
. Phần còn lại của chi phí thực sự có một chi phí tầm thường bằng cách so sánh. Chúng tôi luôn đọc tuần tự mọi dòng của tệp, từ đầu đến cuối và chúng tôi có thể khóa toàn bộ tệp trong suốt thời gian đọc. Điều này có thể làm cho fread
dựa trên getline
dễ triển khai hơn.
Vì vậy, hãy thực hiện chức năng getline
sử dụng fread
(chặn I/O) thay vì fgetc
(ký tự I/O) tồn tại? Chúng tôi khá chắc chắn nó, nhưng nếu không, làm thế nào chúng ta nên thực hiện nó?
Cập nhật Tìm thấy một bài viết hữu ích, Handling User Input in C, bởi Paul Hsieh. Đó là một cách tiếp cận dựa trên fgetc
, nhưng nó có một cuộc thảo luận thú vị của giải pháp thay thế (bắt đầu với cách xấu gets
là, sau đó thảo luận fgets
):
Mặt khác vặn lại phổ biến từ các lập trình viên C (ngay cả những người được coi là có kinh nghiệm) là để nói rằng fgets() nên được sử dụng thay thế. Tất nhiên, tự nó, fgets() không thực sự xử lý đầu vào của người dùng mỗi lần. Bên cạnh việc có điều kiện kết thúc chuỗi kỳ lạ (khi gặp \ n hoặc EOF, nhưng không \ 0) cơ chế được chọn để kết thúc khi bộ đệm đã đạt đến công suất chỉ đơn giản là dừng hoạt động fgets() và \ 0 chấm dứt nó. Vì vậy, nếu đầu vào của người dùng vượt quá độ dài của bộ đệm được phân bổ, fgets() trả về một phần kết quả. Để đối phó với các lập trình viên này có một vài lựa chọn; 1) chỉ đơn giản là đối phó với đầu vào người dùng cắt ngắn (không có cách nào để cung cấp cho người dùng rằng đầu vào đã bị cắt bớt, trong khi họ đang cung cấp đầu vào) 2) Mô phỏng một mảng ký tự có thể phát triển và điền nó với các cuộc gọi liên tiếp đến fgets (). Giải pháp đầu tiên, hầu như luôn luôn là một giải pháp rất nghèo cho đầu vào người dùng có độ dài thay đổi vì bộ đệm chắc chắn sẽ là quá lớn phần lớn thời gian vì nó cố gắng chụp quá nhiều trường hợp thông thường và quá nhỏ đối với các trường hợp bất thường. Các giải pháp thứ hai là tốt, ngoại trừ việc nó có thể phức tạp để thực hiện một cách chính xác. Không giao dịch với fgets ' hành vi kỳ lạ đối với' \ 0 '.
Tập thể dục còn lại để người đọc: Để xác định có bao nhiêu byte đã thực sự đọc bởi một cuộc gọi đến fgets(), người ta có thể thử bằng cách quét, giống như nó, cho một '\ n' và bỏ qua trên bất kỳ '\ 0' nào trong khi không vượt quá kích thước được chuyển đến fgets(). Giải thích lý do tại sao điều này không đủ cho dòng cuối cùng của luồng.Điểm yếu của ftell() ngăn không cho nó giải quyết vấn đề này hoàn toàn?
Tập thể dục còn lại để người đọc: Giải quyết vấn đề xác định độ dài của dữ liệu tiêu thụ bởi fgets() bằng cách ghi đè toàn bộ đệm với một giá trị khác không giữa mỗi cuộc gọi đến fgets().
Vì vậy, với fgets() chúng tôi là trái với sự lựa chọn của văn bản rất nhiều mã và sống chung với một dòng điều kiện chấm dứt mà không phù hợp với phần còn lại của thư viện C, hoặc có một tùy ý cắt. Nếu điều này không đủ tốt, thì chúng ta còn lại cái gì? scanf() trộn lẫn với việc đọc theo cách không thể tách rời và fread() sẽ đọc qua cuối chuỗi. Trong ngắn hạn, thư viện C để lại cho chúng tôi không có gì. Chúng tôi buộc phải tự mình cuộn dựa trên đầu trang của fgetc() trực tiếp. Vì vậy, hãy cho nó một shot.
Vì vậy, thực hiện một chức năng getline
đó là dựa trên fgets
(và không cắt cụt các đầu vào) tồn tại?
Đến câu hỏi mới của bạn ở cuối, có, nó tồn tại. Tôi vạch ra nó trong câu trả lời của tôi. Bài viết bạn đã trích dẫn đề cập đến một vấn đề với dòng cuối cùng không phải là dòng mới được kết thúc; Tôi đã thực hiện điều này một vấn đề không bằng cách điền sẵn bộ đệm bằng ''\ n'' và cung cấp cách để phát hiện tình trạng. –
Cũng lưu ý rằng giải pháp của Paul Hsieh để sử dụng 'fgetc' rất tệ. Về triển khai hiện đại, do yêu cầu hỗ trợ khóa trong trường hợp nhiều luồng truy cập cùng một đối tượng 'FILE', sử dụng' fgetc' sẽ rất chậm. Bạn có thể sử dụng 'getc_unlocked' (nhưng đây là một hàm POSIX, không phải là hàm C chuẩn), nhưng ngay cả với việc mở rộng macro tối ưu của' getc_unlocked', cách 'fgets' tìm kiếm bộ đệm cho' '\ n'' (tức là sử dụng 'memchr') sẽ nhanh gấp nhiều lần so với bất cứ thứ gì bạn có thể làm mà không cần truy cập vào bộ đệm trong. Cũng lưu ý rằng nếu bạn có POSIX (2008), bạn đã có 'getline' rồi. –