"fruits.txt" is a text file that starts with a number, say n, followed by the names of n fruits.I want to store those n names into an array of strings, but while trying to declare that array, I am getting "Segmentation fault(core dumped) error.
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
int count;
fp = fopen("fruits.txt", "r");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fscanf(fp, "%d", &count);
printf("%d\n", count);
char *fruits[count]; // This line is giving Segmentation fault.
fclose(fp);
return 0;
}
Depending on how you wish to provide storage (memory) to hold the fruit names as you read them, you have 2 options (2) use the little know 'm' field modifier (it was 'a' on older implementations so if you are on windoze, read the documentation to make a determination (or just try both and see which one works).
The immediate issue with your code was the fact that the fopen was called with the "w" file mode. That will only work if the fruits.txt already exhisted as the "w" mode will NOT create a file if it doesn't exist. The proper mode is "w+" (or "a" or "a+", either of which will also create a non-existing file). Simply changing your mode to "w+" will allow the fruit info to be written to a newly created fruits.txt.
The issue regarding use of a VLA (variable length array) on some MS compilers may present issues. You will just have to look at your version and the changelogs or documentation (or just try and read the error or warnings. Worse case, you can use a pointer-to-pointer-to-type, or a static array of sufficient size. Given your use of fscanf that approach is continued below. Specifically though:
char *fruits[count]; /* here you delare an array of pointers to type char */
/* utilizing a variable length array. some MS compiler */
/* version do not handle VLA's, VS13/VS15 should be ok */
for (i = 0; i < count; i++) /* read & allocate for each array element */
if (fscanf (fp, " %ms", &fruits[i]) != 1) { /* m allocates, a for ms */
fprintf (stderr, "error: read/allocation for fruits[%d] failed.\n", i);
exit (EXIT_FAILURE);
}
(Note the format string used above " %ms" which has the caveat that the pointer to accept the allocation block must be a pointer-to-pointer to char * -- which is the address of the pointer to allocate/file, e.g. &fruits[i])
The remainder of the code should be very familiar, except now all input and other critical points in the code have been validated by checking the return provided by whatever function to insure no error condition existed up to that point and that none was caused by the operation in question. That is the only way you can have confidence in the operation of your code. Get in the habit.
Putting that together, you could come up with the following.
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int count, i;
FILE *fp;
if (!(fp = fopen ("fruits.txt", "w+"))) { /* "w+" required to create file */
fprintf (stderr, "error: file open failed 'fruits.txt'.\n");
exit (EXIT_FAILURE);
}
fputs ("4 Apple Banana mango berry", fp); /* write string to fruits.txt */
fclose(fp);
if (!(fp = fopen ("fruits.txt", "r"))) { /* validate file open for reading */
fprintf (stderr, "error: file open failed 'fruits.txt'.\n");
exit (EXIT_FAILURE);
}
if (fscanf (fp, " %d", &count) != 1) { /* read all fruir from fruits.txt */
fprintf (stderr, "error: in read of value from 'fruits.txt'.\n");
exit (EXIT_FAILURE);
}
printf ("\n Quantity read from 'fruits.txt' is '%d'.\n\n", count);
char *fruits[count]; /* here you delare an array of pointers to type char */
/* utilizing a variable length array. some MS compiler */
/* version do not handle VLA's, VS13/VS15 should be ok */
for (i = 0; i < count; i++) /* read & allocate for each array element */
if (fscanf (fp, " %ms", &fruits[i]) != 1) { /* m allocates, a for ms */
fprintf (stderr, "error: read/allocation for fruits[%d] failed.\n", i);
exit (EXIT_FAILURE);
}
fclose(fp); /* close file for the last time */
for (i = 0; i < count; i++) /* output array */
printf (" fruit[%d] : %s\n", i, fruits[i]);
for (i = 0; i < count; i++) /* free allocated memory */
free (fruits[i]);
return 0;
}
A basic compile string for the code (in abc.c) and the executable placed in bin/abc could would be:
$ gcc -Wall -Wextra -o bin/abc abc.c -std=gnu11
Example Use/Output
$ ./bin/abc
Quantity read from 'fruits.txt' is '4'.
fruit[0] : Apple
fruit[1] : Banana
fruit[2] : mango
fruit[3] : berry
In any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind is the normal choice.
$ valgrind ./bin/abc
==30980== Memcheck, a memory error detector
==30980== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==30980== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==30980== Command: ./bin/abc
==30980==
Quantity read from 'fruits.txt' is '4'.
fruit[0] : Apple
fruit[1] : Banana
fruit[2] : mango
fruit[3] : berry
==30980==
==30980== HEAP SUMMARY:
==30980== in use at exit: 0 bytes in 0 blocks
==30980== total heap usage: 10 allocs, 10 frees, 1,561 bytes allocated
==30980==
==30980== All heap blocks were freed -- no leaks are possible
==30980==
==30980== For counts of detected and suppressed errors, rerun with: -v
==30980== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts. (although note: some OS's do not provide adequate memeory exclusion files (the file that excludes system and OS memory from being reported as in use) which will cause valgrind to report that some memory has not yet been freed (despite the fact you have done your job and freed al blocks you allocoted and under your control.
That's a lot to take in for a simple problem, and we didn't even talk about the preferred manner of reading the file would have been to read and entire line at a time with fgets or POSIX getline and then to either parse the individual fruits from the line read, or to tokenize the line with strtok. Take the time to digest the cocde and answer for yourself the two compiler questions you must look up (1) support of VLA's for fruits[count] and (2) whether m or a is use by your compiler to allocate.
When using fscanf, you must always check the return value. Only if the function returns a successful value (read the fscanf documentation for that), the program should continue.
I've tried below code and found working.
The difference here is first i've created the file and filled the content in it.
If i don't fill contents in file then i gets segmentation fault (not on all compilers).
So it seems in your case the fscanf() is reading some junk and returning some big junk number.
As others also suggested, please check what fscanf returns. if it returns-1, means nothing is found in your file.
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
int count;
int i;
fp = fopen ("fruits.txt", "w");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fputs("4 Apple Banana mango berry", fp);
fclose(fp);*/
fp = fopen("fruits.txt", "r");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fscanf(fp, "%d", &count);
printf("%d\n", count);
fclose(fp);
char *fruits[count]; // This line is giving Segmentation fault.
return 0;
}
This prints 4 with no error/fault.
Related
I know the theory of fwrite and fread but I must be making some mistake because I can't make them work. I made a random struct, initialized an array and used fwrite to save it into a binary file. Then I opened the same binary file, used fread and saved what was inside in another array. With the debugger I saw what was inside the second array and it says, for example:
ParkingLot2[0].company= "ffffffffffffffff"
ParkingLot2[0].years=-842150451.
When pasting the code I removed all the stuff that made the code too long like if (f==NULL) for controlling the opening of the file and the pointer==NULL after the mallocs and the control that fread and fwrite read the right amount of data.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Cars {
int years;
char company[16];
}Tcars;
void WriteBinaryFile(char* file_name, Tcars* p_1)
{
FILE* f;
f = fopen(file_name, "wb");
fwrite(p_1, sizeof(Tcars), 3, f);
fclose(f);
return;
}
Tcars* ReadBinaryFile(char* file_name, Tcars* p_2, int* pc)
{
FILE* f;
Tcars temp;
size_t number = 1;
f = fopen("cars.dat", "rb");
while (number) {
number = fread(&temp, sizeof(Tcars), 1, f);
if (number)
(*pc)++;
}
/*i already know that the size is 3 but i want to try this method because in my last exam i
was given a .dat file from my professor and i didn't know how much data i had to read through */
if ((*pc) != 0)
{
p_2 = malloc(sizeof(Tcars) * (*pc));
fread(p_2, sizeof(Tcars), (*pc), f);
}
fclose(f);
return p_2;
}
int main()
{
Tcars* ParkingLot1 = malloc(sizeof(Tcars) * 3);
for(int i=0;i<3;i++)
{
ParkingLot1[i].years = 2000 + i;
}
strcpy(ParkingLot1[0].company, "Fiat");
strcpy(ParkingLot1[1].company, "Ford");
strcpy(ParkingLot1[2].company,"Toyota");
Tcars* ParkingLot2 = NULL;
int cars_amount = 0;
WriteBinaryFile("cars.dat", ParkingLot1);
ParkingLot2 = ReadBinaryFile("cars.dat", ParkingLot2, &cars_amount);
free(ParkingLot1);
free(ParkingLot2);
return 0;
}
You have a number of small (and some not so small errors) that are causing you problems. Your primary problem is passing Tcars* p_2 to ReadBinaryFile() and allocating with p_2 = malloc(sizeof(Tcars) * (*pc)); each time. Why?
Each call to malloc() returns a new block of memory with a new and different address. You overwrite the address of p_2 with each new call, creating a memory leak and losing the data stored prior to the last call. Instead, you need to realloc() to reallocate a larger block of memory and copy your existing data to the new larger block so you can add the next Tcars worth of data at the end of the reallocated block.
If you are reading data from one file, then there is no need to pass p_2 as a parameter to begin with. Simply declare a new pointer in ReadBinaryFile() and realloc() and add each Tcars worth of data at the end and then return the newly allocated pointer. (you must validate every allocation and reallocation)
Your choice of void for WriteBinaryFile() will conceal any error encountered creating or writing to your new file. You must validate EVERY input/output file operation, especially if the data written will be used later in your program. A simple choice of return of type int returning 0 for failure and 1 for success (or vice-versa, up to you) is all you need. That way you can handle any error during file creation or writing and not blindly assume success.
A slightly more subtle issue/error is your allocation for ParkingLot1 using malloc(). Ideally you would use calloc() or use memset to set all bytes zero. Why? You write the entire Tcars struct to the file. malloc() does not initialize the memory allocated. That means all characters in the company name between the end of the name (the nul-terminating character) and the end of the 16 bytes of storage will be uninitialized. While that will not cause problems with your read or write, it is far better to ensure all data being written to your file is initialized data. Otherwise examining the contents of the file will show blocks of uninitialized values written to the file.
Another small style issue is the '*' in the declaration of a pointer generally goes with the variable and not the type. Why?
Tcars* a, b, c;
The declaration above most certainly does not declare 3-pointers of type Tcars, instead it declares pointer a and two struct of type Tcars with automatic storage duration b, and c. Writing:
Tcars *a, b, c;
makes that clear.
Lastly, don't use MagicNumbers or hardcode filenames in your functions. You shouldn't need to recompile your code just to read or write a different filename. It's fine to use "cars.dat" as a default filename in main(), but either take the filenames as the 1st argument to your program (that's what int argc, char **argv parameters to main() are for) or prompt the user for a filename and take it as input. 3 and 16 are MagicNumbers. If you need a constant, #define them or use a global enum.
Putting it altogether, you could do something similar to the following to read an unknown number of Tcars from your data file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NCARS 3 /* if you need a constant, #define one (or more) */
#define COMPANY 16
typedef struct Cars {
int years;
char company[COMPANY];
} Tcars;
/* returns 1 on success 0 on error */
int WriteBinaryFile (char *file_name, Tcars *p_1, size_t nelem)
{
FILE *f;
size_t size = sizeof *p_1;
f = fopen (file_name, "wb");
if (!f) { /* validate file open for writing */
perror ("fopen-file_name-write");
return 0;
}
/* validate that nelem blocks of size are written to file */
if (fwrite (p_1, size, nelem, f) != nelem) {
return 0;
}
if (fclose (f)) { /* validate close-after-write */
perror ("fclose-f");
return 0;
}
return 1;
}
/* returns pointer to allocated block holding ncars cars on success,
* NULL on failure to read any cars from file_name.
*/
Tcars *ReadBinaryFile (char *file_name, size_t *ncars)
{
FILE *f;
Tcars *tcars = NULL, temp;
size_t nelem = *ncars, size = sizeof (Tcars);
f = fopen (file_name, "rb");
if (!f) {
perror ("fopen-file_name-read");
return NULL;
}
while (fread (&temp, size, 1, f) == 1) {
/* always realloc to a temporary pointer */
void *tempptr = realloc (tcars, (nelem + 1) * size);
if (!tempptr) { /* validate realloc succeeds or handle error */
perror ("realloc-tcars");
break;
}
tcars = tempptr; /* assign reallocated block */
memcpy (tcars + nelem, &temp, size); /* copy new car to end of block */
nelem += 1;
}
fclose (f);
*ncars = nelem;
return tcars;
}
/* void is fine for print functions with no bearing on the
* continued operation of your code.
*/
void prn_cars (Tcars *cars, size_t nelem)
{
for (size_t i = 0; i < nelem; i++) {
printf ("%4d %s\n", cars[i].years, cars[i].company);
}
}
int main (int argc, char **argv)
{
/* read from filename provided as 1st argument ("cars.dat" by default) */
char *filename = argc > 1 ? argv[1] : "cars.dat";
/* must use calloc() on ParkingLot1 or zero memory to avoid writing
* unintialized characters (rest of company) to file.
*/
Tcars *ParkingLot1 = calloc (NCARS, sizeof(Tcars)),
*ParkingLot2 = NULL;
size_t cars_amount = 0;
if (!ParkingLot1) { /* validate EVERY allocation */
perror ("calloc-ParkingLot1");
return 1;
}
for (int i = 0; i < NCARS; i++) {
ParkingLot1[i].years = 2000 + i;
}
strcpy (ParkingLot1[0].company, "Fiat");
strcpy (ParkingLot1[1].company, "Ford");
strcpy (ParkingLot1[2].company,"Toyota");
/* validate WriteBinaryFile succeeds or handle error */
if (!WriteBinaryFile (filename, ParkingLot1, NCARS)) {
return 1;
}
ParkingLot2 = ReadBinaryFile (filename, &cars_amount);
if (ParkingLot2) { /* validate ReadBinaryFile succeeds or handle error */
prn_cars (ParkingLot2, cars_amount); /* output cars read from file */
free (ParkingLot2); /* free if ParkingLot2 not NULL */
}
free(ParkingLot1); /* free ParkingLot1 */
}
(note: you always check the return of fclose() after-a-write to catch any file errors and error flushing the data to the file that can't be caught at the time of the fwrite() call)
Also note in the man page for fread and fwrite they can read or write less than the number of byte (or elements) you request. A short-read may or may not represent an error or premature end-of-file and you need to call ferror() and feof() to determine which, if any, occurred. While direct file reads from disk are not as prone to short-reads as network reads and writes, a full implementation would protect against a short-read regardless of where the data is being read from or written to. Further investigation is left to you.
Example Use/Output
$ ./fwrite_fread_cars dat/cars.dat
2000 Fiat
2001 Ford
2002 Toyota
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./fwrite_fread_cars dat/cars.dat
==7237== Memcheck, a memory error detector
==7237== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7237== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==7237== Command: ./fwrite_fread_cars dat/cars.dat
==7237==
2000 Fiat
2001 Ford
2002 Toyota
==7237==
==7237== HEAP SUMMARY:
==7237== in use at exit: 0 bytes in 0 blocks
==7237== total heap usage: 9 allocs, 9 frees, 10,340 bytes allocated
==7237==
==7237== All heap blocks were freed -- no leaks are possible
==7237==
==7237== For lists of detected and suppressed errors, rerun with: -s
==7237== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have any question.
I have an input file which contains space separated values
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
I want content of the first line moved to s1, s2 and n respectively
and next n lines contents 2 space separated strings these will move to
production_left and production_right respectively.
The layout will be repeated for subsequent blocks of lines also. The sample data has two blocks of input.
My code given below
int main()
{
char *s1[20], *t1[20];
char *production_left[20], *production_right[20];
int n;
FILE *fp;
fp = fopen("suffix.txt","r");
int iss=0;
do{
fscanf(fp,"%s %s %d",s1[iss],t1[iss],&n);
int i=0;
while(i<n)
{
fscanf(fp,"%s %s",production_left[i],production_right[i]);
i++;
}
}while(!eof(fp));
}
Every time it is giving segmentation fault.
A lot of C, is just keeping clear in your mind, what data you need available in what scope (block) of your code. In your case, the data you are working with is the first (or header) line for each section of your data file. For that you have s1 and t1, but you have nothing to preserve n so that it is available for reuse with your data. Since n holds the number of indexes to expect for production_left and production_right under each heading, simply create an index array, e.g. int idx[MAXC] = {0}; to store each n associated with each s1 and t1. That way you preserve that value for use in iteration later on. (MAXC is just a defined constant for 20 to prevent using magic numbers in your code)
Next, you need to turn to your understanding of pointer declarations and their use. char *s1[MAXC], *t1[MAXC], *production_left[MAXC], *production_right[MAXC]; declares 4 array of pointers (20 pointers each) for s1, t1, production_left and production_right. The pointers are uninitialized and unallocated. While each can be initialized to a pointer value, there is no storage (allocated memory) associated with any of them that would allow copying data. You can't simply use fscanf and assign the same pointer value to each (they would all end up pointing to the last value -- if it remained in scope)
So you have two choices, (1) either use a 2D array, or (2) allocate storage for each string and copy the string to the new block of memory and assign the pointer to the start of that block to (e.g. s1[x]). The strdup function provides the allocate and copy in a single function call. If you do not have strdup, it is a simple function to write using strlen, malloc and memcopy (use memmove if there is a potential that the strings overlap).
Once you have identified what values you need to preserve for later use in your code, you have insured the variables declared are scoped properly, and you have insured that each is properly initialized and storage allocated, all that remains is writing the logic to make things work as you intend.
Before turning to an example, it is worth noting that you are interested in line-oriented input with your data. The scanf family provides formatted input, but often it is better to use a line-oriented function for the actual input (e.g. fgets) and then a separate parse with, e.g. sscanf. In this case it is largely a wash since your first values are string values and the %s format specifier will skip intervening whitespace, but that is often not the case. For instance, you are effectively reading:
char tmp1[MAXC] = "", tmp2[MAXC] = "";
...
if (fscanf (fp, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
That can easily be replaced with a bit more robust:
char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
...
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
(which will validate both the read and the conversion separately)
Above, also note the use of temporary buffers tmp1 and tmp2 (and buf for use with fgets) It is very often advantageous to read input into temporary values that can be validated before final storage for later use.
What remains is simply putting the pieces together in the correct order to accomplish your goals. Below is an example that reads data from the filename given as the first argument (or from stdin if no filename is given) and then outputs the data and frees all memory allocated before exiting.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 20
int main (int argc, char **argv) {
char *s1[MAXC], *t1[MAXC],
*production_left[MAXC], *production_right[MAXC];
int idx[MAXC] = {0}, /* storage for 'n' values */
iss = 0, ipp = 0, pidx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* loop until no header row read, protecting array bounds */
for (; ipp < MAXC && iss < MAXC; iss++) {
char buf[MAXC] = "", tmp1[MAXC] = "", tmp2[MAXC] = "";
int n = 0;
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s %d", tmp1, tmp2, &n) != 3)
break;
idx[iss] = n;
s1[iss] = strdup (tmp1); /* strdup - allocate & copy */
t1[iss] = strdup (tmp2);
if (!s1[iss] || !t1[iss]) { /* if either is NULL, handle error */
fprintf (stderr, "error: s1 or s1 empty/NULL, iss: %d\n", iss);
return 1;
}
/* read 'n' data lines from file, protecting array bounds */
for (int i = 0; i < n && ipp < MAXC; i++, ipp++) {
char ptmp1[MAXC] = "", ptmp2[MAXC] = "";
if (!fgets (buf, sizeof buf, fp) ||
sscanf (buf, "%s %s", ptmp1, ptmp2) != 2) {
fprintf (stderr, "error: read failure, ipp: %d\n", iss);
return 1;
}
production_left[ipp] = strdup (ptmp1);
production_right[ipp] = strdup (ptmp2);
if (!production_left[ipp] || !production_right[ipp]) {
fprintf (stderr, "error: production_left or "
"production_right empty/NULL, iss: %d\n", iss);
return 1;
}
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < iss; i++) {
printf ("%-8s %-8s %2d\n", s1[i], t1[i], idx[i]);
free (s1[i]); /* free s & t allocations */
free (t1[i]);
for (int j = pidx; j < pidx + idx[i]; j++) {
printf (" %-8s %-8s\n", production_left[j], production_right[j]);
free (production_left[j]); /* free production allocations */
free (production_right[j]);
}
pidx += idx[i]; /* increment previous index value */
}
return 0;
}
Example Input File
$ cat dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
Example Use/Output
$ ./bin/production <dat/production.txt
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/production <dat/production.txt
==3946== Memcheck, a memory error detector
==3946== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3946== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==3946== Command: ./bin/production
==3946==
AA BB 4
A B
AB BA
AA CC
CC BB
A B 3
A C
B C
c B
==3946==
==3946== HEAP SUMMARY:
==3946== in use at exit: 0 bytes in 0 blocks
==3946== total heap usage: 18 allocs, 18 frees, 44 bytes allocated
==3946==
==3946== All heap blocks were freed -- no leaks are possible
==3946==
==3946== For counts of detected and suppressed errors, rerun with: -v
==3946== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I am writing a program that takes a file of ordered pairs of numbers as it's input, and I want to split those ordered pairs and convert them to an integer for storage in an array.
The file could be like this:
0 1
1 4
9 11
12 45
I want to write a function that takes in the line, (assumed to be already null terminated in another part of the program), splits the numbers at the space and then stores them in a integer array of size two:
int *store = malloc(2 * sizeof(store));
I have looked into strtok but am I correct to say that it's not thread-safe and hence not the best option in this situation? Also it won't convert the numbers to integers which is something the function also needs to have.
How can I solve this problem?
If you want to read an unknown number of ordered pairs into an array of ordered pairs, there are a number of ways to approach this. As noted in the comment, if you can guarantee that you will always have 2-integers per line, the probably the simplest way to approach reading/converting the values to integers is with fscanf
(when you have the exact same format of data on each line is one of the only times I would recommended fscanf over fgets and sscanf, and then it is only to simplify the example)
When you have an unknown number of elements to read, the basic scheme is to allocate memory for some reasonably anticipated number pairs and then realloc as needed. Since you are reading ordered-pairs, you can simplify the process by allocating storage for an pointer-to-array-of-two-int. For example with your array of ordered-pairs op declared as follows:
int (*op)[2] = NULL;
you can allocate storage for MXLINE number of pairs with:
op = calloc (MXLINE, sizeof *op);
(you can use either malloc or calloc to allocate)
After you allocate initial memory to hold your ordered pairs, you can simply read each line with a format string containing two decimal conversion specifiers (e.g. "%d %d") and have fscanf perform the conversion. You must validate 2 conversions take place and then store the values in your array.
Keep a running count of the number of pairs you add so you can realloc when you reach your limit. An example is shown below. Don't forget to free all memory when it is no longer needed, and also run your code through a memory checker to validate your use of dynamically allocated memory.
Putting it all together in a short example using your datafile, you could do something like the following. Note: the program will read from the file given as the first argument on the command line (or from stdin by default if no filename is given):
#include <stdio.h>
#include <stdlib.h>
#define MXLINE 2
int main (int argc, char **argv) {
int i, idx = 0, mxline = MXLINE;
int (*op)[2] = NULL;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* allocate/validate initial memory */
if (!(op = calloc (MXLINE, sizeof *op))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
/* read each ordered pair into 'op' array */
while (fscanf (fp, "%d %d", &op[idx][0], &op[idx][1]) == 2) {
if (++idx == mxline) { /* realloc when limit reached */
void *tmp = realloc (op, sizeof *op * mxline * 2);
if (!tmp) { /* validate reallocation */
fprintf (stderr, "error: realloc failed.\n");
break;
}
op = tmp; /* assign reallocated pointer to op */
mxline *= 2; /* update line allocation limit */
}
}
for (i = 0; i < idx; i++) /* show ordered pairs */
printf ("pair[%3d] %3d : %3d\n", i, op[i][0], op[i][1]);
free (op); /* free allocated memory */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
return 0;
}
Example Use/Output
$ ./bin/array_ptr2array <dat/orderedpairs.txt
pair[ 0] 0 : 1
pair[ 1] 1 : 4
pair[ 2] 9 : 11
pair[ 3] 12 : 45
Memory/Error Check
Do not forget to validate your use of dynamically allocated memory and that your are freeing all memory when it is no longer needed:
$ valgrind ./bin/array_ptr2array <dat/orderedpairs.txt
==8107== Memcheck, a memory error detector
==8107== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==8107== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==8107== Command: ./bin/array_ptr2array
==8107==
pair[ 0] 0 : 1
pair[ 1] 1 : 4
pair[ 2] 9 : 11
pair[ 3] 12 : 45
==8107==
==8107== HEAP SUMMARY:
==8107== in use at exit: 0 bytes in 0 blocks
==8107== total heap usage: 3 allocs, 3 frees, 112 bytes allocated
==8107==
==8107== All heap blocks were freed -- no leaks are possible
==8107==
==8107== For counts of detected and suppressed errors, rerun with: -v
==8107== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
(note: the initial allocation was for MXLINE (2) pairs. The was intentionally done to force reallocation. In the real world you will want to minimize the number of times your must allocate memory. So if you think you could have 20 or so pairs, set the initial allocation for 24 or 32 pairs or whatever is your best reasonable guess of the number you will have (plus a couple for good-measure))
Look it over and let me know if you have any questions.
Maybe this is a solution:
gets(str, 80, fp); // get a line
sscanf(str, "%d %d", &store[0], &store[1]); // read numbers from that line
I am currently trying to edit specific lines of .txt file in C. The file that im using looks like this :
Pixel location and RGB Color
Now lets say I want to change whats written on the specific line that its highlighted on the image:
400,300: (255,255,255) #FFFFFF
into this:
400,300: (000,000,000) #000000
Basically, im trying to create a black dot in specific pixels, in this case on 400,300. This is what i have of code:
#include <stdio.h>
int main(void)
{
const char *filename = "sample.txt";
int x = 400;
int y = 300;
FILE *fp;
fp = fopen(filename, "w+");
// Algorithm that reads all the file
// If("Operation that reads" == x+","+y)
// {
// Replace the line information after where it starts with "400,300"
// Like this : 400,300: (000,000,000) #000000
// }
// Algorithm that saves the file with the changes.
fclose(fp)
printf("Ok - File %s saved\n", filename);
return 0;
Creating, opening and editing .txt files is kind of new for me so I dont know what to do, the more i read about it, the more confused I get. How do I approach this problem and what code would fit here?
Update 1:
FILE *fp;
fp = fopen(filename, "w+");
if ( fp == NULL )
{
printf("Error while opening file");
}
Ok so after reading what you have placed below i came up with an idea but still needs work. I would print everything from the file to a char array. After that i would search in each slot for the specific line of code that I was looking for and keep the number slot. After that, i would go to array, run it, and when it comes to that specific slot, i would replace the needed data. Now all i needed to do is to swap the information thats in the file for the one thats in the array, save the file and problem solved. But im getting erros in the code and im missing the bits of code that would clear the txt file and save the new data.
Update 2:
#include <stdio.h>
int main(void)
{
int x,y;
int k = 0;
int noline; // Used to locate which line is the string im looking for
char search; // Used to compare with each string
char blackcode = (char)000; // In RGB, Black uses (000,000,000)
char blackhexcode = (char)000000; // The hexcode for black is #000000
const char *filename = "sample.txt";
char* strings[480000]; // Since its a 800x600 resolution picture, it needs that many lines.
char line[30]; // Space created to store whats inside each line of the file before transfering
char temp;
FILE * fp;
fp= fopen(filename, "r+");
if ( fp == NULL )
{
printf("Error while opening file");
}
else
{
while(fgets(line, sizeof line, fp))
{
strings[k]=strdup(line); // ERROR HERE! What Am I missing?
k++;
}
for(k = 0; k< sizeof strings; k++)
{
temp = scanf("%[^:]s", strings[k]);
search = ("%s,%s",x,y);
if(temp = search)
{
noline = k;
}
else
{
printf("Error : Wrong Coordinates");
}
}
for(k = 0; k < sizeof strings; k++)
{
if(k == noline)
{
strings[k] = ("%d,%d: (%s,%s,%s) #%s", x, y, blackcode, blackcode, blackcode, blackhexcode); // ERROR HERE! What did i did wrong?
}
}
// Code that cleans the txt file and saves the array back to txt file
}
fclose(fp);
}
What you are missing is somewhat conceptual, and somewhat related to fopen. When you think about opening a file with fopen, you need to pay particular attention to the effect of the file modes. If you look carefully at the man page regarding either "w" or "w+". In both cases the existing file is truncated. To 0-length in the case of "w".
To avoid this issue, one approach is to read the entire file into a buffer and then make changes to the buffer, writing the modified buffer back to the original filename. This avoids the possibility to attempting to insert/delete bytes without rewriting the remainder of the file.
To handle reading the file into a buffer, the link posted overwriting a specific line on a text file?, provides a roadmap to changing a single line in a file. Your case is different. You want to find/replace All occurrences of a particular pattern. (that is where the truncation issue posses challenges) However much of the solution there can be applied to reading the file itself into a buffer. Specifically the use of fseek and ftell.
Using fseek and ftell provides a simply way to determine the size (or length) of the file that can then be used to allocate space to hold the entire file in memory. Below is one approach to a simple function that takes the address of a character pointer and a file pointer, then using fseek and ftell allocates the required memory to hold the file and then reads the file into the buffer (filebuf) in a single operation with fread. The buffer is filled, in place, and also returned. A pointer to the file length fplen is passed to the function so the length is made available back in the calling function (main() in this case). Returning a pointer to the buffer on success (NULL otherwise) will allow assignment of the return, if desired, and a way to determine success/failure of the read:
char *read_file_into_buf (char **filebuf, long *fplen, FILE *fp)
{
fseek (fp, 0, SEEK_END);
if ((*fplen = ftell (fp)) == -1) { /* get file length */
fprintf (stderr, "error: unable to determine file length.\n");
return NULL;
}
fseek (fp, 0, SEEK_SET); /* allocate memory for file */
if (!(*filebuf = calloc (*fplen, sizeof *filebuf))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return NULL;
}
/* read entire file into filebuf */
if (!fread (*filebuf, sizeof *filebuf, *fplen, fp)) {
fprintf (stderr, "error: file read failed.\n");
return NULL;
}
return *filebuf;
}
Once you have the file in memory, the second piece of the puzzle is simply to scan through the buffer and make the replacements you need. Here there are a number of different tweaks you can apply to optimize the search/replace, but the following is just a straight forward basic search/replace where the only optimization attempt is a comparison of the starting character before using the normal string.h string comparison functions to check for your specified search string. The function returns the number of replacements made so you can determine whether a write out to the original filename is required:
unsigned find_replace_text (char *find, char *rep, char *buf, long sz)
{
long i;
unsigned rpc = 0;
size_t j, flen, rlen;
flen = strlen (find);
rlen = strlen (rep);
for (i = 0; i < sz; i++) {
/* if char doesn't match first in find, continue */
if (buf[i] != *find) continue;
/* if find found, replace with rep */
if (strncmp (&buf[i], find, flen) == 0) {
for (j = 0; buf[i + j] && j < rlen; j++)
buf[i + j] = rep[j];
if (buf[i + j])
rpc++;
}
}
return rpc;
}
Putting all the pieces together in a short example program using your sample data could be written as follows. The program expects the filename as the first argument (or it will read from stdin and write to stdout by default if no filename is given). There are always additional validation checks you can include as well:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
char *read_file_into_buf (char **filebuf, long *fplen, FILE *fp);
unsigned find_replace_text (char *find, char *rep, char *buf, long sz);
int main (int argc, char **argv) {
char *srchstr = "400,300";
char *repstr = "400,300: (000,000,000) #000000";
char *filebuf = NULL;
long int fplen = 0;
FILE *fp = NULL;
/* open file for reading (default stdin) */
fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
return 1;
}
if (!read_file_into_buf (&filebuf, &fplen, fp)) return 1;
if (fplen < 1 || fplen >= INT_MAX) { /* validate file length */
fprintf (stderr, "error: length of file invalid for fwrite use.\n");
return 1;
}
if (fp != stdin) fclose (fp);
/* find/replace text in filebuf */
if (!find_replace_text (srchstr, repstr, filebuf, fplen)) {
printf ("no replacements made.\n");
return 0;
}
/* open file for writing (default stdout) */
fp = argc > 1 ? fopen (argv[1], "w") : stdout;
if (!fp) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
return 1;
}
/* write modified filebuf back to filename */
if (fwrite (filebuf, sizeof *filebuf, (size_t)fplen, fp) != (size_t)fplen) {
fprintf (stderr, "error: file write failed.\n");
return 1;
}
if (fp != stdout)
if (fclose (fp) == EOF) {
fprintf (stderr, "error: fclose() returned EOF\n");
return 1;
}
free (filebuf);
return 0;
}
Just include the functions at the bottom of the file. You can then:
Compile
gcc -Wall -Wextra -O3 -o bin/fread_file fread_file.c
(or use the equivalent compile string with your compiler)
Input File
$ cat dat/rbgtst.txt
400,280: (234,163,097) #EAA361
400,300: (255,255,255) #FFFFFF
400,320: (064,101,160) #4065A0
400,340: (220,194,110) #DCC26E
Use/File After Replacement
$ ./bin/fread_file dat/rbgtst.txt
$ cat dat/rbgtst.txt
400,280: (234,163,097) #EAA361
400,300: (000,000,000) #000000
400,320: (064,101,160) #4065A0
400,340: (220,194,110) #DCC26E
or reading from stdin writing to stdout:
$ ./bin/fread_file <dat/rbgtst.txt
400,280: (234,163,097) #EAA361
400,300: (000,000,000) #000000
400,320: (064,101,160) #4065A0
400,340: (220,194,110) #DCC26E
Memory/Error Check
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
For Linux valgrind is the normal choice. There are many subtle ways to misuse a new block of memory. Using a memory error checker allows you to identify any problems and validate proper use of of the memory you allocate rather than finding out a problem exist through a segfault. There are similar memory checkers for every platform. They are all simple to use, just run your program through it. E.g.:
$ valgrind ./bin/fread_file dat/rbgtst.txt
==13768== Memcheck, a memory error detector
==13768== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==13768== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==13768== Command: ./bin/fread_file dat/rbgtst.txt
==13768==
==13768==
==13768== HEAP SUMMARY:
==13768== in use at exit: 0 bytes in 0 blocks
==13768== total heap usage: 3 allocs, 3 frees, 2,128 bytes allocated
==13768==
==13768== All heap blocks were freed -- no leaks are possible
==13768==
==13768== For counts of detected and suppressed errors, rerun with: -v
==13768== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
You want to confirm All heap blocks were freed -- no leaks are possible and ERROR SUMMARY: 0 errors from 0 contexts (ignore the suppressed note which simply relates to missing debug symbol files not installed on my system)
Look over the code and understand what it is doing. This isn't presented as the only way of doing what you are attempting to do, but it is presented as an example of how to approach the problem while avoiding a number of pitfalls inherent in trying to change a line-at-a-time in an existing file utilizing offsets and a number of reads/writes to the file. Let me know if you have questions.
You cannot write specific line of txt file in general.
Actually, txt file is just a sequence of bytes. Every line separated by each other just by special symbol '\n' (or symbols '\r', '\n': there are two approaches).
So, if you rewrite some line, you have to move data (lines) remained in the file just after your new line.
But if your new line has the same length as before, you can write it over old line without any worries.
The best approach I can think of for something like this is to open the file in read only mode and then copy everything to a new folder by opening a new file in 'w+' mode. Then you go line by line in the read file until you find a line that you wish to change, then you rewrite the line yourself in the new copy file. Then skip that line in the read file and continue on.
After the copy file is what you want, you can replace the name of it to the original file name you want it to have. Then it will act as if you edited the file like you wanted to.
I'm trying to create a code, which reads from textile, and then stores the data into memory, prints out to the screen so the user can read it, but it is still saved into the memory so you can use it for the rest of the program..
Here is the sample of the textile
75
nevermind
nvm
not much
nm
no problem
np
people
ppl
talk to you later
ttyl
because
cuz
i don't know
idk
as soon as possible
asap
yeah
ya
how are you
hru
you
the list goes on, it has a total of 150 words, 151 lines if the first number is included. The 75 serves to tell you how many pairs there are.
anyways, here is the code that i Have written so far, it uses this struct
typedef struct
{
char *English;
char *TextSpeak;
}Pair;
The code i have written so far is:
FILE *infile =fopen("dictionary.txt","r");
int phraseCounter;
fscanf(infile, "%i", &phraseCounter); //Now you have the number of phrase pairs
//Allocate memory
Pair *phrases=malloc(sizeof(Pair) * phraseCounter);
//Run loop
for(int i=0; i < phraseCounter; i++){
//Get the english word
phrases[i].English = malloc(sizeof(char));
fscanf(infile,"%s",phrases[i].English);
//run loop to read next line
for(int a=0; a < phraseCounter; a++){
phrases[i].TextSpeak = malloc(sizeof(char));
fscanf(infile,"%s",phrases[i].TextSpeak);
}
printf("%s - %s\n", phrases[i].English, phrases[i].TextSpeak);
}
fclose(infile);
for(int i=0; i < phraseCounter; i++)
free(phrases[i].English);
free(phrases);
The output i keep getting is:
nevermind - atm
by - definitely
def -
-
-
-
-
-
And it keeps going for 75 lines.
Now I'm not sure whether I should use a 2D array or if this will be acceptable. Any help will be appreciated! Thanks
phrases[i].English = malloc(sizeof(char));
Here the problem lies, you are allocating a single byte and then trying to cram a string into it, which leads to undefined behavior here:
fscanf(infile,"%s", phrases[i].English);
Generally, you should assume a sensible buffer length and read that, and check whether the new line is contained. If that's not the case, read again into another buffer or enlarge the old one (using realloc).
Either that or use a non-standard function like getline that does this already for you under the hood.
Given that your lines are pretty short sentences, a constant buffer size could suffice (let's call it MAX_LINE), which provides us a little easier way to achieve the same:
fscanf(infile, "%*[^\n]s", MAX_LINE, buf);
This reads a string of length MAX_LINE into the buffer buf and terminates before the '\n' is encountered.
When reading strings, you should refrain from using fscanf("%s", buf) and use fgets() or scanf("%*s", MAX_LINE, ...) instead. This ensures that there will be no attempt to write more data than what you specified, avoiding buffer overflows.
Edit: The nested loop shouldn't be there. You are basically overwriting phrases[i].TextSpeak a total of phraseCounter times for no benefit. And in the process of that you are leaking a lot of memory.
There are a number of ways to do this. However, one suggestion I would have would be to make use of line-input tools such as fgets or getline to make reading the file more robust. It is fine to use fscanf for discrete variables (I left it for reading phraseCounter), but for reading string data of unknown length/content, line-input really should be used.
Below is an example of your code with this employed. The code is commented to explain the logic. The real logic here is the fact that you will read 2-lines for each allocated struct. A simple line counter using % (mod) as a toggle can help you keep track of when to allocate a new struct. I also added code to accept the filename as the first argument to the program. (to run, e.g ./progname <filename>). Let me know if you have questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 128
typedef struct Pair
{
char *English;
char *TextSpeak;
} Pair;
int main (int argc, char** argv) {
if (argc < 2) {
fprintf (stderr, "error: insufficient input. Usage: %s filename\n", argv[0]);
return 1;
}
Pair **pair = NULL; /* pointer to array of pointers */
char line[MAXL] = {0}; /* variable to hold line read */
FILE* infile = NULL; /* file pointer for infile */
unsigned int phraseCounter = 0; /* count of pairs */
unsigned int index = 0; /* index to pairs read */
size_t nchr = 0; /* length of line read */
size_t lnum = 0; /* line number read */
/* open file and validate */
if (!(infile = fopen ((argv[1]), "r"))) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* read phraseCounter */
if (!fscanf (infile, "%u%*c", &phraseCounter)) {
fprintf (stderr, "error: failed to read phraseCounter.\n");
return 1;
}
/* allocate phraseCounter number of pointers to Pair */
if (!(pair = calloc (phraseCounter, sizeof *pair))) {
fprintf (stderr, "error: memory allocation failed.\n");
return 1;
}
/* read each line in file */
while (fgets (line, MAXL - 1, infile) != NULL)
{
nchr = strlen (line); /* get the length of line */
if (nchr < 1) /* if blank or short line, skip */
continue;
if (line[nchr-1] == '\n') /* strip newline from end */
line[--nchr] = 0;
if (lnum % 2 == 0) /* even/odd test for pair index */
{
/* allocate space for pair[index] */
if (!(pair[index] = calloc (1, sizeof **pair))) {
fprintf (stderr, "error: memory allocation failed for pair[%u].\n", index);
return 1;
}
pair[index]-> English = strdup (line); /* allocate space/copy to English */
}
else
{
pair[index]-> TextSpeak = strdup (line);/* allocate space/copy to TextSpeak */
index++; /* only update index after TextSpeak read */
}
lnum++; /* increment line number */
}
if (infile) fclose (infile); /* close file pointer after read */
/* print the pairs */
printf ("\n Struct English TextSpeak\n\n");
for (nchr = 0; nchr < index; nchr++)
printf (" pair[%3zu] %-24s %s\n", nchr, pair[nchr]-> English, pair[nchr]-> TextSpeak);
/* free memory allocated to pair */
for (nchr = 0; nchr < index; nchr++)
{
if (pair[nchr]-> English) free (pair[nchr]-> English);
if (pair[nchr]-> TextSpeak) free (pair[nchr]-> TextSpeak);
if (pair[nchr]) free (pair[nchr]);
}
if (pair) free (pair);
return 0;
}
Input
$ cat dat/pairs.txt
10
nevermind
nvm
not much
nm
no problem
np
people
ppl
talk to you later
ttyl
because
cuz
i don't know
idk
as soon as possible
asap
yeah
ya
how are you
hru
Output
$ ./bin/struct_rd_pairs dat/pairs.txt
Struct English TextSpeak
pair[ 0] nevermind nvm
pair[ 1] not much nm
pair[ 2] no problem np
pair[ 3] people ppl
pair[ 4] talk to you later ttyl
pair[ 5] because cuz
pair[ 6] i don't know idk
pair[ 7] as soon as possible asap
pair[ 8] yeah ya
pair[ 9] how are you hru
Verify no memory leaks
$ valgrind ./bin/struct_rd_pairs dat/pairs.txt
==3562== Memcheck, a memory error detector
==3562== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3562== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3562== Command: ./bin/struct_rd_pairs dat/pairs.txt
==3562==
<snip>
==3562==
==3562== HEAP SUMMARY:
==3562== in use at exit: 0 bytes in 0 blocks
==3562== total heap usage: 32 allocs, 32 frees, 960 bytes allocated
==3562==
==3562== All heap blocks were freed -- no leaks are possible
==3562==
==3562== For counts of detected and suppressed errors, rerun with: -v
==3562== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)