C में स्ट्रिंग के लिए फ़ाइल की सामग्री कैसे पढ़ें?


96

C में किसी फ़ाइल को खोलने और उसकी सामग्री को स्ट्रिंग (char *, char [], जो भी हो) में पढ़ने के लिए सबसे सरल तरीका (कम से कम त्रुटि-रहित, कम से कम कोड की लाइनें, हालाँकि आप इसकी व्याख्या करना चाहते हैं) क्या है?


8
"सबसे सरल तरीका" और "कम से कम त्रुटि-प्रवण" अक्सर एक दूसरे के विपरीत होते हैं।
एंडी लेस्टर

14
"सरलतम तरीका" और "कम से कम त्रुटि प्रवण" वास्तव में मेरी पुस्तक का पर्याय हैं। उदाहरण के लिए, C # में उत्तर है string s = File.ReadAllText(filename);। यह कैसे सरल और अधिक त्रुटि प्रवण हो सकता है?
मार्क लकाटा

जवाबों:


145

मैं बस पूरे बफर को एक कच्चे मेमोरी चंक के रूप में मेमोरी में लोड करता हूं और पार्सिंग अपने आप करता हूं। इस तरह से कई प्लेटफ़ॉर्मों पर मानक लीबी क्या करती है, इस पर मेरा सबसे अच्छा नियंत्रण है।

यह एक स्टब है जो मैं इसके लिए उपयोग करता हूं। आप fseek, ftell और fread के लिए त्रुटि-कोड की जांच करना चाहते हैं। (स्पष्टता के लिए छोड़ा गया)।

char * buffer = 0;
long length;
FILE * f = fopen (filename, "rb");

if (f)
{
  fseek (f, 0, SEEK_END);
  length = ftell (f);
  fseek (f, 0, SEEK_SET);
  buffer = malloc (length);
  if (buffer)
  {
    fread (buffer, 1, length, f);
  }
  fclose (f);
}

if (buffer)
{
  // start to process your data / extract strings here...
}

3
मैं फ़्रेड के वापसी मूल्य की भी जांच करूंगा, क्योंकि यह वास्तव में त्रुटियों के कारण पूरी फाइल नहीं पढ़ सकता है और क्या नहीं।
freespace

6
जैसे rmeador ने कहा, fseek फाइल्स> 4GB पर फेल हो जाएगा।
KPexEA

6
सच। बड़ी फ़ाइलों के लिए यह समाधान बेकार है।
निल्स पिपेनब्रिनक

31
चूंकि यह एक लैंडिंग पृष्ठ है, इसलिए मैं इंगित करना चाहूंगा कि freadयह आपके स्ट्रिंग को शून्य-समाप्त नहीं करता है। इससे थोड़ी परेशानी हो सकती है।
ivan-k

18
जैसा कि @Manbroski ने कहा, बफर को '0' समाप्त करने की आवश्यकता है। इसलिए मैं buffer = malloc (length + 1);buffer[length] = '\0';
बदलूंगा और fclose के

26

एक और, दुर्भाग्य से अत्यधिक ओएस पर निर्भर, समाधान मेमोरी मैपिंग फ़ाइल है। लाभ में आम तौर पर रीड का प्रदर्शन शामिल होता है, और कम किए गए मेमोरी का उपयोग अनुप्रयोगों के रूप में होता है और ऑपरेटिंग सिस्टम फ़ाइल कैश वास्तव में भौतिक मेमोरी साझा कर सकते हैं।

POSIX कोड इस तरह दिखेगा:

int fd = open("filename", O_RDONLY);
int len = lseek(fd, 0, SEEK_END);
void *data = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);

दूसरी ओर विंडोज थोड़ा अधिक मुश्किल है, और दुर्भाग्य से मेरे पास परीक्षण करने के लिए मेरे सामने संकलक नहीं है, लेकिन कार्यक्षमता CreateFileMapping()और द्वारा प्रदान की जाती है MapViewOfFile()


3
उन सिस्टम कॉल्स से रिटर्न वैल्यू चेक करना न भूलें!
टोबे स्पाइट

3
lseek () कॉल करते समय int के बजाय off_t का उपयोग करना चाहिए।
ivan.ukr

1
ध्यान दें कि यदि लक्ष्य किसी समय में किसी फ़ाइल की सामग्री को मेमोरी में स्थिर रूप से कैप्चर करना है, तो इस समाधान से बचा जाना चाहिए, जब तक कि आप निश्चित नहीं हैं कि मेमोरी में पढ़ी जा रही फ़ाइल को अंतराल के दौरान अन्य प्रक्रियाओं द्वारा संशोधित नहीं किया जाएगा। जिस पर नक्शे का उपयोग किया जाएगा। अधिक जानकारी के लिए इस पोस्ट को देखें।
user001

12

यदि "इसकी सामग्री को एक स्ट्रिंग में पढ़ें" का अर्थ है कि फ़ाइल में कोड 0 के साथ वर्ण नहीं हैं, तो आप getdelim () फ़ंक्शन का भी उपयोग कर सकते हैं, जो या तो मेमोरी के एक ब्लॉक को स्वीकार करता है और यदि आवश्यक हो, तो उसे पुन: आवंटित करता है, या बस पूरे बफर को आवंटित करता है जब तक यह एक निर्दिष्ट सीमांकक या फ़ाइल के अंत का सामना नहीं करता है, तब तक आप उसमें फ़ाइल को पढ़ते हैं। पूरी फ़ाइल पढ़ने के लिए सीमांकक के रूप में बस '\ 0' पास करें।

यह फ़ंक्शन GNU C लाइब्रेरी, http://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getdelim-994 पर उपलब्ध है

नमूना कोड जितना सरल हो सकता है

char* buffer = NULL;
size_t len;
ssize_t bytes_read = getdelim( &buffer, &len, '\0', fp);
if ( bytes_read != -1) {
  /* Success, now the entire file is in the buffer */

1
मैंने पहले भी इसका इस्तेमाल किया है! यह बहुत अच्छी तरह से काम करता है, जिस फ़ाइल को आप पढ़ रहे हैं, वह पाठ है (जिसमें 0 नहीं है)।
१६

अच्छा! संपूर्ण पाठ फ़ाइलों में स्लैपिंग करते समय बहुत सारी समस्याएं आती हैं। अब अगर ईओएफ तक किसी भी परिसीमन वाले चरित्र की आवश्यकता के बिना बाइनरी फ़ाइल स्ट्रीम पढ़ने का एक समान अल्ट्रा सरल तरीका था!
एंथनी

6

यदि फ़ाइल पाठ है, और आप टेक्स्ट लाइन को लाइन से प्राप्त करना चाहते हैं, तो सबसे आसान तरीका है फ़्यूज़ () का उपयोग करना।

char buffer[100];
FILE *fp = fopen("filename", "r");                 // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);

6

यदि आप स्टडिन या पाइप जैसी विशेष फाइलें पढ़ रहे हैं, तो आप फ़ाइल का आकार पहले से प्राप्त करने के लिए फस्टैट का उपयोग करने में सक्षम नहीं होंगे। इसके अलावा, यदि आप एक बाइनरी फ़ाइल पढ़ रहे हैं तो एम्बेडेड '\' वर्णों के कारण स्ट्रिंग आकार की जानकारी खोना है। किसी फ़ाइल को पढ़ने का सबसे अच्छा तरीका है रीड और रियललोक का उपयोग करना:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main () {
    char buf[4096];
    ssize_t n;
    char *str = NULL;
    size_t len = 0;
    while (n = read(STDIN_FILENO, buf, sizeof buf)) {
        if (n < 0) {
            if (errno == EAGAIN)
                continue;
            perror("read");
            break;
        }
        str = realloc(str, len + n + 1);
        memcpy(str + len, buf, n);
        len += n;
        str[len] = '\0';
    }
    printf("%.*s\n", len, str);
    return 0;
}

1
यह O (n ^ 2) है, जहां n आपकी फ़ाइल की लंबाई है। इससे अधिक अपवित्र वाले सभी समाधान O (n) हैं। कृपया इस समाधान का अभ्यास में उपयोग न करें, या गुणात्मक वृद्धि के साथ एक संशोधित संस्करण का उपयोग करें।
क्लार्क गैबेल

2
realloc () मौजूदा मेमोरी को नए आकार में बढ़ा सकते हैं, पुरानी मेमोरी को मेमोरी के नए बड़े टुकड़े में कॉपी किए बिना। केवल अगर मैलोका () को हस्तक्षेप करने के लिए कॉल हैं, तो इसके लिए मेमोरी को चारों ओर ले जाने और इस समाधान को बनाने की आवश्यकता होगी O (n ^ 2)। यहाँ, मॉलॉक () के लिए कोई कॉल नहीं है जो कॉल के बीच में होता है realloc () तो समाधान ठीक होना चाहिए।
जेक

2
आप एक मध्यवर्ती "buf" से कॉपी करने की आवश्यकता के बिना सीधे "str" ​​बफर (एक उपयुक्त ऑफसेट के साथ) में पढ़ सकते हैं। हालाँकि वह तकनीक जो आम तौर पर फ़ाइल सामग्री के लिए आवश्यक मेमोरी को आवंटित करेगी। बाइनरी फ़ाइलों के लिए भी देखें, प्रिंटफ़ उन्हें सही ढंग से नहीं संभालेंगे, और आप शायद वैसे भी बाइनरी प्रिंट नहीं करना चाहते हैं!
एंथनी

3

नोट: यह ऊपर दिए गए स्वीकृत उत्तर का एक संशोधन है।

यहाँ यह करने का एक तरीका है, त्रुटि जाँच के साथ पूरा।

जब फ़ाइल 1 GiB से बड़ी थी, तब मैंने छोड़ने के लिए एक आकार चेकर जोड़ा है। मैंने ऐसा इसलिए किया क्योंकि प्रोग्राम पूरी फाइल को एक स्ट्रिंग में रखता है जो बहुत अधिक रैम का उपयोग कर सकता है और कंप्यूटर को क्रैश कर सकता है। हालाँकि, अगर आपको इस बात की परवाह नहीं है कि आप इसे कोड से हटा सकते हैं।

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

#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TO_LARGE 2
#define FILE_READ_ERROR 3

char * c_read_file(const char * f_name, int * err, size_t * f_size) {
    char * buffer;
    size_t length;
    FILE * f = fopen(f_name, "rb");
    size_t read_length;

    if (f) {
        fseek(f, 0, SEEK_END);
        length = ftell(f);
        fseek(f, 0, SEEK_SET);

        // 1 GiB; best not to load a whole large file in one string
        if (length > 1073741824) {
            *err = FILE_TO_LARGE;

            return NULL;
        }

        buffer = (char *)malloc(length + 1);

        if (length) {
            read_length = fread(buffer, 1, length, f);

            if (length != read_length) {
                 *err = FILE_READ_ERROR;

                 return NULL;
            }
        }

        fclose(f);

        *err = FILE_OK;
        buffer[length] = '\0';
        *f_size = length;
    }
    else {
        *err = FILE_NOT_EXIST;

        return NULL;
    }

    return buffer;
}

और त्रुटियों की जाँच करने के लिए:

int err;
size_t f_size;
char * f_data;

f_data = c_read_file("test.txt", &err, &f_size);

if (err) {
    // process error
}

2

यदि आप उपयोग कर रहे हैं glib, तो आप g_file_get_contents का उपयोग कर सकते हैं ;

gchar *contents;
GError *err = NULL;

g_file_get_contents ("foo.txt", &contents, NULL, &err);
g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL));
if (err != NULL)
  {
    // Report error to user, and free error
    g_assert (contents == NULL);
    fprintf (stderr, "Unable to read file: %s\n", err->message);
    g_error_free (err);
  }
else
  {
    // Use file contents
    g_assert (contents != NULL);
  }
}

1
// Assumes the file exists and will seg. fault otherwise.
const GLchar *load_shader_source(char *filename) {
  FILE *file = fopen(filename, "r");             // open 
  fseek(file, 0L, SEEK_END);                     // find the end
  size_t size = ftell(file);                     // get the size in bytes
  GLchar *shaderSource = calloc(1, size);        // allocate enough bytes
  rewind(file);                                  // go back to file beginning
  fread(shaderSource, size, sizeof(char), file); // read each char into ourblock
  fclose(file);                                  // close the stream
  return shaderSource;
}

यह एक बहुत ही क्रूड समाधान है क्योंकि अशक्त के खिलाफ कुछ भी नहीं जांचा जाता है।


यह केवल डिस्क आधारित फाइलों के साथ होगा। यह नामित पाइप, मानक इनपुट या नेटवर्क स्ट्रीम के लिए विफल हो जाएगा।
एंथनी

हा, मैं भी यहाँ क्यों आया! लेकिन मुझे लगता है कि आपको या तो स्ट्रिंग को समाप्त करने की आवश्यकता है, या लंबाई को वापस glShaderSourceले लें जो वैकल्पिक रूप से लेता है।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

1

बस ऊपर दिए गए स्वीकृत उत्तर से संशोधित किया गया है।

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

char *readFile(char *filename) {
    FILE *f = fopen(filename, "rt");
    assert(f);
    fseek(f, 0, SEEK_END);
    long length = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buffer = (char *) malloc(length + 1);
    buffer[length] = '\0';
    fread(buffer, 1, length, f);
    fclose(f);
    return buffer;
}

int main() {
    char *content = readFile("../hello.txt");
    printf("%s", content);
}

यह कोई C कोड नहीं है। प्रश्न को C ++ के रूप में टैग नहीं किया गया है।
गेरहार्ड

@Gerhardh नौ साल पहले सवाल का इतनी तेज़ प्रतिक्रिया जब मैं संपादन कर रहा हूँ! यद्यपि फ़ंक्शन भाग शुद्ध सी है, मुझे अपनी इच्छा-नहीं-पर-सी-जवाब के लिए खेद है।
बैजिफ़ेइलोंग

यह प्राचीन प्रश्न सक्रिय प्रश्नों के शीर्ष पर सूचीबद्ध था। मैंने इसकी खोज नहीं की।
गेरहार्ड

यह कोड मेमोरी को लीक कर देता है, अपनी
मैलोडोकाइड

0

मैं अपने स्वयं के संस्करण को जोड़ दूंगा, यहां जवाबों के आधार पर, केवल संदर्भ के लिए। मेरा कोड आकार (चार) को ध्यान में रखता है और इसमें कुछ टिप्पणियां जोड़ता है।

// Open the file in read mode.
FILE *file = fopen(file_name, "r");
// Check if there was an error.
if (file == NULL) {
    fprintf(stderr, "Error: Can't open file '%s'.", file_name);
    exit(EXIT_FAILURE);
}
// Get the file length
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
// Create the string for the file contents.
char *buffer = malloc(sizeof(char) * (length + 1));
buffer[length] = '\0';
// Set the contents of the string.
fread(buffer, sizeof(char), length, file);
// Close the file.
fclose(file);
// Do something with the data.
// ...
// Free the allocated string space.
free(buffer);

0

आसान और साफ सुथरा (फ़ाइल में सामग्री 10000 से कम है):

void read_whole_file(char fileName[1000], char buffer[10000])
{
    FILE * file = fopen(fileName, "r");
    if(file == NULL)
    {
        puts("File not found");
        exit(1);
    }
    char  c;
    int idx=0;
    while (fscanf(file , "%c" ,&c) == 1)
    {
        buffer[idx] = c;
        idx++;
    }
    buffer[idx] = 0;
}

कृपया उन सभी मेमोरी को आवंटित न करें जिन्हें आपको लगता है कि आपको अपफ्रंट की आवश्यकता होगी। यह खराब डिजाइन का एक आदर्श उदाहरण है। जब भी ऐसा करना संभव हो, आपको मेमोरी को आप के रूप में आवंटित करना चाहिए। यह अच्छा डिज़ाइन होगा यदि आप अपेक्षा करते हैं कि फ़ाइल 10,000 बाइट्स लंबी होगी, तो आपका प्रोग्राम ऐसी फ़ाइल को संभाल नहीं सकता है जो किसी अन्य आकार की हो, और आप आकार की जाँच कर रहे हों और वैसे भी अनियमित कर रहे हों, लेकिन यह यहाँ नहीं चल रहा है। आपको वास्तव में सीखना चाहिए कि सी को सही तरीके से कैसे कोड करें।
जैक गिफिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.