2010-03-28 34 views
26

Tôi muốn đọc từng dòng một tập tin, mà không biết chiều dài của dòng trước đó. Dưới đây là những gì tôi có cho đến nay:Đọc dòng từ tập tin mà không biết chiều dài dòng

int ch = getc(file); 
int length = 0; 
char buffer[4095]; 

while (ch != '\n' && ch != EOF) { 
    ch = getc(file); 
    buffer[length] = ch; 
    length++; 
} 

printf("Line length: %d characters.", length); 

char newbuffer[length + 1]; 

for (int i = 0; i < length; i++) 
    newbuffer[i] = buffer[i]; 

newbuffer[length] = '\0'; // newbuffer now contains the line. 

tôi bây giờ có thể tìm ra chiều dài dòng, nhưng chỉ cho dòng ngắn hơn 4095 ký tự, cộng với hai mảng char có vẻ giống như một cách vụng về làm nhiệm vụ. Có cách nào tốt hơn để làm điều này (tôi đã sử dụng fgets() nhưng đã nói nó không phải là cách tốt nhất)?

--Ry

Trả lời

14

Bạn có thể bắt đầu với một số kích thước phù hợp của sự lựa chọn của bạn và sau đó sử dụng realloc giữa chừng nếu bạn cần thêm không gian như:

int CUR_MAX = 4095; 
char *buffer = (char*) malloc(sizeof(char) * CUR_MAX); // allocate buffer. 
int length = 0; 

while ((ch != '\n') && (ch != EOF)) { 
    if(length ==CUR_MAX) { // time to expand ? 
     CUR_MAX *= 2; // expand to double the current size of anything similar. 
     buffer = realloc(buffer, CUR_MAX); // re allocate memory. 
    } 
    ch = getc(file); // read from stream. 
    buffer[length] = ch; // stuff in buffer. 
    length++; 
} 
. 
. 
free(buffer); 

Bạn sẽ phải kiểm tra lỗi phân bổ sau khi cuộc gọi đến mallocrealloc.

+1

It's 'realloc' not' relloc'. –

+0

Cũng giống như một lưu ý, việc đọc từng ký tự rất chậm. Bạn nên đọc nó trong khối lớn (4-16k). – Blindy

+4

@Blindy: tối ưu hóa sớm ... –

1

Bạn thân thiết. Về cơ bản bạn muốn đọc khối dữ liệu và kiểm tra chúng cho các ký tự \n. Nếu bạn tìm thấy một, tốt, bạn có một kết thúc của dòng. Nếu không, bạn phải tăng bộ đệm (tức là cấp phát bộ đệm mới gấp đôi kích thước của bộ đệm đầu tiên và sao chép dữ liệu từ bộ đệm đầu tiên vào bộ đệm mới, sau đó xóa bộ đệm cũ và đổi tên bộ đệm mới của bạn thành cũ - hoặc chỉ realloc nếu bạn đang ở trong C) sau đó đọc một số chi tiết cho đến khi bạn tìm thấy một kết thúc.

Khi bạn có kết thúc, văn bản từ đầu bộ đệm đến ký tự \n là dòng của bạn. Sao chép nó vào một bộ đệm hoặc làm việc trên nó tại chỗ, tùy thuộc vào bạn.

Sau khi bạn đã sẵn sàng cho dòng tiếp theo, bạn có thể sao chép phần còn lại của đầu vào trên dòng hiện tại (về cơ bản là dịch chuyển trái) và điền phần còn lại của bộ đệm với dữ liệu từ đầu vào. Sau đó bạn đi lại cho đến khi bạn hết dữ liệu.

Điều này tất nhiên có thể được tối ưu hóa, với một bộ đệm tròn ví dụ, nhưng điều này nên được quá đủ cho bất kỳ thuật toán io ràng buộc hợp lý.

5

Bạn có thể muốn xem xét Chuck B. Falconer's public domain ggets library. Nếu bạn đang sử dụng hệ thống có glibc, bạn có thể có chức năng getline (không chuẩn) có sẵn cho bạn.

+0

Đẹp! Tôi tin rằng tôi có thể tin tưởng hầu hết các hệ thống giống như UNIX để cài đặt glibc, vì vậy đây chắc chắn là một cách tuyệt vời để đọc trong dòng. – ryyst

+0

Hơn nữa, 'getline' đã được đưa vào tiêu chuẩn POSIX gần đây nhất, vì vậy nó * là * chuẩn trên Unix bây giờ. Tuy nhiên, không đảm bảo rằng nó được bao gồm với c * mỗi se *, tuy nhiên. – dmckee

1

Đó là cách tôi đã làm nó cho stdin, nếu bạn gọi nó là readLine(NULL, 0) chức năng phân bổ một bộ đệm cho bạn với kích thước 1024 và để cho nó phát triển ở bước 1024. Nếu bạn gọi hàm với readLine(NULL, 10) bạn nhận được đệm với các bước 10. Nếu bạn có một bộ đệm, bạn có thể cung cấp nó với kích thước của nó.

#include <stdio.h> 
#include <stdlib.h> 
#include <assert.h> 
#include <string.h> 

char *readLine(char **line, size_t *length) 
{ 
    assert(line != NULL); 
    assert(length != NULL); 

    size_t count = 0; 

    *length = *length > 0 ? *length : 1024; 

    if (!*line) 
    { 
     *line = calloc(*length, sizeof(**line)); 
     if (!*line) 
     { 
      return NULL; 
     } 
    } 
    else 
    { 
     memset(*line, 0, *length); 
    } 

    for (int ch = getc(stdin); ch != '\n' && ch != EOF; ch = getc(stdin)) 
    { 
     if (count == *length) 
     { 
      *length += 2; 
      *line = realloc(*line, *length); 
      if (!*line) 
      { 
       return NULL; 
      } 
     } 

     (*line)[count] = (char)ch; 

     ++count; 
    } 

    return *line; 
} 
Các vấn đề liên quan