Example:
Three files
hi.txt
Inside of txt: "May we be"
again.txt
Inside of txt: "The ones who once"
final.txt
Inside of txt: "knew C"
And then, another file called "order"
order.txt
Inside of txt:
"hi.txt;6"
"again.txt;7"
"final.txt;3"
What I want: read the first file name, open it, list the content, wait 6 seconds, read the second name, open it, list the content, wait 7 seconds, read the third name, open it, list the content, wait 3 seconds.
If I do it without opening the content (you'll see a second while on my code) and list the names, it works, yet for some reason it doesn't when it's about the content.
orderFile = fopen("order.txt","r");
while(fscanf(orderFile,"%49[^;];%d",fileName,&seconds) == 2)
{
contentFile = fopen(fileName,"r");
while(fscanf(contentFile,"%[^\t]",textContent) == 1)
{
printf("%s\n", textContent);
}
sleep(seconds);
fclose(contentFile);
}
fclose(orderFile);
Output:
May we be
(Waits 7 seconds)
Program closes with "RUN SUCCESSFUL"
EDIT#
It works now, as you guys said, this was the problem:
Old:
while(fscanf(orderFile,"%49[^;];%d",fileName,&seconds) == 2)
New:
while(fscanf(orderFile," %49[^;];%d",fileName,&seconds) == 2)
I'm having a "hard" time to completely understand it, what does the space does? doesn't accept enters? spaces? What exactly is it?
Don't use fscanf for that
int
main()
{
FILE *orderFile = fopen("order.txt", "r");
if (orderFile != NULL)
{
int seconds;
char line[128];
/*
* fgets, read sizeof line characters or unitl '\n' is encountered
* this will read one line if it has less than sizeof line characters
*/
while (fgets(line, sizeof line, orderFile) != NULL)
{
/*
* size_t is usually unsigned long int, and is a type used
* by some standard functions.
*/
size_t fileSize;
char *fileContent;
FILE *contentFile;
char fileName[50];
/* parse the readline with scanf, extract fileName and seconds */
if (sscanf(line, "%49[^;];%d", fileName, &seconds) != 2)
continue;
/* try opening the file */
contentFile = fopen(fileName,"r");
if (contentFile == NULL)
continue;
/* seek to the end of the file */
fseek(contentFile, 0, SEEK_END);
/*
* get current position in the stream,
* it's the file size, since we are at the end of it
*/
fileSize = ftell(contentFile);
/* seek back to the begining of the stream */
rewind(contentFile);
/*
* request space in memory to store the file's content
* if the file turns out to be too large, this call will
* fail, and you will need a different approach.
*
* Like reading smaller portions of the file in a loop.
*/
fileContent = malloc(1 + fileSize);
/* check if the system gave us space */
if (fileContent != NULL)
{
size_t readSize;
/* read the whole content from the file */
readSize = fread(fileContent, 1, fileSize, contentFile);
/* add a null terminator to the string */
fileContent[readSize] = '\0';
/* show the contents */
printf("%s\n", fileContent);
/* release the memory back to the system */
free(fileContent);
}
sleep(seconds);
fclose(contentFile);
}
fclose(orderFile);
}
return 0;
}
Everything is barely explained in the code, read the manuals if you need more information.
Related
The software intends to read from a csv styled file (it's delimited by space not by a comma) and split the initial file into two new ones. The two files are determined by the last field, which is a binary value. As it stands, it currently reads the file character by character. I want it to recognize the space, and only run the individual character check on the very last field. To my understanding, strtok() will come in handy, but I'm struggling to find a way to incorporate that into the existing software.
Any help would be greatly appreciated :)
/*
* C program to parse a file, and split it into two based on the final line of input
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Function declarations */
int isMale(signed char _line);
int isFemale(signed char _line);
int isMale(signed char _line)
{
}
int isFemale(signed char _line)
{
}
int main()
{
/* File pointer to hold reference to different files */
FILE * fPtrIn, // Input file
* fPtrMale, // Males of school age
* fPtrFemale, // Females of school age
* fPtrMisc; // Data not within the given parameters
// current_char is the current character being read
// success stores the read status
char current_char;
int success;
// Open all files to perform read/write.
fPtrIn = fopen("data/example.txt", "r");
fPtrMale = fopen("data/males.txt" , "w");
fPtrFemale = fopen("data/females.txt" , "w");
fPtrMisc = fopen("data/erroneus.txt", "w");
// fopen() return NULL if unable to open file in given mode.
if(fPtrIn == NULL || fPtrMale == NULL || fPtrFemale == NULL || fPtrMisc == NULL)
{
// Unable to open file, exit software
printf("Unable to open file.\n");
printf("Please check whether file exists and you have read/write privilege.\n");
exit(EXIT_FAILURE);
}
// File open success message
printf("File opened successfully. \n\n");
// Read an integer and store read status in success.
while (fscanf(fPtrIn, "%d", ¤t_char) != -1)
{
// Write each one to separate file
if (isMale(current_char))
fprintf(fPtMale, "%d\n", current_char);
else if (isFemale(current_char))
fprintf(fPtrFemale, "%d\n", current_char);
else
fprintf(fPtrMisc, "%d\n", current_char);
}
// Done with all files, hence close all.
fclose(fPtrIn);
fclose(fPtrMale);
fclose(fPtrFemale);
fclose(fPtrMisc);
printf("Data written to files successfully.");
return 0;
}
You should
Read lines via fgets()
Copy the line read because strtok() will modify original buffer.
parse fields via strtok()
Judge and output according to the parsed field.
// hoping that too long lines won't come
char line[102400], line_parse[102400];
// Read an integer and store read status in success.
while (fgets(line, sizeof(line), fPtrIn) != NULL)
{
char *last_field, *ret;
// Copy the line for parsing
strcpy(line_parse, line);
// Separate the line into tokens
last_field = ret = strtok(line_parse, " ");
while (ret != NULL)
{
last_field = ret;
ret = strtok(NULL, " ");
}
// Get the first character of the last field
if (last_field == NULL) current_char = '\0'; else current_char = last_field[0];
// Write each one to separate file
if (isMale(current_char))
fputs(line, fPtrMale);
else if (isFemale(current_char))
fputs(line, fPtrFemale);
else
fputs(line, fPtrMisc);
}
I'm making a program to split a file into N smaller parts
of (almost) equal sizes. So here's my code:
FILE * fp = fopen(file,"r");
long aux;
long cursor = 0;
long blockSize = 1024000; //supose each smaller file will have 1 MB
long bytesLimit = blockSize;
for( i = 0 ; i < n ; i++) {
FILE * fp_aux = fopen( outputs[i] , "w"); //outputs is an array of temporary file names
while(cursor < bytesLimit) { //here occurs the infinite loop
fscanf(fp,"%lu\n",&aux);
fprintf(fp_aux,"%lu\n",aux);
cursor = ftell(fp);
}
fclose(fp_aux);
bytesLimit = bytesLimit + blockSize;
}
//here add some more logic to get the remaining content left in the main file
The code works if I want to split the file into two or three parts, but when I try to split it into 10 parts, fscanf locks on reading the same number and stays on an infinite loop there.
My input file has the format "%lu\n" like below:
1231231
4341342
4564565
...
If splitting a file is the focus, then simplify your method. Because your post indicates you are working with a text file, the assumption is that it contains words with punctuation, numbers, linefeeds etc. With this type of content, it can be parsed into lines using fgets()/fputs(). This will allow you to read lines from one large file, tracking accumulated size as you go, and writing lines to several smaller files...
Some simple steps:
1) determine file size of file to be split
2) Set desired small file size.
3) open large file
4) Use fgets/fputs in a loop, opening and closing files to split contents, using accumulated size as split point.
5) Clean up. (fclose files etc.)
Here is an example that will illustrate these steps. This splits a large text file by size, regardless of text content. (I used a text file with 130K of volume and split it into segments of 5k
#define SEGMENT 5000 //approximate target size of small file
long file_size(char *name);//function definition below
int main(void)
{
int segments=0, i, len, accum;
FILE *fp1, *fp2;
long sizeFile = file_size(largeFileName);
segments = sizeFile/SEGMENT + 1;//ensure end of file
char filename[260]={"c:\\play\\smallFileName_"};//base name for small files.
char largeFileName[]={"c:\\play\\largeFileName.txt"};//change to your path
char smallFileName[260];
char line[1080];
fp1 = fopen(largeFileName, "r");
if(fp1)
{
for(i=0;i<segments;i++)
{
accum = 0;
sprintf(smallFileName, "%s%d.txt", filename, i);
fp2 = fopen(smallFileName, "w");
if(fp2)
{
while(fgets(line, 1080, fp1) && accum <= SEGMENT)
{
accum += strlen(line);//track size of growing file
fputs(line, fp2);
}
fclose(fp2);
}
}
fclose(fp1);
}
return 0;
}
long file_size(char *name)
{
FILE *fp = fopen(name, "rb"); //must be binary read to get bytes
long size=-1;
if(fp)
{
fseek (fp, 0, SEEK_END);
size = ftell(fp)+1;
fclose(fp);
}
return size;
}
if you have bad data in the file that isn't a long unsigned int format then the fscanf will read it, the file pointer for the fp file object won't change. Then the program sets the fp file pointer back to the start of that read and it will do it again
To prevent this you need to check the return value of the fscanf to see that it has an appropriate value ( probably 1 )
If you want to split a file into several parts with a specified maximum file size of each part, why do you use fscanf(..), ftell(..) and fprintf(..)?
This is not the fastest way to achieve your goal...
I recommend doing it in this way:
Open input file
As long as there is input data (!feof(..))
Open output file (if not already open)
Read block of input data (fread)
Write block of data to output file (fwrite)
track number of bytes written and close output file if maximum file size is reached
Go back to step 2.
Clean up
If doing so the split files will not exceed a specific maximum file size. Additionally you avoid usage of slow file I/O functions like fprintf.
A possible implementation would look like this:
/*
** splitFile
** Splits an existing input file into multiple output files with a specified
** maximum file size.
**
** Return Value:
** Number of created result files, or 0 in case of bad input data or a negative
** value in case of an error during file splitting.
*/
int splitFile(char *fileIn, size_t maxSize)
{
int result = 0;
FILE *fIn;
FILE *fOut;
char buffer[1024 * 16];
size_t size;
size_t read;
size_t written;
if ((fileIn != NULL) && (maxSize > 0))
{
fIn = fopen(fileIn, "rb");
if (fIn != NULL)
{
fOut = NULL;
result = 1; /* we have at least one part */
while (!feof(fIn))
{
/* initialize (next) output file if no output file opened */
if (fOut == NULL)
{
sprintf(buffer, "%s.%03d", fileIn, result);
fOut = fopen(buffer, "wb");
if (fOut == NULL)
{
result *= -1;
break;
}
size = 0;
}
/* calculate size of data to be read from input file in order to not exceed maxSize */
read = sizeof(buffer);
if ((size + read) > maxSize)
{
read = maxSize - size;
}
/* read data from input file */
read = fread(buffer, 1, read, fIn);
if (read == 0)
{
result *= -1;
break;
}
/* write data to output file */
written = fwrite(buffer, 1, read, fOut);
if (written != read)
{
result *= -1;
break;
}
/* update size counter of current output file */
size += written;
if (size >= maxSize) /* next split? */
{
fclose(fOut);
fOut = NULL;
result++;
}
}
/* clean up */
if (fOut != NULL)
{
fclose(fOut);
}
fclose(fIn);
}
}
return (result);
}
The above code split a test file with a size of 126803945 bytes into 121 1MB parts in about 500ms.
Note that the size of buffer (here: 16KB) affects the speed a file is split. The bigger the buffer the faster a huge file is split. If you want to use really large buffers (>1MB or so) you have to allocate (and free) the buffer on each call (or use a static buffer if you do not need reentrant code).
This question already has answers here:
Append to the end of a file in C
(2 answers)
Closed 9 years ago.
I have a Linux C program that store configuration parameters in a text file. I read from the text file using
FILE *file = fopen(filename, "r"):
and write to the file using following code
FILE *file = fopen(filename, "w"):
I am getting a problem where the text file is cleared, it is blank the next time I come to read it.
I understand that when the file is opened for write it gets overwritten, my program stores the contents of the file after it has read it in and writes it back out.
It does in the main write it out correctly, but occasionally I will find that the text file is blank.
My initial thoughts were that this may be because the program was unsafely stopped, mid way through writing to the file leaving it blank or there were 2 instances of the program running and as one open it for writing, the other reads it in, meaning it would read in a blank file and then overwrite it with a blank file when it writes it out.
After some testing this does not seem to be the case.
This leaves me unsure as to what is causing the text file to be cleared.
Does anyone have any ideas?
See code below
char text_lines[200][54]; /* global variable */
void read_in_text_file()
{
/* this sub reads in the text file */
//printf("read in file\n");
/* declares the variables */
char line[128];
int counter = 0;
int length;
/* open a text file and read it in line by line, storing each line in a variable. also returning a value for the number of lines in each section */
static const char filename[] = "config.txt";
FILE *file = fopen(filename,"r"); /* opens the config file */
if (file==NULL){ /* checks if the file has successfully opened */
perror ("Error opening file"); /* displays error message on stderr - that returns reason file did not open */
printf("Error opening file\n"); /* tells the user that the file has not opened */
exit(0); /* exits the program if the text file can not be read in */
}
else{
//printf("the file is open\n");
while ( fgets ( line, sizeof line, file ) != NULL) /* reads each line of the text file */
{
sprintf(text_lines[counter],"%s",line); /* puts the line into a variable */
length = zstrlen(text_lines[counter]); /* calculates the length of the text not including \r or \n characters */
if(text_lines[counter][length-1] == '\n') /* checks if the last character is \n (a new line character) */
{
text_lines[counter][length-1] = '\0'; /* removes this new line character and replaces it with end of line identifier */
}
counter = counter + 1; /* uses a counter for each line */
} /* end of while loop */
number_of_lines = counter; /* puts the number of lines into a integer variable */
fclose(file); /* closes the file */
}
} /* end of sub for reading in the text file */
/* some changes may be made to the config before it is printed to the file again */
void print_to_text_file()
{
pthread_mutex_lock(&lock); /* block until thread has ownership */
/* sub for printing all the lines in the text_lines variable to the text file "config.txt" */
int counter;
static const char filename[] = "config.txt";
FILE *file = fopen(filename,"w"); /* opens the config.txt file, with write privileges */
if (file==NULL){ /* checks if the file has successfully opened */
perror ("Error opening file"); /* displays error message on stderr - that returns reason file did not open */
printf("Error opening file\n"); /* tells the user that the file has not opened */
}
else{
//printf("the file is open\n"); /* prints to the terminal screen the file has opened */
for (counter = 0; counter < number_of_lines; counter++) /* uses a for loop to scroll through all text lines */
{
// printf("%s\n",text_lines[counter]);
fprintf(file, "%s\n",text_lines[counter]); /* prints current text line to the file */
}
fclose(file); /* closes the file */
}
pthread_mutex_unlock(&lock); /* release blocking on thread */
} /* end of print text to file sub */
First, you should know that when you open a file with fopen and a mode of w, the file is immediately truncated to zero bytes (erasing its contents). You can use w+ or wa to prevent this.
Second, you might be missing an fclose() to close the file when you're done with it. Although when your program exits, all files are typically closed. However, if you don't close the file, changes made to it might not be committed to disk.
Since you updated the question to include some code, I can say that your program is unsafe and can overrun the text_lines buffer if the file contains more than 200 lines. If you can provide an actual complete but minimal test program that can be compiled and run, it would help you to get more answers next time.
Here is an actual program that can be compiled and run. It reads in the lines of config.txt, converts all characters to capitals, then writes the lines back out to the file. I removed your pthread mutex function calls since it was unnecessary in this test program (there's no multithreading).
#include <ctype.h> /* toupper */
#include <stdio.h> /* fopen, fclose, fgets, perror */
#include <stdlib.h> /* exit */
#include <string.h> /* strlen */
/* Constants */
/* Maximum number of lines to read */
#define TEXT_LINES_CAPACITY 54
/* Maximum length of a line including EOL and NUL */
#define MAX_LINE_LEN 200
/* Global variables */
char text_lines[TEXT_LINES_CAPACITY][MAX_LINE_LEN]; /* text of lines */
int number_of_lines; /* number of lines in text_lines */
/* Function declarations */
void capitalize_string(char* s);
void strip_eol(char* s);
void read_in_text_file(void);
void print_to_text_file(void);
/* Function definitions */
int main()
{
int i;
/* Read in the contents of the file. */
read_in_text_file();
/* Modify it by capitalizing the text. */
for (i = 0; i < number_of_lines; ++i)
{
capitalize_string(text_lines[i]);
}
/* Write out the modified contents to the same file. */
print_to_text_file();
return 0;
}
void capitalize_string(char* s)
{
while (*s != 0)
{
*s = toupper(*s);
++s;
}
}
/* Open a text file and read it in line by line. The lines are stored in the
* global variable text_lines and the number of lines in number_of_lines. */
void read_in_text_file(void)
{
static const char filename[] = "config.txt";
FILE *file = fopen(filename,"r"); /* opens the config file */
if (file == NULL)
{
/* Print error message after the file name. */
perror(filename);
exit(1); /* Exit with failure code (nonzero) */
}
else
{
/* Read each line of the text file. */
while (number_of_lines < TEXT_LINES_CAPACITY &&
fgets(text_lines[number_of_lines], MAX_LINE_LEN, file) != NULL)
{
strip_eol(text_lines[number_of_lines]);
++number_of_lines;
}
fclose(file);
}
}
/* Remove LF and/or CR characters from the end of the string. */
void strip_eol(char* s)
{
/* Loop while the string ends with a CR or LF character. */
while (strlen(s) > 0 &&
(s[strlen(s) - 1] == '\n' ||
s[strlen(s) - 1] == '\r'))
{
/* Erase the last character. */
s[strlen(s) - 1] = '\0';
}
}
/* Write all the lines of text_lines to the text file "config.txt" */
void print_to_text_file(void)
{
static const char filename[] = "config.txt";
/* open the config.txt file, with write privileges */
FILE *file = fopen(filename,"w");
if (file == NULL)
{
/* Print error message after the file name. */
perror(filename);
}
else
{
int i;
/* Iterate over all text lines. */
for (i = 0; i < number_of_lines; i++)
{
fprintf(file, "%s\n", text_lines[i]); /* prints current text line to the file */
}
fclose(file); /* closes the file */
}
}
I'm trying to make a program that replacing strings in files.
I got the program below that replacing all the occurrences of one string in the file, but now I need to extend so it'll replace multiple strings.
The trivial way is to run the program several times, each time with different string as input, but I'm looking for more efficient way to do it.
My input can be:
Set of strings to replace (each string appears once).
List of strings to replace by order of appearance (string can be at the list several times) but without knowing their offset.
Thanks for the help.
#include <stdio.h>
#include <string.h>
#define LINE_LEN 128
int main(){
char fileOrig[32] = "orig.txt";
char fileRepl[32] = "new.txt";
char text2find[80];
char text2repl[80];
printf("enter text to replace in the file:");
scanf ("%s",text2find);
sprintf(text2repl,"%s%s%s","<b><font color=\"#FF0000\">",text2find,"</font></b>");
char buffer[LINE_LEN+2];
char *buff_ptr, *find_ptr;
FILE *fp1, *fp2;
int buff_int;
size_t find_len = strlen(text2find);
fp1 = fopen(fileOrig,"r");
fp2 = fopen(fileRepl,"w");
buff_int=(int)buffer;
while(fgets(buffer,LINE_LEN+2,fp1)){
buff_ptr = buffer;
while ((find_ptr = strstr(buff_ptr,text2find))){
while(buff_ptr < find_ptr)
fputc((int)*buff_ptr++,fp2);
fputs(text2repl,fp2);
buff_ptr += find_len;
}
fputs(buff_ptr,fp2);
}
fclose(fp2);
fclose(fp1);
return 0;
}
Sometimes things can get complicated. Say if you have strings to replace as {ab,ba} and they would be replaced to {xy,yx} respectively. Say you have the input file to contain "aba". Now the output becomes order dependant.
Similar confusion can occur if the replacement of one string causes another string to be formed which belongs to the strings-to-replace list.
IMO, you should define what you want to do in this situations and then use an approach similar to what you have already done.
BTW, you can better your string matching by using an finite automata based approach or use some existing state of the art algorithm like KMP or Boyer-Moore. This will let you search multiple strings at once.
I'm not sure if what you want is possible, but you might want to look into string search algorithms to make the search part of your algorithm more optimized. A naive search algorithm has, according to Wikipedia, complexity Θ((n-m+1) m), with n the length of your text and m the length of your search string. Take a look at the link, you can do significantly better.
Once you have all the offsets of the strings to replace, the actual replacing seems to be fairly straightforward.
I'm sorry I can't completely answer your question, but I thought this might give you some optimization ideas.
I think this will help you. Please use following Code to Search & Replace string .
Call this function from Top lavel function like this:
replaceIPAddress( "System.cfg", "172.16.116.157", "127.0.0.1");
void replaceIPAddress( char * confFileName, char *text_to_find , char *text_to_replace )
{
FILE *input = fopen(confFileName, "r");
FILE *output = fopen("temp.txt", "w");
char buffer[512];
while (fgets(buffer, sizeof(buffer), input) != NULL)
{
char *pos = strstr(buffer, text_to_find);
if (pos != NULL)
{
/* Allocate memory for temporary buffer */
char *temp = calloc(
strlen(buffer) - strlen(text_to_find) + strlen(text_to_replace) + 1, 1);
/* Copy the text before the text to replace */
memcpy(temp, buffer, pos - buffer);
/* Copy in the replacement text */
memcpy(temp + (pos - buffer), text_to_replace, strlen(text_to_replace));
/* Copy the remaining text from after the replace text */
memcpy(temp + (pos - buffer) + strlen(text_to_replace),
pos + strlen(text_to_find),
1 + strlen(buffer) - ((pos - buffer) + strlen(text_to_find)));
fputs(temp, output);
free(temp);
}
else
fputs(buffer, output);
}
fclose(output);
fclose(input);
/* Rename the temporary file to the original file */
rename("temp.txt", confFileName);
}
//
// Find and replace data in a file
//
// This is not as straightforward a problem as it initially appears,
// because if you have the text
//
// "Jack is a pirate"
//
// And you wish to replace "is" with "was", the string needs to be longer,
// or else you end up with the following:
//
// "Jack wasapirate"
//
// This becomes more of a problem for larger text. For example, if we wanted
// to replace "Jack" with "Rumpelstiltskin", we'd end up with:
//
// "Rumpelstiltskin"
//
// Which completely overwrites our original text!!!
//
// In order to do this correctly, we wither need to:
//
// 1. Read the entire file into a in-memory buffer
// 2. Write to a temporary file, then replace the original file
//
// Option #2 is easier to implement, and should work for your coursework.
//
#include <stdio.h>
int main(int argc, char** argv)
{
// We need a buffer to read in data
const int BufferSize = 0x1000;
char Buffer[BufferSize];
char *InputFileName = "input.txt";
char *TemporaryFileName = "temp.txt";
// Open the file for reading. 'rt' means that it must already exist, for reading.
FILE *Input = fopen(InputFileName, "rt");
// Our output file. 'w+' means to create the file if it doesnt exist, for writing.
FILE *Output = fopen(TemporaryFileName, "w+");
// Our find and replace arguments
char *Find = "is";
char *Replace = "was";
if(NULL == Input)
{
printf("Could not open file");
return 1;
}
printf("Find: %s\n", Find);
printf("Replace: %s\n", Replace);
// For each line...
while(NULL != fgets(Buffer, BufferSize, Input))
{
// For each incidence of "is"
char *Stop = NULL; // Where to stop copying (at 'is')
char *Start = Buffer; // Start at the beginning of the line, and after each match
printf("Line: %s\n", Buffer);
while(1)
{
// Find next match
Stop = strstr(Start, Find);
if(NULL == Stop)
{
// Print the remaining text in the line
fwrite(Start, 1, strlen(Start), Output);
break;
}
// Write out everything between the end of the previous match, and the
// beginning of the current match.
//
// For example:
//
// "Jack is a pirate who is cool"
//
// Has two instances to replace. In order, we'd find them as such:
//
// "Jack is a pirate who is cool"
// ^
// ^
// What we want to do is write:
// - "Jack "
// - "was"
// - "a pirate who "
// - "was"
// - "cool"
printf("Match starts at: %s\n", Stop);
// We have found a match! Copy everything from [Start, Stop)
fwrite(Start, 1, Stop - Start, Output);
// Write our replacement text
fwrite(Replace, 1, strlen(Replace), Output);
// Next time, we want to start searching after our 'match'
Start = Stop + strlen(Find);
printf("Search resumes at: %s\n", Start);
};
}
// Close our files
fclose(Input);
fclose(Output);
// If desired, rename the Output file to the Input file
rename(TemporaryFileName, InputFileName);
return 0;
}
Using C I would like to read in the contents of a text file in such a way as to have when all is said and done an array of strings with the nth string representing the nth line of the text file. The lines of the file can be arbitrarily long.
What's an elegant way of accomplishing this? I know of some neat tricks to read a text file directly into a single appropriately sized buffer, but breaking it down into lines makes it trickier (at least as far as I can tell).
Thanks very much!
Breaking it down into lines means parsing the text and replacing all the EOL (by EOL I mean \n and \r) characters with 0.
In this way you can actually reuse your buffer and store just the beginning of each line into a separate char * array (all by doing only 2 passes).
In this way you could do one read for the whole file size+2 parses which probably would improve performance.
It's possible to read the number of lines in the file (loop fgets), then create a 2-dimensional array with the first dimension being the number of lines+1. Then, just re-read the file into the array.
You'll need to define the length of the elements, though. Or, do a count for the longest line size.
Example code:
inFile = fopen(FILENAME, "r");
lineCount = 0;
while(inputError != EOF) {
inputError = fscanf(inFile, "%s\n", word);
lineCount++;
}
fclose(inFile);
// Above iterates lineCount++ after the EOF to allow for an array
// that matches the line numbers
char names[lineCount][MAX_LINE];
fopen(FILENAME, "r");
for(i = 1; i < lineCount; i++)
fscanf(inFile, "%s", names[i]);
fclose(inFile);
For C (as opposed to C++), you'd probably wind up using fgets(). However, you might run into issues due to your arbitrary length lines.
Perhaps a Linked List would be the best way to do this?
The compiler won't like having an array with no idea how big to make it. With a Linked List you can have a really large text file, and not worry about allocating enough memory to the array.
Unfortunately, I haven't learned how to do linked lists, but maybe somebody else could help you.
If you have a good way to read the whole file into memory, you are almost there. After you've done that you could scan the file twice. Once to count the lines, and once to set the line pointers and replace '\n' and (and maybe '\r' if the file is read in Windows binary mode) with '\0'. In between scans allocate an array of pointers, now that you know how many you need.
you can use this way
#include <stdlib.h> /* exit, malloc, realloc, free */
#include <stdio.h> /* fopen, fgetc, fputs, fwrite */
struct line_reader {
/* All members are private. */
FILE *f;
char *buf;
size_t siz;
};
/*
* Initializes a line reader _lr_ for the stream _f_.
*/
void
lr_init(struct line_reader *lr, FILE *f)
{
lr->f = f;
lr->buf = NULL;
lr->siz = 0;
}
/*
* Reads the next line. If successful, returns a pointer to the line,
* and sets *len to the number of characters, at least 1. The result is
* _not_ a C string; it has no terminating '\0'. The returned pointer
* remains valid until the next call to next_line() or lr_free() with
* the same _lr_.
*
* next_line() returns NULL at end of file, or if there is an error (on
* the stream, or with memory allocation).
*/
char *
next_line(struct line_reader *lr, size_t *len)
{
size_t newsiz;
int c;
char *newbuf;
*len = 0; /* Start with empty line. */
for (;;) {
c = fgetc(lr->f); /* Read next character. */
if (ferror(lr->f))
return NULL;
if (c == EOF) {
/*
* End of file is also end of last line,
` * unless this last line would be empty.
*/
if (*len == 0)
return NULL;
else
return lr->buf;
} else {
/* Append c to the buffer. */
if (*len == lr->siz) {
/* Need a bigger buffer! */
newsiz = lr->siz + 4096;
newbuf = realloc(lr->buf, newsiz);
if (newbuf == NULL)
return NULL;
lr->buf = newbuf;
lr->siz = newsiz;
}
lr->buf[(*len)++] = c;
/* '\n' is end of line. */
if (c == '\n')
return lr->buf;
}
}
}
/*
* Frees internal memory used by _lr_.
*/
void
lr_free(struct line_reader *lr)
{
free(lr->buf);
lr->buf = NULL;
lr->siz = 0;
}
/*
* Read a file line by line.
* http://rosettacode.org/wiki/Read_a_file_line_by_line
*/
int
main()
{
struct line_reader lr;
FILE *f;
size_t len;
char *line;
f = fopen("foobar.txt", "r");
if (f == NULL) {
perror("foobar.txt");
exit(1);
}
/*
* This loop reads each line.
* Remember that line is not a C string.
* There is no terminating '\0'.
*/
lr_init(&lr, f);
while (line = next_line(&lr, &len)) {
/*
* Do something with line.
*/
fputs("LINE: ", stdout);
fwrite(line, len, 1, stdout);
}
if (!feof(f)) {
perror("next_line");
exit(1);
}
lr_free(&lr);
return 0;
}