I found this piece of code at Reading a file character by character in C and it compiles and is what I wish to use. My problem that I cannot get the call to it working properly. The code is as follows:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL)
return NULL; //could not open file
code = malloc(1500);
while ((c = fgetc(file)) != EOF)
{
code[n++] = (char) c;
}
code[n] = '\0';
return code;
}
I am not sure of how to call it. Currently I am using the following code to call it:
.....
char * rly1f[1500];
char * RLY1F; // This is the Input File Name
rly1f[0] = readFile(RLY1F);
if (rly1f[0] == NULL) {
printf ("NULL array); exit;
}
int n = 0;
while (n++ < 1000) {
printf ("%c", rly1f[n]);
}
.....
How do I call the readFile function such that I have an array (rly1f) which is not NULL? The file RLY1F exists and has data in it. I have successfully opened it previously using 'in line code' not a function.
Thanks
The error you're experiencing is that you forgot to pass a valid filename. So either the program crashes, or fopen tries to open a trashed name and returns NULL
char * RLY1F; // This is not initialized!
RLY1F = "my_file.txt"; // initialize it!
The next problem you'll have will be in your loop to print the characters.
You have defined an array of pointers char * rly1f[1500];
You read 1 file and store it in the first pointer of the array rly1f[0]
But when you display it you display the pointer values as characters which is not what you want. You should just do:
while (n < 1000) {
printf ("%c", rly1f[0][n]);
n++;
}
note: that would not crash but would print trash if the file read is shorter than 1000.
(BLUEPIXY suggested the post-incrementation fix for n BTW or first character is skipped)
So do it more simply since your string is nul-terminated, pass the array to puts:
puts(rly1f[0]);
EDIT: you have a problem when reading your file too. You malloc 1500 bytes, but you read the file fully. If the file is bigger than 1500 bytes, you get buffer overflow.
You have to compute the length of the file before allocating the memory. For instance like this (using stat would be a better alternative maybe):
char *readFile(char *fileName, unsigned int *size) {
...
fseek(file,0,SEEK_END); // set pos to end of file
*size = ftell(file); // get pos, i.e. size
rewind(file); // set pos to 0
code = malloc(*size+1); // allocate the proper size plus one
notice the extra parameter which allows you to return the size as well as the file data.
Note: on windows systems, text files use \r\n (CRLF) to delimit lines, so the allocated size will be higher than the number of characters read if you use text mode (\r\n are converted to \n so there are less chars in your buffer: you could consider a realloc once you know the exact size to shave off the unused allocated space).
Related
(C, visual studio 2022 environment)
I have to write a program that read a .txt file (character by character) and puts each character in another .txt file (in the same directory created by visual studio).
the initial .txt file is too long, so in this question I wrote a smaller part of the data.
this is my initial file.txt:
3.456789
3.456789
3.5671234566
3.450934759435735738457
3.3298017238973289742398074238
3.456789
3.456789
3.5671234566
3.450934759435735738457
3.3298017238973289742398074238
3.456789
3.456789
3.5671234566
3.450934759435735738457
3.3298017238973289742398074238
3.456789
3.456789
3.5671234566
3.450934759435735738457
3.3298017238973289742398074238
and this is the code: (i used only the main.c because it's only a test to test the C input output):
the code is supposed to do these stuff:
open a read-only file. (pointed to by f).
(null pointer check. I checked f).
I defined the array with the name x. (it's a pointer that points to 0-th element of the array).
I defined CHAR COUNT because I used it in the first while loop (the read part).
(null pointer check. I checked x).
reading consists of an infinite loop made up by these 3 parts: read, check, use.
read part: I assigned to count the value of fgetc [fgetc() return value is a character].
fgetc takes characters of f stream.
check part: I checked if it's equal to EOF macro. if it's the case, then "break" and go outside the first while loop. {while writing this question, I'm thinking about one possible error in this phase: if this check condition holds true, than the second array is executed but I'd have another error in the second while loop because I didn't read the first file entirely}.
use part: If the if check holds false, than do a calloc and allocate enough memory (i, sizeof(double)) to store the value that is read with fgetc.
(another null pointer check. I checked x again because I have allocated x). {while writing this question, I'm thinking if calloc have to be checked or not, because it's not like malloc. malloc fills up allocated memory with random stuff, but calloc initializes the memory to zero}.
I assigned the values (x[i] = count).
I incremented i by 1, of course.
outside the first loop, I opened another file (pointed to by g), but this file is a write only file.
(null pointer check. I checked g).
second infinite while loop, but I used fputc (it writes one character on the file).
(EOF check. I checked whether or not x[j] is equal to EOF macro.
-outside the second while loop, I freed allocated memory, and I closed
the txt files. (before the return 0 statement).
to sum up my question:
"while writing this question, I'm thinking about one possible error in this phase: if this check condition holds true, than the second array is executed but I'd have another error in the second while loop because I didn't read the first file entirely" what do you think?
"while writing this question, I'm thinking if calloc have to be checked or not, because it's not like malloc. malloc fills up allocated memory with random stuff, but calloc initializes the memory to zero". what do you think?
and the general question: is it legit to write this program this way, or do you think there's a wiser way to do the same task? (note: I know that I could've used fprintf() and fscanf(), but I wanted to test my skills to write a program that performs the same task but characters by characters (on a long txt file)).
(I have two errors and three warnings:
1st error: uninitialized local variable f used (why? I have initialized it because it points to the beginning of the file)
2nd error: uninitialized local variables x used (why? x is a pointer and I have checked if it's a null pointer so I don't have to initialize it)
1st warning: "function": incompatible types (from "FILE *" to "const char *".
the remaining warnings are like the errors, the debugger copied it in the warning list too.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE* f = fopen(f, "r");
if (f == NULL) {
return NULL;
}
int* x; char count = 0;
if (x == NULL) {
return NULL;
}
unsigned int i = 1;
while (1) {
count = fgetc(f);
if (count == EOF) {
break;
}
int* x = calloc(i, sizeof(double));
if (x == NULL) {
return -3;
}
x[i] = count;
i++;
}
FILE* g = fopen("otherfile.txt", "w");
if (g == NULL) {
return -2;
}
int j = 1;
while (1) {
fputc(x[j], g);
if (x[j] == EOF) {
break;
}
j++;
}
free(x);
fclose(f);
fclose(g);
return 0;
}
FILE* f = fopen(f, "r");
This is obvious typo. It should be fopen("inputfile", "r").
int* x;
if (x == NULL) { return NULL; }
x is not initialized. That means it could be NULL (zero) or it could be any other random value. You can't test it at this point.
while (1) {
...
int* x = calloc(i, sizeof(double));
}
Here you have declared a different pointer with the same name. The second x is a different variable because it's in a different scope (when you put variables inside {} the scope changes)
The second x allocates memory then it is lost in the next iteration of the loop. This leads to significant memory leak.
sizeof(double) is not applicable. If you intend to store in a char buffer, first you have to know how many characters there are in the file. It's impossible to know that before you read the file.
To get around this problem, you can go through the file once and count the characters, or you can check the file size.
Once you allocate enough memory, you can use fread/fwrite to read the entire file.
int main(void)
{
FILE* fin = fopen("input.txt", "r");
if (!fin) { perror("input error"); return 0; }
FILE* fout = fopen("output.txt", "w");
if (!fout) { perror("fout error"); return 0; }
fseek(fin, 0, SEEK_END); //go to the end of file
size_t filesize = ftell(fin); //get file size
fseek(fin, 0, SEEK_SET); //go back to the beginning
//allocate enough memory
char* buffer = malloc(filesize * sizeof(char));
//read one character at a time (or `fread` the whole file)
size_t i = 0;
while (1)
{
int c = fgetc(fin);
if (c == EOF) break;
//save to buffer
buffer[i++] = (char)c;
}
//write one character at a time (or `fwrite` the whole file)
for (i = 0; i < filesize; i++)
fputc(buffer[i], fout);
free(buffer);
fclose(fin);
fclose(fout);
return 0;
}
i'm new in C programming and I am trying to open a txt file for reading it.
I have a text file with different file names I want to read, each one in a different line. I created a function txt_to_stations() that reads this file and returns a double pointer to char, I guess it has to be a double pointer because I want to save a string of char strings. This is the function.
char** txt_to_stations(const char* txt_file){
FILE* file;
file = fopen(txt_file,"r");
int line_count = 0;
int char_count = 0;
int len = 1;
char tmp_station[25];
char** stations = (char**)malloc(len*sizeof(char*));
char character;
while(!feof(file)){
character = fgetc(file);
if(character!='\n'){
tmp_estation[char_count] = character;
char_count++;
}else if(character=='\n'){
stations = (char**)realloc(stations,len*sizeof(char*));
stations[line_count] = (char*)malloc(char_count*sizeof(char));
strcpy(stations[line_count],tmp_station);
len++;
line_count++;
char_count = 0;
}
}
fclose(file);
return stations;
}
My text file is this one. "stations.txt"
weatherdata-429-81.csv
weatherdata-429-84.csv
weatherdata-429-88.csv
The problem comes when I try from the main function to read this files. The function works great because if I
char** stations = txt_to_stations("stations.txt") and then for example
printf("station1: %s\n",stations[0]) it prints in terminal weatherdata-429-81.csv.
But if I define a new file in main function
FILE* reading;
reading = fopen(stations[0]);
if(reading==NULL){
printf("csv file cant be opened");
}
It prints "csv file cant be opened", which means fopen(stations[0])==NULL but i does not cause if I simply change stations[0] by fopen("weatherdata-429-81.csv") it works. It may be a rookie error but I understand that stations[0] == weatherdata-429-81.csv (as char*)
I really tried converting stations[0](char*) to a const char* , and also in "stations.txt" writing each name into double quotes,but anyways it did not work at all. Someone helping please.
I ran your code through a debugger, and found some mistakes. I commented them(and some other points) out for you.
char** txt_to_stations(const char* txt_file)
{
FILE* file;
file = fopen(txt_file, "r");
if(file == NULL) // you forgot to error-check file. fopen is one of the most likely functions to return NULL, so you really can't forget that
{
printf("Error opening file.");
exit(0); // exits your program, regardless of where you are, defined in stdlib.h
}
int line_count = 0;
int char_count = 0;
int len = 2; // I made len slightly bigger, nothing with a little bit of buffer and also allows you to keep space for the NULL terminator
char tmp_station[25] = {0}; // always a good idea to zero out an array you're creating, while testing I found some stray garbage characters at the end of one of the strings...
char** stations = (char**) malloc(len * sizeof(char*));
char character;
while (1) // this should run forever and you should break out of this loop when you reach the end of the file
{
// this is one way to properly find the end of a file, don't put feof call inside the while condition
if (feof(file))
break;
// fgets() would save you a lot of time, but oh well, don't wanna change your code too much...
character = fgetc(file);
if(character != '\n')
{
tmp_station[char_count] = character;
char_count++;
}
else // removed the if here cause it was redundant.... else means that it must be a newline character
{
tmp_station[char_count] = '\0'; // you forgot to null-terminate the string you took into tmp_station. this is the main reason the function wasn't working
stations = (char**) realloc(stations, len*sizeof(char*));
stations[line_count] = (char*) malloc(char_count*sizeof(char));
strcpy(stations[line_count], tmp_station);
len++;
line_count++;
char_count = 0;
// it's a good idea to terminate an array of pointers with NULL, how will you know you reached the end of the array?
stations[line_count] = NULL;
}
}
fclose(file);
return stations;
}
There's a tiny little problem with your code as of now. See, because you only import a line into the array of pointers if and only if a newline is found, that means if the last line of your stations.txt file does not have a newline, that line will not get imported into the array. There are two solutions.
Add an extra newline at the end of your text file like this
weatherdata-429-81.csv
weatherdata-429-84.csv
weatherdata-429-88.csv
Change the code so you can import the last line regardless of whether it has a newline character or not.
Use fgets(). It will keep reading the entire file until it reaches the end of the file, so you can store whatever it returns inside an array and manually remove the newline characters that it generates yourself.
I'm try to get my text to be read back to front and to be printed in the reverse order in that file, but my for loop doesn't seem to working. Also my while loop is counting 999 characters even though it should be 800 and something (can't remember exactly), I think it might be because there is an empty line between the two paragraphs but then again there are no characters there.
Here is my code for the two loops -:
/*Reversing the file*/
char please;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, outputfile);
while (work[count] != NULL)
{
characters3++;
count++;
}
printf("The number of characters to be copied is-: %d", characters3);
for (characters3; characters3 >= 0; characters3--)
{
please = work[characters3];
work[r] = please;
r++;
}
fprintf(outputfile, "%s", work);
/*Closing all the file streams*/
fclose(firstfile);
fclose(secondfile);
fclose(outputfile);
/*Message to direct the user to where the files are*/
printf("\n Merged the first and second files into the output file
and reversed it! \n Check the outputfile text inside the Debug folder!");
There are a couple of huge conceptual flaws in your code.
The very first one is that you state that it "doesn't seem to [be] working" without saying why you think so. Just running your code reveals what the problem is: you do not get any output at all.
Here is why. You reverse your string, and so the terminating zero comes at the start of the new string. You then print that string – and it ends immediately at the first character.
Fix this by decreasing the start of the loop in characters3.
Next, why not print a few intermediate results? That way you can see what's happening.
string: [This is a test.
]
The number of characters to be copied is-: 15
result: [
.tset aa test.
]
Hey look, there seems to be a problem with the carriage return (it ends up at the start of the line), which is exactly what should happen – after all, it is part of the string – but more likely not what you intend to do.
Apart from that, you can clearly see that the reversing itself is not correct!
The problem now is that you are reading and writing from the same string:
please = work[characters3];
work[r] = please;
You write the character at the end into position #0, decrease the end and increase the start, and repeat until done. So, the second half of reading/writing starts copying the end characters back from the start into the end half again!
Two possible fixes: 1. read from one string and write to a new one, or 2. adjust the loop so it stops copying after 'half' is done (since you are doing two swaps per iteration, you only need to loop half the number of characters).
You also need to think more about what swapping means. As it is, your code overwrites a character in the string. To correctly swap two characters, you need to save one first in a temporary variable.
void reverse (FILE *f)
{
char please, why;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, f);
printf ("string: [%s]\n", work);
while (work[count] != 0)
{
characters3++;
count++;
}
characters3--; /* do not count last zero */
characters3--; /* do not count the return */
printf("The number of characters to be copied is-: %d\n", characters3);
for (characters3; characters3 >= (count>>1); characters3--)
{
please = work[characters3];
why = work[r];
work[r] = please;
work[characters3] = why;
r++;
}
printf ("result: [%s]\n", work);
}
As a final note: you do not need to 'manually' count the number of characters, there is a function for that. All that's needed instead of the count loop is this;
characters3 = strlen(work);
Here's a complete and heavily commented function that will take in a filename to an existing file, open it, then reverse the file character-by-character. Several improvements/extensions could include:
Add an argument to adjust the maximum buffer size allowed.
Dynamically increase the buffer size as the input file exceeds the original memory.
Add a strategy for recovering the original contents if something goes wrong when writing the reversed characters back to the file.
// naming convention of l_ for local variable and p_ for pointers
// Returns 1 on success and 0 on failure
int reverse_file(char *filename) {
FILE *p_file = NULL;
// r+ enables read & write, preserves contents, starts pointer p_file at beginning of file, and will not create a
// new file if one doesn't exist. Consider a nested fopen(filename, "w+") if creation of a new file is desired.
p_file = fopen(filename, "r+");
// Exit with failure value if file was not opened successfully
if(p_file == NULL) {
perror("reverse_file() failed to open file.");
fclose(p_file);
return 0;
}
// Assumes entire file contents can be held in volatile memory using a buffer of size l_buffer_size * sizeof(char)
uint32_t l_buffer_size = 1024;
char l_buffer[l_buffer_size]; // buffer type is char to match fgetc() return type of int
// Cursor for moving within the l_buffer
int64_t l_buffer_cursor = 0;
// Temporary storage for current char from file
// fgetc() returns the character read as an unsigned char cast to an int or EOF on end of file or error.
int l_temp;
for (l_buffer_cursor = 0; (l_temp = fgetc(p_file)) != EOF; ++l_buffer_cursor) {
// Store the current char into our buffer in the original order from the file
l_buffer[l_buffer_cursor] = (char)l_temp; // explicitly typecast l_temp back down to signed char
// Verify our assumption that the file can completely fit in volatile memory <= l_buffer_size * sizeof(char)
// is still valid. Return an error otherwise.
if (l_buffer_cursor >= l_buffer_size) {
fprintf(stderr, "reverse_file() in memory buffer size of %u char exceeded. %s is too large.\n",
l_buffer_size, filename);
fclose(p_file);
return 0;
}
}
// At the conclusion of the for loop, l_buffer contains a copy of the file in memory and l_buffer_cursor points
// to the index 1 past the final char read in from the file. Thus, ensure the final char in the file is a
// terminating symbol and decrement l_buffer_cursor by 1 before proceeding.
fputc('\0', p_file);
--l_buffer_cursor;
// To reverse the file contents, reset the p_file cursor to the beginning of the file then write data to the file by
// reading from l_buffer in reverse order by decrementing l_buffer_cursor.
// NOTE: A less verbose/safe alternative to fseek is: rewind(p_file);
if ( fseek(p_file, 0, SEEK_SET) != 0 ) {
return 0;
}
for (l_temp = 0; l_buffer_cursor >= 0; --l_buffer_cursor) {
l_temp = fputc(l_buffer[l_buffer_cursor], p_file); // write buffered char to the file, advance f_open pointer
if (l_temp == EOF) {
fprintf(stderr, "reverse_file() failed to write %c at index %lu back to the file %s.\n",
l_buffer[l_buffer_cursor], l_buffer_cursor, filename);
}
}
fclose(p_file);
return 1;
}
I'm working in C and I'm modifying existing code.
I have a char array which is stored in a file as follows:
"\x01\x02\x03"
"\x04\x05\x06"
"\x07\x08\x09"
In the original source code this char array is included as follows:
const static char chs[] =
#include "file.h"
;
I'm modifying this code to load the file into a char array during runtime (to get the exact same result as with the above approach) instead of it to be included by the pre-processor. My first approach was to simply read the file into a char buffer, as follows:
FILE *fp;
const char *filename = "file.h";
fp = fopen (filename, "rb");
assert(fp != NULL);
fseek(fp, 0L, SEEK_END);
long int size = ftell(fp);
rewind(fp);
// read entire file into the buffer
char *buffer = (char*)malloc(sizeof(char) * size);
size_t nrOfBytesRead = fread(buffer, 1, size, fp);
However I've quickly discovered that this is not correct. The file already contains the exact code representation of the char array, I cannot simply read it into a char buffer and get the same result as the include approach.
What is the best way to get my char array which is stored in file, into a char array during runtime?
As you've seen, when you read the file using fread it reads it byte for byte. It doesn't get any of the syntactic processing that the compiler does on your source files. It doesn't know that strings live inside of quotes. It doesn't map escape sequences like \x01 into single bytes.
You have several different possibilities for fixing this:
Teach your program how to do that processing as it reads the file. This would be a fair amount of work.
Put just the bytes you want into the file.
Pick a different encoding for your file.
To say a little more about #2: If you don't want to change your file-reading code, what you can do is to create an (in this case) 9-byte file containing just the nine bytes you want. Since your nine bytes are not text, it'll end up being a "binary" file, which you won't be able to straightforwardly edit with an ordinary text editor, etc. (In fact, depending on the tools you have available to you, it might be challenging just to create this particular 9-byte file.)
So if you can't use #1 or #2, you might want to go with #3: pick a brand-new way to encode the data in the file, easier to parse than #1, but easier to prepare than #2. My first thought would be to have the file be hexadecimal. That is, the file would contain
010203040506070809
or
010203
040506
070809
Your file-reading code, instead of the single call to fread, would read two characters at a time and assemble them into bytes for your array. (I'd sketch this out for you, but the compilation I was waiting for has finished, and I ought to get back to my job.)
This should read the hex values from the file and save them to buffer.
fgets() reads each line from the file.
sscanf() reads each hex value from the line.
The format string for sscanf, "\\x%x%n", scans the backslash, an x, the hex value and stores the number of characters processed by the scan. The number of characters processed is used to advance through the line. This is needed if some lines have a different number of hex values.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char line[100] = {'\0'};
unsigned char *buffer = NULL;
unsigned char *temp = NULL;
unsigned int hex = 0;
int size = 0;
int offset = 0;
int used = 0;
int bufferused = 0;
int increment = 100;
int each = 0;
FILE *pf = NULL;
if ( ( pf = fopen ( "file.h", "r")) != NULL) {
while ( fgets ( line, sizeof ( line), pf)) {//get each line of the file
offset = 1;//to skip leading quote
//sscanf each hex value in the line
while ( ( sscanf ( line + offset, "\\x%x%n", &hex, &used)) == 1) {
offset += used;// to advance through the line
if ( bufferused >= size) {
temp = realloc ( buffer, size + increment);
if ( temp == NULL) {
//one way to handle the failure
printf ( "realloc failed\n");
free ( buffer);
exit (1);
}
buffer = temp;
size += increment;
}
buffer[bufferused] = hex;
bufferused++;
}
}
fclose ( pf);
}
for ( each = 0; each < bufferused; each++) {
printf ( "%x\n", buffer[each]);
}
free ( buffer);
return 0;
}
I'm getting some issues with reading the content of my array. I'm not sure if I'm storing it correctly as my result for every line is '1304056712'.
#include <stdio.h>
#include <stdlib.h>
#define INPUT "Input1.dat"
int main(int argc, char **argv) {
int data_index, char_index;
int file_data[1000];
FILE *file;
int line[5];
file = fopen(INPUT, "r");
if(file) {
data_index = 0;
while(fgets(line, sizeof line, file) != NULL) {
//printf("%s", line); ////// the line seems to be ok here
file_data[data_index++] = line;
}
fclose(file);
}
int j;
for(j = 0; j < data_index; j++) {
printf("%i\n", file_data[j]); // when i display data here, i get '1304056712'
}
return 0;
}
I think you need to say something like
file_data[data_index++] = atoi(line);
From your results I assume the file is a plain-text file.
You cannot simply read the line from file (a string, an array of characters) into an array of integers, this will not work. When using pointers (as you do by passing line to fgets()) to write data, there will be no conversion done. Instead, you should read the line into an array of chars and then convert it to integers using either sscanf(), atoi() or some other function of your choice.
fgets reads newline terminated strings. If you're reading binary data, you need fread. If you're reading text, you should declare line as an array of char big enough for the longest line in the file.
Because file_data is an array of char, file_data[data_index] is a single character. It is being assigned a pointer (the base address of int line[5] buffer). If reading binary data, file_data should be an array of integers. If reading strings, it should be an array of string, ie char pointers, like char * file_data[1000]
you also need to initialize data_index=0 outside the if (file) ... block, because the output loop needs it to be set even if the file failed to open. And when looping and storing input, the loop should test that it's not reached the size of the array being stored into.