সি এর স্ট্রিং এ কোনও ফাইলের সামগ্রী কীভাবে পড়বেন?


97

সিতে কোনও ফাইল খুলতে এবং এর বিষয়বস্তুগুলিকে স্ট্রিংয়ে পড়তে (চর *, চর [], যাই হোক না কেন) সবচেয়ে সহজ উপায় (কমপক্ষে ত্রুটি-প্রবণ, কোডের ন্যূনতম লাইনগুলি, তবে আপনি এটি ব্যাখ্যা করতে চান) কী?


9
"সহজ উপায়" এবং "সর্বনিম্ন ত্রুটি-প্রবণ" প্রায়শই একে অপরের বিরোধী।
অ্যান্ডি লেস্টার 14

15
"সহজ উপায়" এবং "সর্বনিম্ন ত্রুটিযুক্ত প্রবণ" আসলে আমার বইয়ের সমার্থক। উদাহরণস্বরূপ, সি # তে উত্তরটি string s = File.ReadAllText(filename);। কীভাবে এটি সহজ এবং আরও ত্রুটিযুক্ত প্রবণ হতে পারে?
লাকাটা

উত্তর:


146

আমি কেবল কাঁচা স্মৃতি মেমরির অংশ হিসাবে পুরো বাফারটি লোড করি এবং নিজেই বিশ্লেষণ করি। একাধিক প্ল্যাটফর্মে স্ট্যান্ডার্ড লিব কী করে তার উপর আমার সর্বোত্তম নিয়ন্ত্রণ রয়েছে।

এটি আমি এটির জন্য ব্যবহার করছি ub আপনি 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...
}

4
আমি ফ্রেডের রিটার্ন মানটিও যাচাই করতাম, যেহেতু এটি ত্রুটির কারণে পুরো ফাইলটি না পড়তে পারে এবং না কী।
ফ্রিস্পেস

6
যেমনটি রিমেডোর বলেছিলেন, fseek ফাইলগুলিতে> 4 জিবি ব্যর্থ হবে।
কেপিএক্সইএ

6
সত্য। বড় ফাইলগুলির জন্য এই দ্রবণটি সফল হয়।
নীলস পাইপেনব্রিংক

33
যেহেতু এটি একটি অবতরণ পৃষ্ঠা, তাই আমি এটি উল্লেখ করতে চাই যে freadএটি আপনার স্ট্রিংকে শূন্য করে না। এর ফলে কিছুটা ঝামেলা হতে পারে।
আইভান-কে

19
@ মনব্রোস্কি যেমন বলেছিলেন, বাফারকে '\ 0' সমাপ্ত করা দরকার। তাই আমি পরিবর্তন হবে buffer = malloc (length + 1);এবং fclose সাথে পরে যোগ করুন: buffer[length] = '\0';(Valgrind দ্বারা যাচাই)
soywod

26

অন্যটি, দুর্ভাগ্যক্রমে উচ্চ ওএস-নির্ভর, সমাধান হ'ল মেমরি ম্যাপিং। সুবিধার মধ্যে সাধারণত পড়ার পারফরম্যান্স অন্তর্ভুক্ত থাকে এবং অ্যাপ্লিকেশন ভিউ এবং অপারেটিং সিস্টেমগুলির ফাইল ফাইল ক্যাশে প্রকৃতপক্ষে শারীরিক স্মৃতি ভাগ করতে পারে বলে মেমরির ব্যবহার হ্রাস পায়।

পসিক্স কোডটি এর মতো দেখায়:

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()


4
সেই সিস্টেম কলগুলি থেকে ফিরতি মানগুলি পরীক্ষা করতে ভুলবেন না!
টবির স্পিড

4
lseek () কল করার সময় অবশ্যই INT এর পরিবর্তে অফ_টি ব্যবহার করতে হবে।
ivan.ukr

4
মনে রাখবেন যে লক্ষ্যটি স্থিরভাবে কোনও নির্দিষ্ট মুহুর্তে কোনও ফাইলের বিষয়বস্তু মেমোরিতে ধারণ করতে হয়, তবে এই সমাধানটি এড়ানো উচিত, যদি না আপনি নিশ্চিত হন যে ফাইলটি মেমরির মধ্যে পড়ছে অন্তরালে অন্যান্য প্রক্রিয়া দ্বারা পরিবর্তন করা হবে না যার উপর দিয়ে মানচিত্রটি ব্যবহার করা হবে। আরও তথ্যের জন্য এই পোস্টটি দেখুন ।
ব্যবহারকারী001

13

যদি "এর বিষয়বস্তুগুলিকে স্ট্রিংয়ে পড়ুন" এর অর্থ হল যে ফাইলটিতে কোড 0 সহ অক্ষর নেই, আপনি গেটডেলিম () ফাংশনটিও ব্যবহার করতে পারেন, যা হয় মেমরির একটি ব্লক গ্রহণ করে এবং প্রয়োজনে এটি পুনরায় স্থানান্তর করে, বা কেবলমাত্র পুরো বাফারটির জন্য বরাদ্দ করে আপনি, এবং এটিতে ফাইলটি পড়েন যতক্ষণ না এটি নির্দিষ্ট সীমাবদ্ধ বা ফাইলটির শেষের মুখোমুখি হয়। পুরো ফাইলটি পড়ার জন্য সীমাবদ্ধ হিসাবে কেবল '\ 0' পাস করুন।

এই ফাংশনটি জিএনইউ সি লাইব্রেরিতে, 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 */

4
আমি এটি আগে ব্যবহার করেছি! এটি খুব সুন্দরভাবে কাজ করে, ধরে নিচ্ছেন যে আপনি যে ফাইলটি পড়ছেন সেটি পাঠ্য (এতে \ 0 থাকে না)।
পূর্বসূরীর

সুন্দর! পুরো পাঠ্য ফাইলগুলিতে স্লুরপ করার সময় প্রচুর সমস্যা বাঁচায়। এখন যদি কোনও সীমানার চরিত্রের প্রয়োজন ছাড়াই ইওএফ না হওয়া পর্যন্ত বাইনারি ফাইল স্ট্রিম পড়ার অনুরূপ অতি সাধারণ উপায় ছিল!
আনথনি

6

ফাইলটি যদি পাঠ্য হয় এবং আপনি পাঠ্য লাইনটি লাইন পেতে চান তবে সবচেয়ে সহজ উপায় হ'ল fgets () ব্যবহার করা।

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

6

আপনি যদি স্টিডিন বা পাইপের মতো বিশেষ ফাইলগুলি পড়ছেন তবে আপনি ফাইলের আকার আগেই পেতে fstat ব্যবহার করতে সক্ষম হবেন না। এছাড়াও, আপনি যদি বাইনারি ফাইল পড়ে থাকেন তবে এম্বেডড '\ 0' অক্ষরের কারণে স্ট্রিং আকারের তথ্য হারাতে চলেছে। তারপরে কোনও ফাইল পড়ার সর্বোত্তম উপায় হ'ল পঠন এবং পুনর্নির্মাণ ব্যবহার:

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

4
এটি ও (এন ^ 2), যেখানে এন আপনার ফাইলের দৈর্ঘ্য। এর চেয়ে বেশি উন্নতমানের সমস্ত সমাধান হ'ল হে (এন)। অনুগ্রহ করে এই সমাধানটি ব্যবহার করবেন না বা গুণিত বৃদ্ধি সহ একটি পরিবর্তিত সংস্করণ ব্যবহার করবেন না।
ক্লার্ক গায়েবেল

4
রিলোক () পুরানো মেমরিটিকে কোনও নতুন বড় মেমরির অনুলিপি না করে বিদ্যমান মেমরিটিকে নতুন আকারে প্রসারিত করতে পারে। malloc () এ যদি কেবল ইন্টারেঞ্জিং কল আসে তবে কেবল তার কি মেমোরিটিকে চারপাশে স্থানান্তরিত করতে হবে এবং এই সমাধানটিকে ও (এন ^ 2) করা প্রয়োজন। এখানে, malloc () তে কল নেই যে রিলোক () এর কলগুলির মধ্যে ঘটে তাই সমাধানটি ভাল হওয়া উচিত।
জেক

4
মধ্যবর্তী "বুফ" থেকে অনুলিপি না করেই আপনি সরাসরি "স্ট" বাফারে (উপযুক্ত অফসেট সহ) পড়তে পারেন। যে কৌশলটি সাধারণত ফাইল সামগ্রীগুলির জন্য প্রয়োজনীয় মেমরির অতিরিক্ত বরাদ্দ করবে। বাইনারি ফাইলগুলির জন্যও নজর রাখুন, প্রিন্টফগুলি সেগুলি সঠিকভাবে পরিচালনা করবে না এবং আপনি সম্ভবত বাইনারি প্রিন্ট করতে চান না!
এন্থনি

4

দ্রষ্টব্য: এটি উপরে গৃহীত উত্তরের একটি পরিবর্তন।

এটি করার একটি উপায় এখানে, ত্রুটি পরীক্ষা করে সম্পূর্ণ complete

ফাইলটি যখন 1 জিআইবির চেয়ে বড় ছিল তখন আমি প্রস্থান করতে একটি আকারের পরীক্ষক যুক্ত করেছি। আমি এটি করেছি কারণ প্রোগ্রামটি পুরো ফাইলটিকে একটি স্ট্রিংয়ে রাখে যা খুব বেশি র‌্যাম ব্যবহার করে এবং কম্পিউটার ক্র্যাশ করতে পারে। তবে, যদি আপনি এটির যত্ন না পান তবে আপনি কেবল কোড থেকে এটিকে সরাতে পারেন।

#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) {
                 free(buffer);
                 *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
}
else {
    // process data
    free(f_data);
}

4
কেবল একটি প্রশ্ন: bufferআপনি যে বরাদ্দ দিয়েছিলেন malloc(length +1)তা মুক্তি দেওয়া হচ্ছে না। এই পদ্ধতির ভোক্তা কি এমন কিছু করতে পারে বা free()বরাদ্দ মেমরির কোনও প্রয়োজন নেই ?
পাব্লস্প্রোজেক্ট

যদি কোনও ত্রুটি না ঘটে থাকে, বিনামূল্যে (f_data); বলা উচিত। এটি নির্দেশ করার জন্য ধন্যবাদ
জো কুল

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

2

উপরের স্বীকৃত উত্তর থেকে সবেমাত্র পরিবর্তিত।

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

এটি কোনও সি কোড নয়। প্রশ্নটি সি ++ হিসাবে ট্যাগ করা হয়নি।
গেরার্ধ

@ গেরহর্দ্ধ নয় বছর আগে আমি যখন সম্পাদনা করছি তখন প্রশ্নের এত দ্রুত প্রতিক্রিয়া! যদিও ফাংশনটির অংশটি খাঁটি সি, তবুও আমি আমার উইল-অন-সি-র উত্তরটির জন্য দুঃখিত।
BaiJiFeiLong

এই প্রাচীন প্রশ্নটি সক্রিয় প্রশ্নের শীর্ষে তালিকাভুক্ত করা হয়েছিল। আমি এটি অনুসন্ধান করিনি।
গেরহর্দ্ধ

4
এই কোডটি মেমরি ফাঁস করে, আপনার
ম্যালোকড

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ঐচ্ছিকরূপে লাগে।
সিরো সান্তিলি :5 冠状 病 六四 事件 法轮功

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

আপনি মনে করেন যে সমস্ত স্মৃতি আপনার সামনে দরকার হবে দয়া করে বরাদ্দ করবেন না । এটি খারাপ ডিজাইনের একটি নিখুঁত উদাহরণ। যখনই এটি করা সম্ভব তখন আপনার মেমরিটি বরাদ্দ করা উচিত। আপনি যদি ফাইলটি ১০,০০০ বাইট লম্বা করে আশা করেন তবে এটি আপনার ডিজাইনের পক্ষে ভাল হবে, আপনার প্রোগ্রামটি অন্য কোনও আকারের কোনও ফাইল হ্যান্ডেল করতে পারে না এবং আপনি আকারটি পরীক্ষা করছেন এবং যাইহোক ত্রুটিযুক্ত হয়ে যাচ্ছেন, তবে এটি এখানে চলছে না। আপনার সঠিকভাবে সি কোড করার পদ্ধতিটি শিখানো উচিত।
জ্যাক গিফিন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.