Làm thế nào để bạn xác định kích thước của một tập tin trong C?


137

Làm thế nào tôi có thể tìm ra kích thước của một tập tin, tính bằng byte?

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}

Bạn sẽ cần sử dụng chức năng thư viện để lấy thông tin chi tiết về tệp. Vì C hoàn toàn độc lập với nền tảng, bạn sẽ cần cho chúng tôi biết bạn đang phát triển nền tảng / hệ điều hành nào!
Chris Roberts

Tại sao char* file, tại sao không FILE* file? -1
Ông Oscar

-1 vì các chức năng tệp nên chấp nhận mô tả tệp không phải đường dẫn tệp
Mr Oscar

Câu trả lời:


144

Dựa trên mã của NilObject:

#include <sys/stat.h>
#include <sys/types.h>

off_t fsize(const char *filename) {
    struct stat st; 

    if (stat(filename, &st) == 0)
        return st.st_size;

    return -1; 
}

Thay đổi:

  • Tạo đối số tên tệp a const char.
  • Sửa struct statđịnh nghĩa, thiếu tên biến.
  • Trả -1về lỗi thay vì 0, điều này sẽ mơ hồ cho một tệp trống. off_tlà một loại đã ký nên điều này là có thể.

Nếu bạn muốn fsize()in một thông báo bị lỗi, bạn có thể sử dụng điều này:

#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

off_t fsize(const char *filename) {
    struct stat st;

    if (stat(filename, &st) == 0)
        return st.st_size;

    fprintf(stderr, "Cannot determine size of %s: %s\n",
            filename, strerror(errno));

    return -1;
}

Trên các hệ thống 32 bit, bạn nên biên dịch tùy chọn này với tùy chọn -D_FILE_OFFSET_BITS=64, nếu không off_tsẽ chỉ giữ các giá trị tối đa 2 GB. Xem phần "Sử dụng LFS" của Hỗ trợ tệp lớn trong Linux để biết chi tiết.


19
Đây là đặc thù của Linux / Unix - có lẽ đáng để chỉ ra vì câu hỏi không chỉ định HĐH.
Hội trường Drew

1
Bạn có thể thay đổi kiểu trả về thành ssize_t và bỏ kích thước từ off_t mà không gặp rắc rối nào. Nó có vẻ có ý nghĩa hơn khi sử dụng ssize_t :-) (Không bị nhầm lẫn với size_t không được ký và không thể được sử dụng để chỉ ra lỗi.)
Ted Percival

1
Để biết thêm mã di động, hãy sử dụng fseek+ ftelltheo đề xuất của Derek.
Ciro Santilli 郝海东 冠状 病 事件 法轮功

9
Để biết thêm mã di động, hãy sử dụng fseek+ ftelltheo đề xuất của Derek. Số tiêu chuẩn C đặc biệt khẳng định rằng fseek()để SEEK_ENDtrên một tập tin nhị phân là hành vi không xác định. 7.19.9.2 fseekHàm ... Luồng nhị phân không cần hỗ trợ một cách có ý nghĩa fseekcác cuộc gọi với giá trị từSEEK_END , và như được lưu ý dưới đây, từ chú thích số 234 trên p. 267 của C chuẩn liên kết, và đó nhãn đặc biệt fseekđể SEEK_ENDtrong một dòng nhị phân như hành vi không xác định. .
Andrew Henle

74

Đừng sử dụng int. Những tập tin có kích thước trên 2 gigabyte ngày nay rất phổ biến.

Đừng sử dụng unsigned int. Các tệp có kích thước trên 4 gigabyte là phổ biến vì một số bụi bẩn ít phổ biến hơn

IIRC thư viện chuẩn định nghĩa off_tlà số nguyên 64 bit không dấu, đây là thứ mà mọi người nên sử dụng. Chúng ta có thể định nghĩa lại là 128 bit trong một vài năm khi chúng ta bắt đầu có 16 tệp exabyte treo xung quanh.

Nếu bạn đang ở trên windows, bạn nên sử dụng GetFileSizeEx - nó thực sự sử dụng số nguyên 64 bit đã ký, vì vậy họ sẽ bắt đầu gặp sự cố với 8 tệp exabyte. Microsoft ngu ngốc! :-)


1
Tôi đã sử dụng trình biên dịch trong đó off_t là 32 bit. Cấp, đây là trên các hệ thống nhúng, nơi các tệp 4GB ít phổ biến hơn. Dù sao, POSIX cũng định nghĩa off64_t và các phương thức tương ứng để thêm vào sự nhầm lẫn.
Aaron Campbell

Tôi luôn thích những câu trả lời giả định Windows và không làm gì khác ngoài chỉ trích câu hỏi. Bạn có thể vui lòng thêm một cái gì đó tuân thủ POSIX không?
SS Anne

1
@ JL2210 câu trả lời được chấp nhận từ Ted Percival cho thấy một giải pháp tuân thủ theo nguyên tắc, vì vậy tôi thấy không có ý nghĩa gì trong việc lặp lại điều hiển nhiên. Tôi (và 70 người khác) nghĩ rằng việc thêm ghi chú về windows và không sử dụng số nguyên 32 bit đã ký để thể hiện kích thước tệp là một giá trị gia tăng trên đó. Chúc mừng
Orion Edwards

30

Giải pháp của Matt sẽ hoạt động, ngoại trừ đó là C ++ thay vì C và thông báo ban đầu không cần thiết.

unsigned long fsize(char* file)
{
    FILE * f = fopen(file, "r");
    fseek(f, 0, SEEK_END);
    unsigned long len = (unsigned long)ftell(f);
    fclose(f);
    return len;
}

Sửa lỗi niềng răng cho bạn, quá. ;)

Cập nhật: Đây thực sự không phải là giải pháp tốt nhất. Nó bị giới hạn ở các tệp 4GB trên Windows và nó có thể chậm hơn so với việc chỉ sử dụng một cuộc gọi dành riêng cho nền tảng như GetFileSizeExhoặc stat64.


Vâng, bạn nên. Tuy nhiên, trừ khi có một lý do thực sự thuyết phục không viết riêng cho nền tảng, có lẽ bạn chỉ nên sử dụng một cuộc gọi dành riêng cho nền tảng thay vì kiểu mở / tìm kiếm kết thúc / nói / đóng.
Công viên Derek

1
Xin lỗi về việc trả lời trễ, nhưng tôi đang gặp vấn đề lớn ở đây. Nó khiến ứng dụng bị treo khi truy cập các tệp bị hạn chế (như mật khẩu được bảo vệ hoặc các tệp hệ thống). Có cách nào để hỏi người dùng mật khẩu khi cần không?
Justin

@Justin, có lẽ bạn nên mở một câu hỏi mới cụ thể về vấn đề bạn đang gặp phải và cung cấp chi tiết về nền tảng bạn đang truy cập, cách bạn truy cập các tệp và hành vi là gì.
Công viên Derek

1
Cả hai C99 và C11 trở lại long inttừ ftell(). (unsigned long)đúc không cải thiện phạm vi như đã bị giới hạn bởi chức năng. ftell()trả về -1 khi có lỗi và bị xáo trộn với diễn viên. Đề nghị fsize()trả lại cùng loại với ftell().
chux - Tái lập Monica

Tôi đồng ý. Các diễn viên là để phù hợp với nguyên mẫu ban đầu trong câu hỏi. Mặc dù vậy, tôi không thể nhớ tại sao tôi lại biến nó thành unsign dài thay vì int unsign.
Công viên Derek

15

** Đừng làm điều này ( tại sao? ):

Trích dẫn tài liệu chuẩn C99 mà tôi tìm thấy trực tuyến: "Đặt chỉ báo vị trí tệp thành cuối tệp, như với fseek(file, 0, SEEK_END), có hành vi không xác định cho luồng nhị phân (vì có thể theo dõi các ký tự null) hoặc cho bất kỳ luồng nào có mã hóa phụ thuộc trạng thái điều đó không chắc chắn kết thúc ở trạng thái dịch chuyển ban đầu. **

Thay đổi định nghĩa thành int để thông báo lỗi có thể được truyền đi, sau đó sử dụng fseek()ftell()để xác định kích thước tệp.

int fsize(char* file) {
  int size;
  FILE* fh;

  fh = fopen(file, "rb"); //binary mode
  if(fh != NULL){
    if( fseek(fh, 0, SEEK_END) ){
      fclose(fh);
      return -1;
    }

    size = ftell(fh);
    fclose(fh);
    return size;
  }

  return -1; //error
}

5
@mezhaka: Báo cáo CERT đó đơn giản là sai. fseekoftello(hoặc fseekftellnếu bạn bị mắc kẹt mà không có trước đây và hài lòng với các giới hạn về kích thước tệp bạn có thể làm việc) là cách chính xác để xác định độ dài của tệp. statcác giải pháp dựa trên không hoạt động trên nhiều "tệp" (chẳng hạn như các thiết bị khối) và không khả dụng cho các hệ thống không phải POSIX-ish.
R .. GitHub DỪNG GIÚP ICE

1
Đây là cách duy nhất để có được kích thước tập tin trên nhiều hệ thống phù hợp phi posix (như mbed rất tối giản của tôi)
Earlz

9

POSIX

Các POSIX tiêu chuẩn có phương pháp riêng của mình để có được kích thước tập tin.
Bao gồm cáisys/stat.h tiêu đề để sử dụng chức năng.

Tóm tắc

  • Lấy số liệu thống kê tập tin bằng cách sử dụng stat(3) .
  • Có được st_sizetài sản.

Ví dụ

Lưu ý : Nó giới hạn kích thước 4GB. Nếu không phải Fat32hệ thống tập tin thì hãy sử dụng phiên bản 64 bit!

#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char** argv)
{
    struct stat info;
    stat(argv[1], &info);

    // 'st' is an acronym of 'stat'
    printf("%s: size=%ld\n", argv[1], info.st_size);
}
#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char** argv)
{
    struct stat64 info;
    stat64(argv[1], &info);

    // 'st' is an acronym of 'stat'
    printf("%s: size=%ld\n", argv[1], info.st_size);
}

ANSI C (tiêu chuẩn)

Các ANSI C không trực tiếp cung cấp cách thức để xác định độ dài của tập tin.
Chúng ta sẽ phải sử dụng tâm trí của chúng tôi. Hiện tại, chúng tôi sẽ sử dụng phương pháp tìm kiếm!

Tóm tắc

  • Tìm kiếm tập tin đến cuối bằng cách sử dụng fseek(3) .
  • Nhận vị trí hiện tại bằng cách sử dụng ftell(3).

Thí dụ

#include <stdio.h>

int main(int argc, char** argv)
{
    FILE* fp = fopen(argv[1]);
    int f_size;

    fseek(fp, 0, SEEK_END);
    f_size = ftell(fp);
    rewind(fp); // to back to start again

    printf("%s: size=%ld", (unsigned long)f_size);
}

Nếu tập tin là stdinhoặc một đường ống. POSIX, ANSI C sẽ không hoạt động.
Nó sẽ trở lại 0nếu tập tin là một đường ống hoặc stdin.

Ý kiến : Bạn nên sử dụng tiêu chuẩn POSIX thay thế. Bởi vì, nó có hỗ trợ 64 bit.


1
struct _stat64__stat64()cho _Windows.
Bob Stein

5

Và nếu bạn đang xây dựng một ứng dụng Windows, hãy sử dụng API GetFileSizeEx vì I / O của tệp CRT rất lộn xộn, đặc biệt là để xác định độ dài tệp, do đặc thù trong cách biểu thị tệp trên các hệ thống khác nhau;)


5

Nếu bạn ổn với việc sử dụng thư viện std c:

#include <sys/stat.h>
off_t fsize(char *file) {
    struct stat filestat;
    if (stat(file, &filestat) == 0) {
        return filestat.st_size;
    }
    return 0;
}

24
Đó không phải là tiêu chuẩn C. Đây là một phần của tiêu chuẩn POSIX, nhưng không phải là tiêu chuẩn C.
Công viên Derek


1

Tôi đã sử dụng bộ mã này để tìm độ dài tập tin.

//opens a file with a file descriptor
FILE * i_file;
i_file = fopen(source, "r");

//gets a long from the file descriptor for fstat
long f_d = fileno(i_file);
struct stat buffer;
fstat(f_d, &buffer);

//stores file size
long file_length = buffer.st_size;
fclose(i_file);

1

Thử cái này --

fseek(fp, 0, SEEK_END);
unsigned long int file_size = ftell(fp);
rewind(fp);

Điều này làm là đầu tiên, tìm đến cuối tập tin; sau đó, báo cáo nơi con trỏ tập tin. Cuối cùng (đây là tùy chọn) nó tua lại từ đầu tập tin. Lưu ý rằngfp nên là một luồng nhị phân.

file_size chứa số byte mà tệp chứa. Lưu ý rằng vì (theo climits.h) loại dài không dấu được giới hạn ở 4294967295 byte (4 gigabyte), bạn sẽ cần tìm một loại biến khác nếu bạn có khả năng xử lý các tệp lớn hơn đó.


3
Điều này khác với câu trả lời của Derek từ 8 năm trước như thế nào?
PP

Đó là hành vi không xác định cho luồng nhị phân và đối với luồng văn bản ftellkhông trả về đại diện giá trị của số byte có thể được đọc từ tệp.
Andrew Henle

0

Tôi có một chức năng chỉ hoạt động tốt với stdio.h. Tôi thích nó rất nhiều và nó hoạt động rất tốt và khá súc tích:

size_t fsize(FILE *File) {
    size_t FSZ;
    fseek(File, 0, 2);
    FSZ = ftell(File);
    rewind(File);
    return FSZ;
}

0

Đây là một chức năng đơn giản và sạch sẽ trả về kích thước tệp.

long get_file_size(char *path)
{
    FILE *fp;
    long size = -1;
    /* Open file for reading */
    fp = fopen(path, "r");
    fseek(fp, 0, SEEK_END);
    size = ftell(fp); 
    fp.close();
    return 
}

1
Bạn không cần phải đóng tập tin?
Jerry Jeremiah

Không, tôi không thích các chức năng mong đợi một con đường. Thay vào đó, vui lòng đặt ti ra một con trỏ tệp
Mr Oscar

-3

Bạn có thể mở tệp, chuyển đến 0 offset tương đối từ dưới cùng của tệp với

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

giá trị được trả về từ fseek là kích thước của tệp.

Tôi đã không viết mã trong C trong một thời gian dài, nhưng tôi nghĩ nó sẽ hoạt động.


12
Bạn không cần phải xác định một cái gì đó như XEMKBOTTOM. #include <stdio.h> fseek (xử lý, 0, XEMK_END);
sigjuice

-4

Nhìn vào câu hỏi, ftellcó thể dễ dàng nhận được số byte.

  long size = ftell(FILENAME);
  printf("total size is %ld bytes",size);

ftellđối số mô tả tệp, không phải là tên tệp, làm đối số.
Barmar

@Barmar, Không ftellkhông mong đợi một mô tả tập tin, nó mong đợi một FILE*thay thế. Xem trang người đàn ông đầu tiên!

Cách tiếp cận hoàn toàn sai, Đó là hằng số ftellsẽ trở lại 0mọi lúc!

Câu trả lời này hoàn toàn sai, vì đối với một, bạn cần sử dụng fseek()đầu tiên để tìm kiếm phần cuối của tệp, và cũng ftell()mong đợi FILE *, không phải là một chuỗi! Bạn sẽ được phục vụ tốt để đưa ra câu trả lời của bạn.
Ông Oscar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.