คุณจะกำหนดขนาดของไฟล์ใน C ได้อย่างไร?


137

ฉันจะหาขนาดของไฟล์เป็นไบต์ได้อย่างไร

#include <stdio.h>

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

คุณจะต้องใช้ฟังก์ชันไลบรารีเพื่อดึงรายละเอียดของไฟล์ เนื่องจาก C เป็นแพลตฟอร์มอิสระอย่างสมบูรณ์คุณจะต้องแจ้งให้เราทราบว่าแพลตฟอร์ม / ระบบปฏิบัติการใดที่คุณกำลังพัฒนา!
Chris Roberts

ทำไมchar* fileทำไมไม่FILE* file? -1
Mr Oscar

-1 เพราะฟังก์ชั่นไฟล์ควรยอมรับไฟล์ descriptor ไม่ใช่พา ธ ไฟล์
Mr Oscar

คำตอบ:


144

ขึ้นอยู่กับรหัสของ 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; 
}

การเปลี่ยนแปลง:

  • const charทำชื่อไฟล์อาร์กิวเมนต์
  • แก้ไขstruct statคำจำกัดความซึ่งไม่มีชื่อตัวแปร
  • ส่งคืน-1ข้อผิดพลาดแทน0ซึ่งจะคลุมเครือสำหรับไฟล์ว่าง off_tเป็นประเภทที่เซ็นชื่อดังนั้นจึงเป็นไปได้

หากคุณต้องการfsize()พิมพ์ข้อความผิดพลาดคุณสามารถใช้สิ่งนี้:

#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;
}

ในระบบ 32 บิตคุณควรรวบรวมสิ่งนี้ด้วยตัวเลือก-D_FILE_OFFSET_BITS=64มิฉะนั้นoff_tจะเก็บค่าได้สูงสุด 2 GB ดูส่วน "การใช้ LFS" ของการสนับสนุนไฟล์ขนาดใหญ่ใน Linuxสำหรับรายละเอียด


19
นี่เป็น Linux / Unix ที่เฉพาะเจาะจง - อาจคุ้มค่าที่ชี้ให้เห็นเนื่องจากคำถามไม่ได้ระบุระบบปฏิบัติการ
Drew Hall

1
คุณอาจเปลี่ยนประเภทการส่งคืนเป็น ssize_t และแปลงขนาดจาก off_t ได้โดยไม่มีปัญหา มันจะดูเหมือนจะทำให้ความรู้สึกมากขึ้นในการใช้ ssize_t :-) (ไม่ต้องสับสนกับ size_t ซึ่งเป็นได้รับการรับรองและไม่สามารถนำมาใช้เพื่อระบุข้อผิดพลาด.)
เท็ด Percival

1
สำหรับรหัสพกพาเพิ่มเติมให้ใช้fseek+ ftellตามที่ Derek เสนอ
Ciro Santilli 法轮功冠状病六四事件法轮功

9
สำหรับรหัสพกพาเพิ่มเติมให้ใช้fseek+ ftellตามที่ Derek เสนอ ไม่C มาตรฐานระบุว่าการfseek()ไปSEEK_ENDที่ไฟล์ไบนารีนั้นเป็นพฤติกรรมที่ไม่ได้กำหนด 7.19.9.2 fseekฟังก์ชั่น ... สตรีมแบบไบนารีไม่จำเป็นต้องรองรับการfseekโทรที่มีความหมายซึ่งมีค่าเท่ากับSEEK_ENDและตามที่ระบุไว้ด้านล่างซึ่งมาจากเชิงอรรถ 234 ในหน้า 267 ของการเชื่อมโยง C มาตรฐานและที่เฉพาะป้ายfseekไปSEEK_ENDในกระแสไบนารีเป็นพฤติกรรมที่ไม่ได้กำหนด .
Andrew Henle

74

intอย่าใช้ ไฟล์ที่มีขนาดเกิน 2 กิกะไบต์นั้นถือได้ว่าเป็นเรื่องสกปรกในทุกวันนี้

unsigned intอย่าใช้ ไฟล์ที่มีขนาดเกิน 4 กิกะไบต์นั้นเป็นเรื่องธรรมดาเนื่องจากฝุ่นสกปรกเล็กน้อยทั่วไป

IIRC ไลบรารีมาตรฐานกำหนดoff_tเป็นจำนวนเต็ม 64 บิตที่ไม่ได้ลงชื่อซึ่งเป็นสิ่งที่ทุกคนควรใช้ เราสามารถกำหนดใหม่ให้เป็น 128 บิตในไม่กี่ปีเมื่อเราเริ่มมี 16 exabyte ไฟล์ที่แขวนอยู่รอบ ๆ

หากคุณใช้ Windows คุณควรใช้GetFileSizeExซึ่งใช้จำนวนเต็ม 64 บิตที่ได้รับการเซ็นชื่อดังนั้นพวกเขาจะเริ่มตีปัญหาด้วยไฟล์ exabyte 8 ไฟล์ โง่เขลา Microsoft! :-)


1
ฉันใช้คอมไพเลอร์โดยที่ off_t คือ 32 บิต จริงอยู่ที่นี่เป็นระบบฝังตัวที่มีไฟล์ 4GB อยู่น้อย อย่างไรก็ตาม POSIX ยังกำหนด off64_t และวิธีการที่สอดคล้องกันเพื่อเพิ่มความสับสน
Aaron Campbell

ฉันรักคำตอบที่ถือว่า Windows และไม่ทำอะไรเลยนอกจากวิจารณ์คำถาม คุณช่วยเพิ่มสิ่งที่สอดคล้องกับ POSIX ได้ไหม
SS Anne

1
@ JL2210 คำตอบที่ยอมรับจาก Ted Percival แสดงให้เห็นถึงวิธีการแก้ปัญหาที่เป็นไปตาม posix ดังนั้นฉันไม่เห็นความรู้สึกในการทำซ้ำอย่างชัดเจน ฉัน (และคนอื่น ๆ 70 คน) คิดว่าการเพิ่มบันทึกย่อเกี่ยวกับ windows และไม่ใช้เลขจำนวนเต็ม 32 บิตที่ลงชื่อเพื่อแสดงขนาดไฟล์เป็นมูลค่าเพิ่มที่อยู่ด้านบน ไชโย
โอไรออนเอ็ดเวิร์ด

30

วิธีการแก้ปัญหาของ Matt ควรทำงานยกเว้นว่าเป็น C ++ แทน C และการบอกเริ่มต้นไม่ควรจำเป็น

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;
}

แก้ไขรั้งสำหรับคุณเช่นกัน ;)

อัปเดต: นี่ไม่ใช่ทางออกที่ดีที่สุดจริงๆ มัน จำกัด ให้ไฟล์ 4GB บน Windows และเป็นไปได้ช้ากว่าเพียงแค่ใช้โทรแพลตฟอร์มที่เฉพาะเจาะจงเช่นหรือGetFileSizeExstat64


ใช่คุณควรจะ. อย่างไรก็ตามเว้นแต่มีเหตุผลที่น่าสนใจอย่างแท้จริงไม่ได้เขียนเฉพาะแพลตฟอร์ม แต่คุณควรใช้การโทรเฉพาะแพลตฟอร์มแทนรูปแบบ open / seek-end / tell / close
Derek Park

1
ขออภัยเกี่ยวกับการตอบกลับล่าช้า แต่ฉันมีปัญหาสำคัญที่นี่ มันทำให้แอปหยุดเมื่อเข้าถึงไฟล์ที่ถูก จำกัด (เช่นการป้องกันด้วยรหัสผ่านหรือไฟล์ระบบ) มีวิธีขอรหัสผ่านจากผู้ใช้เมื่อจำเป็นหรือไม่?
Justin

@Justin คุณควรเปิดคำถามใหม่โดยเฉพาะเกี่ยวกับปัญหาที่คุณพบและให้รายละเอียดเกี่ยวกับแพลตฟอร์มที่คุณใช้งานวิธีการเข้าถึงไฟล์และพฤติกรรมของมัน
Derek Park

1
ทั้ง C99 และ C11 ผลตอบแทนจาก long int การร่ายไม่ได้ปรับปรุงช่วงตามที่ จำกัด ไว้โดยฟังก์ชั่น ส่งคืน -1 เมื่อเกิดข้อผิดพลาดและทำให้สับสนกับการร่าย ขอแนะนำให้ส่งกลับชนิดเดียวกับ ftell()(unsigned long)ftell()fsize()ftell()
chux - Reinstate Monica

ฉันเห็นด้วย. นักแสดงก็เพื่อให้ตรงกับต้นแบบดั้งเดิมในคำถาม ฉันจำไม่ได้ว่าทำไมฉันถึงเปลี่ยนให้กลายเป็นชื่อที่ไม่ได้ลงชื่อแทนการใช้ int ที่ไม่ได้ลงชื่อ
Derek Park

15

** อย่าทำอย่างนี้ ( เพราะอะไร ):

การอ้างอิงเอกสารมาตรฐาน C99 ที่ฉันพบทางออนไลน์: "การตั้งค่าตัวบ่งชี้ตำแหน่งไฟล์เป็นจุดสิ้นสุดไฟล์เช่นเดียวกับfseek(file, 0, SEEK_END)พฤติกรรมที่ไม่ได้กำหนดสำหรับสตรีมไบนารี่ (เนื่องจากอักขระ null ที่เป็นไปได้ต่อท้าย) หรือสตรีมใด ๆ ที่ไม่สิ้นสุดอย่างมั่นใจในสถานะการเปลี่ยนครั้งแรก **

เปลี่ยนนิยามเป็น int เพื่อให้สามารถส่งข้อความแสดงข้อผิดพลาดจากนั้นใช้fseek()และftell()เพื่อกำหนดขนาดไฟล์

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: รายงาน CERT นั้นผิด fseekoและftello(หรือfseekและftellหากคุณติดค้างอยู่โดยไม่มีข้อ จำกัด ในอดีตและมีความสุขกับการ จำกัด ขนาดไฟล์ที่คุณสามารถทำงานได้) เป็นวิธีที่ถูกต้องในการกำหนดความยาวของไฟล์ statโซลูชันที่ใช้งานไม่ได้กับ "ไฟล์" จำนวนมาก (เช่นอุปกรณ์บล็อก) และไม่สามารถพกพาไปยังระบบที่ไม่ใช่ POSIX-ish
. GitHub หยุดช่วยน้ำแข็ง

1
นี่เป็นวิธีเดียวที่จะได้ขนาดไฟล์ในระบบที่ไม่เป็นไปตามมาตรฐาน posix (เช่น mbed ที่เรียบง่ายของฉัน)
Earlz

9

POSIX

POSIXมาตรฐานมีวิธีการของตัวเองเพื่อให้ได้ขนาดไฟล์
รวมsys/stat.hส่วนหัวเพื่อใช้ฟังก์ชั่น

สรุป

  • stat(3)รับสถิติไฟล์โดยใช้
  • รับst_sizeทรัพย์สิน

ตัวอย่าง

หมายเหตุ : มัน จำกัด ขนาด4GBไว้ที่ หากไม่ใช่Fat32ระบบไฟล์ให้ใช้เวอร์ชัน 64 บิต!

#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 (มาตรฐาน)

ANSI Cไม่ได้โดยตรงให้วิธีการที่จะกำหนดความยาวของไฟล์
เราจะต้องใช้ความคิดของเรา สำหรับตอนนี้เราจะใช้วิธีการค้นหา!

สรุป

  • fseek(3)ขอไฟล์ที่จะสิ้นสุดการใช้
  • ftell(3)ได้รับตำแหน่งปัจจุบันใช้

ตัวอย่าง

#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);
}

หากไฟล์เป็นstdinหรือไปป์ POSIX, ANSI Cจะไม่ทำงาน
มันจะไปกลับมาหากไฟล์ที่เป็นท่อหรือ0stdin

ความคิดเห็น : คุณควรใช้มาตรฐานPOSIXแทน เพราะมันรองรับ 64 บิต


1
struct _stat64และ__stat64()สำหรับ _Windows
Bob Stein

5

และถ้าคุณกำลังสร้างแอพ Windows ให้ใช้GetFileSizeEx API เนื่องจากไฟล์ I / O ของ CRT นั้นยุ่งโดยเฉพาะอย่างยิ่งในการกำหนดความยาวของไฟล์เนื่องจากลักษณะเฉพาะในการนำเสนอไฟล์บนระบบต่าง ๆ ;)


5

หากคุณพอใจกับการใช้ไลบรารี 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
นั่นไม่ใช่มาตรฐาน C. มันเป็นส่วนหนึ่งของมาตรฐาน POSIX แต่ไม่ใช่มาตรฐาน C
Derek Park

3

การค้นหาอย่างรวดเร็วใน Google พบวิธีการใช้ fseek และ ftellและเธรดกับคำถามนี้พร้อมคำตอบที่ไม่สามารถทำได้ด้วย C ในอีกวิธีหนึ่ง

คุณสามารถใช้ไลบรารี่แบบพกพาเช่นNSPR ( ไลบรารี่ที่ให้พลัง Firefox) หรือตรวจสอบการนำไปใช้ (ค่อนข้างลำบาก)


1

ฉันใช้รหัสชุดนี้เพื่อหาความยาวของไฟล์

//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

ลองนี้ -

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

สิ่งนี้ทำก่อนอื่นให้หาจุดสิ้นสุดของไฟล์ จากนั้นรายงานว่าตัวชี้ไฟล์อยู่ที่ใด สุดท้าย (เป็นทางเลือก) มันจะย้อนกลับไปที่จุดเริ่มต้นของไฟล์ โปรดทราบว่าfpควรเป็นสตรีมไบนารี

file_size มีจำนวนไบต์ที่ไฟล์มี โปรดทราบว่าเนื่องจาก (ตาม climits.h) ประเภทยาวที่ไม่ได้ลงชื่อจะถูก จำกัด ที่ 4294967295 ไบต์ (4 กิกะไบต์) คุณจะต้องค้นหาตัวแปรชนิดอื่นหากคุณมีแนวโน้มที่จะจัดการกับไฟล์ที่มีขนาดใหญ่กว่านั้น


3
สิ่งนี้แตกต่างจากคำตอบของดีเร็กเมื่อ 8 ปีที่แล้วหรือไม่?
PP

นั่นเป็นพฤติกรรมที่ไม่ได้กำหนดสำหรับสตรีมไบนารี่และสำหรับสตรีมข้อความftellจะไม่ส่งคืนตัวแทนค่าของจำนวนไบต์ที่สามารถอ่านได้จากไฟล์
Andrew Henle

0

stdio.hฉันมีฟังก์ชั่นที่ทำงานได้ดีมีเพียง ฉันชอบมันมากและใช้งานได้ดีและค่อนข้างกระชับ:

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

0

นี่คือฟังก์ชั่นที่เรียบง่ายและสะอาดที่จะคืนค่าขนาดไฟล์

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
คุณไม่จำเป็นต้องปิดไฟล์หรือ
Jerry Jeremiah

ไม่ฉันไม่ชอบฟังก์ชันที่คาดหวังเส้นทาง แต่ให้ทำเช่นนี้แทนตัวชี้ไฟล์แทน
นายออสการ์

-3

คุณสามารถเปิดไฟล์ไปที่ 0 offset เทียบกับด้านล่างของไฟล์ด้วย

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

ค่าที่ส่งคืนจาก fseek คือขนาดของไฟล์

ฉันไม่ได้ใช้รหัสใน C เป็นเวลานาน แต่ฉันคิดว่ามันควรจะใช้ได้


12
คุณไม่ควรจะต้องนิยาม SEEKBOTTOM #include <stdio.h> fseek (จัดการ, 0, SEEK_END);
sigjuice

-4

เมื่อดูที่คำถามftellสามารถรับจำนวนไบต์ได้อย่างง่ายดาย

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

ftellคาดว่า file descriptor ไม่ใช่ชื่อไฟล์เป็นอาร์กิวเมนต์
Barmar

@Barmar, ftellไม่ไม่คาดว่าจะเป็นตัวให้คำอธิบายไฟล์ แต่คาดว่าจะเป็นFILE*แทน ดูหน้าคนแรก!

วิธีการนั้นผิดอย่างสมบูรณ์มันคงที่ที่ftellจะกลับมา0ทุกครั้ง!

คำตอบนี้แบนออกผิดสำหรับคุณต้องใช้fseek()ก่อนเพื่อค้นหาจุดสิ้นสุดของไฟล์และยังftell()คาดว่า a FILE *ไม่ใช่สายอักขระ คุณจะได้รับการตอบรับอย่างดี
นายออสการ์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.