อะไรคือวิธีที่ง่ายที่สุด (มีข้อผิดพลาดน้อยที่สุดโค้ดน้อยที่สุด แต่คุณต้องการตีความ) เพื่อเปิดไฟล์ใน C และอ่านเนื้อหาในสตริง (char *, char [], อะไรก็ได้)?
string s = File.ReadAllText(filename);
คือ มันจะง่ายกว่าและเกิดข้อผิดพลาดได้อย่างไร?
อะไรคือวิธีที่ง่ายที่สุด (มีข้อผิดพลาดน้อยที่สุดโค้ดน้อยที่สุด แต่คุณต้องการตีความ) เพื่อเปิดไฟล์ใน C และอ่านเนื้อหาในสตริง (char *, char [], อะไรก็ได้)?
string s = File.ReadAllText(filename);
คือ มันจะง่ายกว่าและเกิดข้อผิดพลาดได้อย่างไร?
คำตอบ:
ฉันมักจะโหลดบัฟเฟอร์ทั้งหมดเป็นหน่วยความจำดิบลงในหน่วยความจำและทำการแยกวิเคราะห์ด้วยตัวเอง ด้วยวิธีนี้ฉันสามารถควบคุมสิ่งที่ lib มาตรฐานทำบนหลายแพลตฟอร์มได้ดีที่สุด
นี่คือต้นขั้วที่ฉันใช้สำหรับสิ่งนี้ คุณอาจต้องการตรวจสอบรหัสข้อผิดพลาดสำหรับ 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...
}
fread
ไม่มีการยุติสตริงของคุณเป็นศูนย์ ซึ่งอาจนำไปสู่ปัญหาบางอย่าง
buffer = malloc (length + 1);
และเพิ่มหลังจาก fclose: buffer[length] = '\0';
(ตรวจสอบโดย Valgrind)
อีกวิธีหนึ่งที่น่าเสียดายที่ขึ้นอยู่กับระบบปฏิบัติการอย่างมากคือการแมปไฟล์หน่วยความจำ ประโยชน์โดยทั่วไปรวมถึงประสิทธิภาพของการอ่านและการใช้หน่วยความจำที่ลดลงเนื่องจากมุมมองแอปพลิเคชันและแคชไฟล์ของระบบปฏิบัติการสามารถแชร์หน่วยความจำกายภาพได้
รหัส 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);
ในทางกลับกัน Windows นั้นยุ่งยากกว่าเล็กน้อยและน่าเสียดายที่ฉันไม่มีคอมไพเลอร์อยู่ตรงหน้าฉันเพื่อทดสอบ แต่ฟังก์ชันนี้มีให้โดยCreateFileMapping()
และMapViewOfFile()
.
หาก "อ่านเนื้อหาเป็นสตริง" หมายความว่าไฟล์ไม่มีอักขระที่มีรหัส 0 คุณยังสามารถใช้ฟังก์ชัน getdelim () ซึ่งยอมรับบล็อกหน่วยความจำและจัดสรรใหม่หากจำเป็นหรือเพียงแค่จัดสรรบัฟเฟอร์ทั้งหมดสำหรับ คุณและอ่านไฟล์เข้าไปจนกว่าจะพบตัวคั่นที่ระบุหรือจุดสิ้นสุดของไฟล์ เพียงแค่ส่ง '\ 0' เป็นตัวคั่นเพื่ออ่านไฟล์ทั้งหมด
ฟังก์ชันนี้มีอยู่ใน GNU C Library 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 */
หากไฟล์เป็นข้อความและคุณต้องการรับข้อความทีละบรรทัดวิธีที่ง่ายที่สุดคือใช้ fgets ()
char buffer[100];
FILE *fp = fopen("filename", "r"); // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);
หากคุณกำลังอ่านไฟล์พิเศษเช่น stdin หรือไพพ์คุณจะไม่สามารถใช้ fstat เพื่อรับขนาดไฟล์ล่วงหน้าได้ นอกจากนี้หากคุณกำลังอ่านไฟล์ไบนารี fgets จะสูญเสียข้อมูลขนาดสตริงเนื่องจากอักขระ '\ 0' ที่ฝังอยู่ วิธีที่ดีที่สุดในการอ่านไฟล์คือใช้ read and realloc:
#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 GiB ฉันทำเช่นนี้เนื่องจากโปรแกรมทำให้ไฟล์ทั้งหมดเป็นสตริงซึ่งอาจใช้ ram มากเกินไปและทำให้คอมพิวเตอร์พัง อย่างไรก็ตามหากคุณไม่สนใจสิ่งนั้นคุณก็สามารถลบออกจากโค้ดได้
#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);
}
buffer
คุณจัดสรรให้malloc(length +1)
ไม่ได้รับการปลดปล่อย เป็นสิ่งที่ผู้ใช้วิธีนี้จะต้องทำหรือไม่จำเป็นต้องfree()
มีหน่วยความจำที่จัดสรร?
หากคุณใช้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);
}
}
เพิ่งแก้ไขจากคำตอบที่ยอมรับข้างต้น
#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);
}
// 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
ว่างเปล่าหรือคืนค่าความยาวที่เลือกใช้
ฉันจะเพิ่มเวอร์ชันของตัวเองตามคำตอบที่นี่เพื่อการอ้างอิงเท่านั้น รหัสของฉันคำนึงถึงขนาดของ (ถ่าน) และเพิ่มความคิดเห็นเล็กน้อยลงไป
// 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);
ง่ายและเรียบร้อย (สมมติว่าเนื้อหาในไฟล์น้อยกว่า 10,000):
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;
}