I need to read 4000 lines from a file, do some manipulations with them, then read next 4000 lines, do some manipulations with them, then read next 4000 lines and so on till the end of some BIG file. How to do this correctly?
Here is my basic code which is just reading all lines but not block by block from a file:
#include <stdio.h>
#include <string.h>
#define bufSize 1024
int main(int argc, char *argv[])
{
FILE* fp;
char buf[bufSize];
if (argc != 2)
{
fprintf(stderr, "Usage: %s <soure-file>\n", argv[0]);
return 1;
}
if ((fp = fopen(argv[1], "r")) == NULL)
{ /* Open source file. */
perror("fopen source-file");
return 1;
}
while (fgets(buf, sizeof(buf), fp) != NULL)
{
buf[strlen(buf) - 1] = '\0'; /* eat the newline fgets() stores */
printf("%s\n", buf);
}
fclose(fp);
return 0;
}
What is the problem?
You would need to use 2 loops. The outer one would repeat reading chunks of lines until EOF.
The code could look like the following sketch:
...
while (1)
{
/* read <= 4000 lines and process */
}
The inner would read the lines and stores them away:
size_t lines = 0; /** next index to be used with lineBuffer
(and number of lines already stored)*/
char *lineBuffer[4000];
char buf[bufSize];
while (lines < 4000 && fgets(buf, sizeof(buf), fp) != NULL)
{
buf[strlen(buf) - 1] = '\0';
lineBuffer[lines] = malloc(strlen(buf);
strcpy(lineBuffer[lines], buf);
lines++;
}
if (lines == 0)
{
break; /* we are done*/
}
/* do processing on data */
/* deallocate dynamic memory */
for (int i = lines - 1; lines>=0; i--)
{
free(lineBuffer[i]);
}
lines = 0;
Of course you could use static memory allocation using
char lineBuffer[4000][bufSize];
instead of mallocing. That would save doing alloc/dealloc sequences, or do allocation on top-level (outside the loops) using calloc(400*bufSize);
But this is left to personal preferences given the fact that a total of about 4MB of memory is being used with current settings.
With respect to performance:
having statically allocated memory might give some speed benefit.
You might also try increasing buffer size used by stdio (by a setbuffer() variant to read larger chunks from file.
Whether this will have any noticable effect is subject to some performance measurings you should take (if an issue in the first place)
Ok,then allocate a two dimensional array on the heap and declare a variable n of type size_t to keep track of the number of lines.Try the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define bufSize 1024
int main(int argc, char *argv[])
{
FILE* fp;
if (argc != 2)
{
fprintf(stderr, "Usage: %s <soure-file>\n", argv[0]);
return 1;
}
if ((fp = fopen(argv[1], "r")) == NULL)
{ /* Open source file. */
perror("fopen source-file");
return 1;
}
size_t n = 0;
char(*buf)[bufSize] = malloc(bufSize * 4000);
if (!buf) {
fprintf(stderr, "Error - Failed to allocate memory.\n");
fclose(fp);
return 1;
}
while (1)
{
if (fgets(buf[n], bufSize, fp) != NULL) {
n++;
if (n == 4000) {
/* do something */
// ...
n = 0;
}
}
else {
// do something with leftover
break;
}
}
free(buf);
fclose(fp);
return 0;
}
Related
I want to do something pretty simple, but somehow it doesn't do what I want.
I have a file with numbers in it. I wanna read from that file and print those numbers but in reverse order.
so let's say we have the numers:
10
32
43
6
7
I want to have:
7
6
43
32
10
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[]) {
char userInput[MAX], target[MAX];
FILE *file = stdin;
if (argc > 2) {
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2) {
file = fopen(argv[1], "r");
if (!file) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
while(fgets(userInput, sizeof(userInput), file)) {
size_t len = strlen(userInput);
int i;
for (i = len-1; i >= 0; i--) {
if (userInput[i] == ' ') {
userInput[i] = '\0';
printf("%s ", &(userInput[i]) + 1);
}
}
printf("%s", userInput);
}
if (file != stdin) {
fclose(file);
}
exit(EXIT_SUCCESS);
}
Does anyone see the mistake?
This is the content of my .txt file:
11
34
45
3
78
43
3
4
9
34
23
43
Your program is designed to accept all of its input on a single line. Your input file contains multiple lines.
The following 1. solution is based on the example shown in the question.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[])
{
char userInput[MAX] = "";
char target[MAX] = "";
FILE *file = stdin;
if (argc > 2)
{
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2)
{
file = fopen(argv[1], "r");
if (!file)
{
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
/* Read all numbers from file and write it to target buffer */
while(fgets(userInput, sizeof(userInput), file))
{
/* Break loop if there is not enough space to store current number. */
if ((strlen(target) + strlen(userInput)) >= MAX)
{
break;
}
/* Add current number read from file to target buffer */
strcat(target, userInput);
/* Print current number read from file */
printf("%s", userInput);
}
if (file != stdin) {
fclose(file);
}
/*
* Run over target buffer in reverse order and replace current number
* split character '\n' by string end marker '\0'.
* After that print current number.
*/
size_t len = strlen(target);
if (len > 0)
{
for (size_t i = len-1; i != 0; i--)
{
if (target[i] == '\n')
{
target[i] = '\0';
printf("%s\n", &(target[i]) + 1);
}
}
/* Print first number */
printf("%s\n", &(target[0]));
}
exit(EXIT_SUCCESS);
}
However it might be better to store the numbers within an array of integer using sscanf() and after that print the content of the array in reverse order.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[])
{
char userInput[MAX] = "";
int nums[MAX];
int count = 0;
FILE *file = stdin;
if (argc > 2)
{
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2)
{
file = fopen(argv[1], "r");
if (!file)
{
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
/* Read all numbers from file and write it to target buffer */
while(fgets(userInput, sizeof(userInput), file))
{
sscanf(userInput, "%i", &nums[count]);
/* Break loop if there is not enough space to store current number. */
if (count >= MAX)
{
break;
}
count++;
/* Print current number read from file */
printf("%s", userInput);
}
if (file != stdin) {
fclose(file);
}
/* Print numbers stored in array in reverse order */
printf("\n");
for (int idx = count; idx != 0; idx--)
{
printf("%i\n", nums[idx-1]);
}
exit(EXIT_SUCCESS);
}
You can read your input file starting from the end using fseek
Here there's a possible implementation based on your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[]) {
int i = 0, end = 0, cnt = 0;
char ch, cnt_flag = 0;
char userInput[MAX];
FILE *fp = stdin;
if (argc > 2) {
fprintf(stderr, "Usage: %s[<fp>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2) {
fp = fopen(argv[1], "r");
if (!fp) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
fseek(fp, 0, SEEK_END);
end = ftell(fp);
while (i < end) {
i++;
fseek(fp, -i, SEEK_END);
ch = fgetc(fp);
if (cnt_flag == 1) {
cnt++;
if (ch == '\n') {
/* skip empty lines */
if (cnt > 1) {
fgets(userInput, cnt, fp);
printf("%s\n", userInput);
}
cnt_flag = 0;
cnt = 0;
}
}
if (ch == '\n')
cnt_flag = 1;
}
/* read first line */
fseek(fp, 0, SEEK_SET);
fgets(userInput, cnt + 1, fp);
printf("%s\n", userInput);
if (fp != stdin) {
fclose(fp);
}
exit(EXIT_SUCCESS);
}
If u need to reverse strings in file, check this out. This is crossplatform solution without big chunk buffer usage. Only restriction that this is for file operations only. Advantages: file size independent solution.
This example based on your code.
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#define NEW_LINE 2 // Windows new line: 2 bytes (CR) + (LF)
#else
#define NEW_LINE 1 // Linux new line: 1 byte (LF)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
// This function can be replaced with any other solution
// u need (write data into another file, or allocate data in the memory and etc)
void printLineFromFile(FILE *file) {
char buff[255];
// write line with \n to buff
fgets(buff, 255, file);
// total buff size
int buffLen = strlen(buff);
int printLen = buffLen;
// printLen equals buffLen if the last symbol isn't \n
if (buff[buffLen - 1] == '\n') printLen--;
// print specified amount of bytes from buff
fprintf(stdout, "%.*s\n", printLen, buff);
}
int main(int argc, char *argv[]) {
FILE *file = stdin;
if (argc > 2) {
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2) {
file = fopen(argv[1], "r");
if (!file) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
// set position to the end of file
fseek(file, 0, SEEK_END);
// endPosition - position of last byte in file. (not EOF)
long endPosition = ftell(file) - 1;
// currPosition - moving position across the file
long currPosition = endPosition;
// byte buffer to read into it
int ch;
while (currPosition >= 0) {
// moving file position to the currPosition
fseek(file, currPosition, SEEK_SET);
if (currPosition == 0) {
printLineFromFile(file);
currPosition -= NEW_LINE;
continue;
}
ch = fgetc(file);
if (ch == '\n') {
if (currPosition == endPosition) {
currPosition -= NEW_LINE;
fprintf(stdout, "\n");
}
else {
printLineFromFile(file);
currPosition -= NEW_LINE;
continue;
}
}
// move back to position before fgetc
currPosition--;
};
if (file != stdin) {
fclose(file);
}
exit(EXIT_SUCCESS);
}
I'm just getting into C and I figured this would be a good exercise. I've been putsing around with fgets trying to read from a file and I am just doing something wrong. I would like to enter the file name to read, enter the file name to output to, create that file, sort it (just a list of words), and then dump the sorted list into the created file. I know I should be doing something like:
char strIn[25];
printf("Enter a source filename: ");
fgets(strIn, 25, stdin);
printf("You entered: %s \n", strIn);
FILE *infile;
infile = fopen(strIn, "r");
if (infile == NULL){
printf("Unable to open file.");
}
char strOut[25];
printf("Enter a destination filename: ");
fgets(strOut, 25, stdin);
printf("You entered: %s \n", strOut);
FILE *outfile;
Any help is appreciated! Thank you
fgets puts the newline character \n at the end of your buffer. So you need to remove it.
int length = strlen(strIn);
if ( length > 0 && strIn[length-1] == '\n' )
strIn[length-1] = '\0';
You are on the right track. qsort will do what you want. The approach used here is not scalable; everything is held in memory and static allocation will make things HUGE very quickly, but it works as a toy example. At the moment it will break once there are more than 1000 lines in the input file.
# include <stdio.h>
# include <string.h>
#define MAXNAMELEN 100
#define MAXLINELEN 100
#define MAXITEMS 1000
int main(int argc, char ** argv) {
FILE * infile, * outfile;
// Statically allocated -- dastardly!
char name[MAXNAMELEN];
char line[MAXLINELEN];
char lines[MAXITEMS][MAXLINELEN];
int i, items = 0;
printf("Enter a source filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
// No error checking -- ANYWHERE -- dastardly!
infile = fopen(name, "r");
while (fgets(line, sizeof(line), infile)) {
strcpy(lines[items], line);
items++;
}
qsort(lines, items, MAXLINELEN, strcmp);
printf("Enter a destination filename: ");
fgets(name, sizeof(name), stdin);
name[strlen(name)-1] = '\0'; // strip newline
outfile = fopen(name, "w");
for (i=0; i<items; i++) {
fputs(lines[i], outfile);
}
fclose(infile);
fclose(outfile);
}
Now with dynamic allocation and error-checking (possibly an improvement on than the version above). Sorts /usr/share/dict/words (99171 lines) no problem at all. Still requires that the entire array be held in memory. See External Sorting for a way around that.
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
#define CHUNKLEN 100
#define INITITEMS 1000
/* Reads a string from stream into buffer until a newline or EOF.
buffer is dynamically allocated (and reallocated if necessary)
to ensure the string fits. Returns the number of characters put
into the buffer (zero if EOF and no characters read), or -1 on error. */
int unlimited_read(char ** buffer, FILE * stream) {
int bufl = CHUNKLEN;
int strl = 0;
char * chunk = (char *)malloc(CHUNKLEN);
if ( (*buffer = (char *) malloc(CHUNKLEN)) == NULL ) {
perror("memory error (malloc)");
return -1;
}
while (fgets(chunk, CHUNKLEN, stream) != NULL) {
strcpy(*buffer + strl, chunk);
strl += strlen(chunk);
if ( (strl == bufl - 1 ) && *(*buffer + strl - 1) != '\n') {
// lengthen buffer
bufl += CHUNKLEN - 1;
if ( (*buffer = realloc(*buffer, bufl)) == NULL ) {
perror("memory error (realloc)");
return -1;
}
} else {
// This shouldn't fail -- we're only ever making it smaller
*buffer = realloc(*buffer, strl);
return strl;
}
} // while
// If fgets returned NULL and we are not at EOF, it didn't work
return feof(stream) ? strl : -1;
}
/* Compare two strings given pointers to those strings.
Routine is courtesy of the qsort man page */
int cmpstringp(const void *p1, const void *p2) {
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
/* Sort lines in a file. File must end with a newline. */
int main(int argc, char ** argv) {
FILE * infile, * outfile;
char * inname, * outname, *tmpstr;
char ** lines;
int ret, tmp, nlines, i, items = 0;
if (argc != 3) {
printf("Usage: %s file_to_sort output_file\n", argv[0]);
exit(EXIT_FAILURE);
}
inname = argv[1];
outname = argv[2];
if ( (lines = malloc(INITITEMS * sizeof(char *))) == NULL) {
perror("memory error (malloc)");
exit(EXIT_FAILURE);
}
nlines = INITITEMS;
infile = fopen(inname, "r");
while ((ret = unlimited_read(&lines[items], infile)) > 0) {
items++;
if (items == nlines) {
nlines += INITITEMS;
lines = realloc(lines, (nlines * sizeof(char *)));
}
}
if (ret < 0) {
printf("WARNING: possibly truncated file\n");
}
tmpstr = lines[items - 1]; // Final line in file
tmp = strlen(tmpstr);
if (tmpstr[tmp - 1] != '\n') {
printf("Error: input file does not end with newline\n");
exit(EXIT_FAILURE);
}
qsort(lines, items, sizeof(char *), cmpstringp);
outfile = fopen(outname, "w");
for (i = 0; i < items; i++) {
fputs(lines[i], outfile);
free(lines[i]);
}
free(lines);
fclose(infile);
fclose(outfile);
}
I have a filed called a1.txt which contains the words
amazing
malevolent
permanent
and another one called a2.txt with
Amazing
Bridge
Malevolent
Here is the code that I use to read the files into arrays, thanks to #M Oehm.
NOTE: void b(); is the same as void a() but it reads a2.txt instead.
void a();
void b();
char (*a1)[50];
char (*a2)[50];
int n;
int main(int argc, char *argv[]) {
a();
printf("\n\n");
b();
int i=0, j=0;
for (i; i < strlen(*a1); i++)
{
for (j; j <strlen(*a2); j++)
{
printf("\n%d", strcmp(a1[i], a2[j]));
}
}
return 0;
}
void a(){
FILE *f;
int i;
f = fopen("a1.txt", "r");
if (f == NULL) {
fprintf(stderr, "Can't open file\n");
exit(1);
}
/* first pass */
n = 0;
while (fscanf(f, "%*s") != EOF) n++; /* star means: scan, but don't store */
a1 = malloc((n + 1) * sizeof(*a1));
if (a1 == NULL) {
fprintf(stderr, "Allocation failed\n");
exit(1);
}
/* second pass */
fseek(f, 0, SEEK_SET);
for (i = 0; i < n; i++) {
fscanf(f, "%49s", a1[i]);
}
*a1[n] = '\0';
/* process words */
for (i = 0; i < n; i++) {
printf("%s\n",a1[i]);
}}
As you can see the rows of the arrays are dynamic(I used three words as a test, however this should be done for an unknown amount of words hence the usage of calloc). Is it possible to detect the rows of each array and write the common words of each in a new file?
Finding the common words is a simple matter, I assume, of using strstr.
You seem to have some misconceptions about memory allocation:
char *str[50] creates an array of 50 (uninitialised) pointers of char. Perhaps you want char (*str)[50], which is a pointer to an array of 50 chars, to which you can allocate memory.
lSize is the length of the file, i.e. the number of chars. It looks a bit as if you wanted to count the number of words.
I'll present two strategies for reading words into a char array.
Read fixed-size words
This strategy uses a fixed word size of 50, as in your example. It opens the file and reads it in two passes. The first to determine the number of words, the next to read the actual words after allocating enough space.
int main(int argc, char *argv[])
{
FILE *f;
char (*str)[50]; /* Pointer to words of max length 49 */
int n; /* number of words */
int i;
if (argc != 2) {
fprintf(stderr, "Usage: $fifo file_name.ip\n");
exit(1);
}
f = fopen(argv[1], "r");
if (f == NULL) {
fprintf(stderr, "Can't open file\n");
exit(1);
}
/* first pass */
n = 0;
while (fscanf(f, "%*s") != EOF) n++; /* star means: scan, but don't store */
str = malloc((n + 1) * sizeof(*str));
if (str == NULL) {
fprintf(stderr, "Allocation failed\n");
exit(1);
}
/* second pass */
fseek(f, 0, SEEK_SET);
for (i = 0; i < n; i++) {
fscanf(f, "%49s", str[i]);
}
*str[n] = '\0';
/* process words */
for (i = 0; i < n; i++) {
printf("%4d: '%s'\n", i, str[i]);
}
free(str);
return 0;
}
This approach is reasonably simple, but it has two drawbacks: You will waste memory, because most words won't be 50 characters long. And you have to scan the file twice. Both drawbacks are not serious on modern computers.
Allocate as you go
You can also maintain the words as pointers to pointers to char, char **str. str[i] gives you a word, which is stored as pointer into existing memory of a null-terminated string. The function strtok gives you such strings.
This "existing memory" is the contents of the file as char buffer. Rohan has shown you how to get ti: By getting the file length, allocating and reading.
This method takes only one pass, because it reallocates memory according to its needs. Start with space for, say, 64 words, read them, find out we need more, so reallocate to make 128 words fit, read words 64-127, and so on.
int main(int argc, char *argv[])
{
FILE *f;
char *buf; /* Buffer that hold the file's contets */
size_t size; /* Size of that buffer */
char **str; /* Array of pointers to words in that buffer */
int n; /* number of words */
int nalloc; /* For how many words space is allocated */
int i;
if (argc != 2) {
fprintf(stderr, "Usage: $fifo file_name.ip\n");
exit(1);
}
f = fopen(argv[1], "r");
if (f == NULL) {
fprintf(stderr, "Can't open file\n");
exit(1);
}
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(size + 1);
if (buf == NULL) {
fprintf(stderr, "Allocation failed\n");
exit(1);
}
/* read whoe file */
fread(buf, 1, size, f);
buf[size] = '\0';
fclose(f);
n = 0;
nalloc = 0;
str = NULL;
for (;;) {
if (n >= nalloc) {
/* reallocate */
nalloc = nalloc * 2;
if (nalloc == 0) nalloc = 64;
str = realloc(str, nalloc * sizeof(*str));
if (str == NULL) {
fprintf(stderr, "Reallocation failed\n");
exit(1);
}
}
str[n] = strtok(n ? NULL : buf, " \t\n\r");
if (str[n] == NULL) break;
n++;
}
/* process words */
for (i = 0; i < n; i++) {
printf("%4d: '%s'\n", i, str[i]);
}
free(buf);
free(str);
return 0;
}
This approach is more efficient, but also more complicated. Note how many variables I need to keep track of everything: The llocated size, the actual size, the size of the text buffer. And I have to take care of two allocated arrays.
Given that you want to read two files, it makes sense to pack these variables into a structure and read each file into such a structure.
Conclusion
These are only two of many ways to read words from a file. Both are not trivial and require that you understand how to manage memory.
I think one of the most basic things to learn is that a pointer may be used for many different things. It can just point to existing memory, whether that has been allocated or is an automatic array. But it can also be used as a handle to allocated memory; it will then behave like an array, excapt that you have to free the memory after use. You should not "move" such pointers, i.e. change the address they point to.
Both kinds of pointers look the same in your code, but you have to know which pointer acts as what.
With
char *a1[50];
char *a2[50]; //not used so can remove
You are creating array of char pointers, not array of characters. You may want to just use char pointers as
char *a1;
char *a2;
Then instead of
a1[50] = calloc(1, lSize +1);
do
a1 = calloc(1, lSize +1);
Using a1[50] as in your code is incorrect and will cause undefined behavior (including segmentation fault). The array elements are from 0 to 49, so last element is a1[49].
Also, you can use lSize to read those many characters as below
for (i=0; i <lSize; i++)
{
if (fscanf(file, "%c", &a1[i]) == 1){
printf("%c", a1[i]);
}
}
But may be you can skip the for loop limit and read from file until there is no error.
So the assignment is to implement a substring search program using an input file to be searched from and an input to be searched. I created the following code:
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
FILE *fp;
fp = fopen(argv[1],"r");
if (fp == NULL)
{
printf("Error");
return 0;
}
char* tmpp[100];
int count = 0;
char* nexts = argv[2];
char* tmp = fgets(tmpp,100,fp);
while(tmp = strstr(tmp,nexts))
{
count++;
tmp++;
}
printf("%d\n\n",count);
fclose(fp);
return 0;
}
The program compiles but when i go to implement it in the ubuntu terminal as:
echo "aabb" >beta
./a.out beta a
1
Why isnt the program using the first argument (argv[1]) as beta and the second argument (argv[2]) as a correctly?
You should open a file and then read bytes from that file into temporary buffer:
FILE *file = fopen("file", "r");
while (1) {
char buffer[BUFSIZ+1];
size_t nread = fread(buffer, 1, sizeof(buffer)-1, file);
if (nread == 0) break; // read error or EOF
buffer[nread] = 0;
// chunk with BUFSIZ amount of bytes is available via buffer (and is zero-terminated)
}
If you want to search for string/pattern in a file, be aware that looked pattern in file may cross your chunk-size boundary, for example: you look for "hello", and BUFSIZ is 512. File contains "hello" at byte 510. Obviously, if you read by 512, you will get the first chunk ending with "he", and the second chunk starting with "llo". Probability of this situation is nonzero for all chunk sizes (except SIZE_MAX, but that buffer size is impossible by other reasons). Dealing with borders may be very complicated.
Close...but this is closer:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if (argc != 3)
{
fprintf(stderr, "Usage: %s file pattern\n", argv[0]);
return 1;
}
FILE *fp = fopen(argv[1], "r");
if (fp == NULL)
{
fprintf(stderr, "Error: failed to open file %s for reading\n", argv[1]);
return 1;
}
char tmpp[1000];
int count = 0;
char* nexts = argv[2];
while (fgets(tmpp, sizeof(tmpp), fp) != 0)
{
char *tmp = tmpp;
while ((tmp = strstr(tmp, nexts)) != 0)
{
count++;
tmp++;
}
}
printf("%d\n", count);
fclose(fp);
return 0;
}
The main difference is that this loops reading multiple lines from the input file. Yours would only work on files with a single line of input.
I'm reading a file and want to put each line into a string in an array. The length of the file is arbitrary and the length of each line is arbitrary (albeit assume it will be less than 100 characters).
Here's what I've got and it's not compiling. Essentially this is an array to an array of characters, right? So shouldn't it be char** words = (**char)malloc(sizeof(*char));?
#include <stdio.h>
#include <stdlib.h>
int main(){
int BUFSIZE = 32767;//max number of lines to read
char** words = (**char)malloc(sizeof(*char));//gives error: expected expression before 'char'
FILE *fp = fopen("coll.txt", "r");
if (fp == 0){
fprintf(stderr, "Error opening file");
exit(1);
}
int i = 0;
words[i] = malloc(BUFSIZE);
while(fscanf(fp, "%100s", words[i]) == 1)//no line will be longer than 100
{
i++;
words[i] = realloc(words, sizeof(char*)*i);
}
int j;
for(j = 0; j < i; j++)
printf("%s\n", words);
return 0;
}
Note: I've read "Reading from a file and storing in array" but it doesn't answer my question.
There are a few issues with your program. The realloc() statement is not used correctly. I also prefer fgets() for getting a line. Here is my solution. This also uses realloc() to increase the allocation of the buffer lines so that you neither have to know the number of lines in advance nor do you have to read the file in two passes (faster that way). This is a common technique to use when you don't know how much memory you'll have to allocate in advance.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int lines_allocated = 128;
int max_line_len = 100;
/* Allocate lines of text */
char **words = (char **)malloc(sizeof(char*)*lines_allocated);
if (words==NULL)
{
fprintf(stderr,"Out of memory (1).\n");
exit(1);
}
FILE *fp = fopen("coll.txt", "r");
if (fp == NULL)
{
fprintf(stderr,"Error opening file.\n");
exit(2);
}
int i;
for (i=0;1;i++)
{
int j;
/* Have we gone over our line allocation? */
if (i >= lines_allocated)
{
int new_size;
/* Double our allocation and re-allocate */
new_size = lines_allocated*2;
words = (char **)realloc(words,sizeof(char*)*new_size);
if (words==NULL)
{
fprintf(stderr,"Out of memory.\n");
exit(3);
}
lines_allocated = new_size;
}
/* Allocate space for the next line */
words[i] = malloc(max_line_len);
if (words[i]==NULL)
{
fprintf(stderr,"Out of memory (3).\n");
exit(4);
}
if (fgets(words[i],max_line_len-1,fp)==NULL)
break;
/* Get rid of CR or LF at end of line */
for (j=strlen(words[i])-1;j>=0 && (words[i][j]=='\n' || words[i][j]=='\r');j--)
;
words[i][j+1]='\0';
}
/* Close file */
fclose(fp);
int j;
for(j = 0; j < i; j++)
printf("%s\n", words[j]);
/* Good practice to free memory */
for (;i>=0;i--)
free(words[i]);
free(words);
return 0;
}
You should change the line:
char** words = (**char)malloc(sizeof(*char));
into this:
char** words=(char **)malloc(sizeof(char *)*Max_Lines);