Read lines of text file into array, using double pointer and malloc - c

I want to read lines of a text file, and the content of it is as below.
first
second
third
I've already written some code, but the result was different from what I expected. I hope you can help me a little. (code below)
/*
content of test.txt
first
second
third
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// double pointer for two string lines
// and the two string lines will be pointed by (char *) pointer
FILE *fp = fopen("test.txt", "r");
char **lst = malloc(3 * sizeof(char *)); // corrected 2 -> 3
// read lines until feof(fp) is not Null
// Only 2 lines will be read by this loop
int i = 0;
while (!feof(fp)) {
char line[10];
fgets(line, 10, fp);
*(lst + i) = line;
printf("%d : %s", i, *(lst + i));
i++;
}
/*
Result:
0 : first
1 : second
2 : third // Why was this line printed here?
There are 3 lines, not 2 lines!
*/
printf("\n\n");
int j;
for (j = 0; j < i; j++) {
printf("%d : %s\n", j, *(lst + j));
}
/*
result:
0 : third
1 : third
2 : third
The whole lines were not printed correctly!
*/
free(lst);
return 0;
}
Expected output:
0 : first
1 : second
2 : third
Many thanks.

First and foremost, you are allocating space for an array of two char*s and you have a single statically sized buffer for a string. But you’re attempting to read three strings. Where do you think the space for the strings is coming from? You’re not allocating it.
You need to make your various numbers match up: allocate an array of three strings, and then allocate three string buffers:
char **lst = malloc(3 * sizeof *lst);
for (int i = 0; i < 3; i++) {
lst[i] = malloc(10);
fgets(lst[i], 10, fp);
}
And don’t forget to free all allocated buffers subsequently:
for (int i = 0; i < 3; i++) {
free(lst[i]);
}
free(lst);
… of course this code isn’t terribly great either since it hard-codes the number of lines you can read, and the maximum line length. But it should get you started.

Related

c program to read file and count words specified in array

I'm trying to read a file containing a paragraph, count the number of times specific words occur (words that I have specified and stored in an array) and then print that result to another file that would look something like,
systems, 2
computer, 3
programming, 6
and so on. Currently, all this code does is spit out every word in the paragraph and their respective counts. Any help would be much appreciated.
#include <stdio.h>
#include <string.h>
int main()
{
FILE* in;
FILE* out;
char arr1[13][100] = { "systems", "programming", "computer", "applications", "language", "machine"};
int arr2[180] = {0};
int count = 0;
char temp[150];
in = fopen("out2.dat", "r");
out = fopen("out3.dat", "w");
while (fscanf(in, "%s", temp) != EOF)
{
int i, check = 8;
for (i = 0;i < count;i++)
{
if (strcmp(temp, arr1[i]) == 0)
{
arr2[i]++;
check = 1;
break;
}
}
if (check == 1) continue;
strcpy(arr1[count], temp);
arr2[count++] = 1;
}
int i;
for (i = 0; i < count; i++)
fprintf(out, "%s, %d\n", arr1[i], arr2[i]);
return 0;
}
The use of count does not make much sense throughout this program.
It is declared as int count = 0;, and then used as the upper bound in this loop
for (i = 0; i < count; i++)
limiting which search words will be used. This also means that this loop will not be entered on the first iteration of the surrounding while loop.
As such, check != 1, so after this count is used as the index in arr1 at which the currently read "word" will be copied into
strcpy(arr1[count], temp);
which makes absolutely no sense. Why overwrite data you are searching for?
Then count is incremented to 1 after being used to set the first element of arr2 to 1.
On the second iteration of the while loop, the for loop will run for exactly one iteration, comparing the newly read "word" (temp) against the first element of arr1 (which is now the last "word" read).
If this matches: the first element in arr2 is incremented from 1 to 2, the string copy is skipped, and count is not incremented.
If this does not match, the new "word" is copied into the second element of arr1, the second element of arr2 is set to 1, and count is incremented to 2.
This spirals out of control from here.
Given the input shown above, this accesses arr1 out-of-bounds when count reaches 13.
With files that have a small selection of data (<= 13 unique "words", lengths < 100), this may accidentally "work" by populating arr1 with the words from the file. This will have the end effect of showing you the counts of each "word" in the input file.
Eventually, you will invoke Undefined Behavior when one of the following occurs:
fscanf(in, "%s", temp) reads a string that overflows the temp buffer.
count exceeds the bounds of arr1 or arr2.
strcpy(arr1[count], temp); copies a string that overflows a buffer in arr1.
Either fopen fail.
In addition to being unsafe, fscanf(in, "%s", temp) will consider anything other than whitespace as being part of a valid string. This includes trailing punctuation, which may or may not be an issue depending on which tokens you want to match (systems. vs. systems). You may need more robust parsing.
In any case, either create an array of structures composed of search words and frequencies, or, create two arrays of the same length to represent this data:
const char *words[6] = { "systems", "programming", "computer", "applications", "language", "machine"};
unsigned freq[6] = { 0 };
There is no need to copy anything. Remember to check if fopen fails, and to limit %s when reading as not to overflow the input buffer.
The rest of the program looks similar: test each input "word" against all search words; increment the corresponding frequency if a match.
An example using an array of structures:
#include <stdio.h>
#include <string.h>
int main(void) {
struct {
const char *word;
unsigned freq;
} search_words[] = {
{ "systems", 0 },
{ "programming", 0 },
{ "computer", 0 },
{ "applications", 0 },
{ "language", 0 },
{ "machine", 0 }
};
size_t length = sizeof search_words / sizeof *search_words;
FILE *input_file = fopen("out2.dat", "r");
FILE *output_file = fopen("out3.dat", "w");
if (!input_file || !output_file) {
fclose(input_file);
fclose(output_file);
fprintf(stderr, "Could not access files.\n");
return 1;
}
char word[256];
while (1 == fscanf(input_file, "%255s", word))
for (size_t i = 0; i < length; i++)
if (0 == strcmp(word, search_words[i].word))
search_words[i].freq++;
fclose(input_file);
for (size_t i = 0; i < length; i++)
fprintf(output_file, "%s, %u\n",
search_words[i].word,
search_words[i].freq);
fclose(output_file);
}
cat out3.dat:
systems, 1
programming, 1
computer, 2
applications, 2
language, 1
machine, 1

HEAP CORRUPTION DETECTED: after normal block(#87)

I'm trying to do a program that get number of names from the user, then it get the names from the user and save them in array in strings. After it, it sort the names in the array by abc and then print the names ordered. The program work good, but the problem is when I try to free the dynamic memory I defined.
Here is the code:
#include <stdio.h>
#include <string.h>
#define STR_LEN 51
void myFgets(char str[], int n);
void sortString(char** arr, int numberOfStrings);
int main(void)
{
int i = 0, numberOfFriends = 0, sizeOfMemory = 0;
char name[STR_LEN] = { 0 };
char** arrOfNames = (char*)malloc(sizeof(int) * sizeOfMemory);
printf("Enter number of friends: ");
scanf("%d", &numberOfFriends);
getchar();
for (i = 0; i < numberOfFriends; i++) // In this loop we save the names into the array.
{
printf("Enter name of friend %d: ", i + 1);
myFgets(name, STR_LEN); // Get the name from the user.
sizeOfMemory += 1;
arrOfNames = (char*)realloc(arrOfNames, sizeof(int) * sizeOfMemory); // Change the size of the memory to more place to pointer from the last time.
arrOfNames[i] = (char*)malloc(sizeof(char) * strlen(name) + 1); // Set dynamic size to the name.
*(arrOfNames[i]) = '\0'; // We remove the string in the currnet name.
strncat(arrOfNames[i], name, strlen(name) + 1); // Then, we save the name of the user into the string.
}
sortString(arrOfNames, numberOfFriends); // We use this function to sort the array.
for (i = 0; i < numberOfFriends; i++)
{
printf("Friend %d: %s\n", i + 1, arrOfNames[i]);
}
for (i = 0; i < numberOfFriends; i++)
{
free(arrOfNames[i]);
}
free(arrOfNames);
getchar();
return 0;
}
/*
Function will perform the fgets command and also remove the newline
that might be at the end of the string - a known issue with fgets.
input: the buffer to read into, the number of chars to read
*/
void myFgets(char str[], int n)
{
fgets(str, n, stdin);
str[strcspn(str, "\n")] = 0;
}
/*In this function we get array of strings and sort the array by abc.
Input: The array and the long.
Output: None*/
void sortString(char** arr, int numberOfStrings)
{
int i = 0, x = 0;
char tmp[STR_LEN] = { 0 };
for (i = 0; i < numberOfStrings; i++) // In this loop we run on all the indexes of the array. From the first string to the last.
{
for (x = i + 1; x < numberOfStrings; x++) // In this loop we run on the next indexes and check if is there smaller string than the currnet.
{
if (strcmp(arr[i], arr[x]) > 0) // If the original string is bigger than the currnet string.
{
strncat(tmp, arr[i], strlen(arr[i])); // Save the original string to temp string.
// Switch between the orginal to the smaller string.
arr[i][0] = '\0';
strncat(arr[i], arr[x], strlen(arr[x]));
arr[x][0] = '\0';
strncat(arr[x], tmp, strlen(tmp));
tmp[0] = '\0';
}
}
}
}
After the print of the names, when I want to free the names and the array, in the first try to free, I get an error of: "HEAP CORRUPTION DETECTED: after normal block(#87)". By the way, I get this error only when I enter 4 or more players. If I enter 3 or less players, the program work properly.
Why does that happen and what I should do to fix it?
First of all remove the unnecessary (and partly wrong) casts of the return value of malloc and realloc. In other words: replace (char*)malloc(... with malloc(..., and the same for realloc.
Then there is a big problem here: realloc(arrOfNames, sizeof(int) * sizeOfMemory) : you want to allocate an array of pointers not an array of int and the size of a pointer may or may not be the same as the size of an int. You need sizeof(char**) or rather the less error prone sizeof(*arrOfNames) here.
Furthermore this in too convoluted (but not actually wrong):
*(arrOfNames[i]) = '\0';
strncat(arrOfNames[i], name, strlen(name) + 1);
instead you can simply use this:
strcpy(arrOfNames[i], name);
Same thing in the sort function.
Keep your code simple.
But actually there are more problems in your sort function. You naively swap the contents of the strings (which by the way is inefficient), but the real problem is that if you copy a longer string, say "Walter" into a shorter one, say "Joe", you'll write beyond the end of the allocated memory for "Joe".
Instead of swapping the content of the strings just swap the pointers.
I suggest you take a pencil and a piece of paper and draw the pointers and the memory they point to.

Attempting to read in a file and storing the first input in an array val and the second input in an array wt (weight)

I need to read in a file called "data.txt" and store the first input as a value and the second corresponding input as a weight. I'm having issues reading them in and storing the values.
data.txt (example)
3 25
2 20
1 15
4 40
5 50
This is what I´ve started with:
FILE *myFile;
myFile=fopen("data.txt", "r");
int val[20]={0}; //initialize value array to zero
int wt[20]={0};
int W=80; //Set capacity to 80
int i;
int n;
while(!feof(myFile)){
fscanf(myFile, "%1d%1d", &val[i], &wt[i]);
}
n = sizeof(val)/sizeof(val[0]);
printf("%d", knapSack(W, wt, val, n));//prints out the maximum value
fclose(myFile);
return 0;
I've edited the above code to the following:
FILE *myFile;
myFile=fopen("data.txt", "r");
int val[20]={0};
int wt[20]={0};
int W=80; //Set capacity to 80
int i;
int n;
for(i=0;i<sizeof(val);i++){
fscanf(myFile, "%1d%1d", &wt[i],&val[i]);
}
n = sizeof(val)/sizeof(val[0]);
printf("%d", knapSack(W, wt, val, n));//prints out the maximum value
fclose(myFile);
return 0;
It keeps outputting 55 when I use the inputs from the data.txt example.
The biggest problem you are having is you are not controlling your read-loop with the return of the read itself. For example, in your case you would want:
int i = 0;
while (fscanf(myFile, "%1d%1d", &wt[i],&val[i]) == 2)
i++;
At the end of your read, i would hold the number of elements read into your arrays.
(note: you cannot use any input function correctly unless you check the return...)
Instead of reading the values into separate arrays, whenever you are coordinating multiple values as a single object (e.g. each val and wt pair), you should be thinking struct. That allows you to coordinate both values as a single object.
A simple example in your case could be:
#include <stdio.h>
#define MAXVAL 20 /* if you need a constant, #define one (or more) */
typedef struct { /* struct with int val, wt + typdef for conveninece */
int val, wt;
} mydata;
int main (int argc, char **argv) {
size_t n = 0; /* number of elements read */
mydata arr[MAXVAL] = {{ .val = 0 }}; /* array of mydtata */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* read all pairs of values in file into array */
while (fscanf (fp, "%d %d", &arr[n].val, &arr[n].wt) == 2)
n++;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < n; i++) /* output values */
printf ("arr[%zu] %2d %2d\n", i, arr[i].val, arr[i].wt);
}
Above, the code does the same as I suggested in conditioning the read-loop on successfully reading a pair of values from the file. The only difference is that is coordinates the val and wt values in a struct.
Example Use/Output
With your data in the file dat/val_wt.txt, you would receive the following output:
$ ./bin/read_val_wt dat/val_wt.txt
arr[0] 3 25
arr[1] 2 20
arr[2] 1 15
arr[3] 4 40
arr[4] 5 50
While above we read directly with fscanf, you can make your read a bit more robust by reading each line into a character array first, and then parsing the wanted values from the character array with sscanf. You are essentially doing the same thing, but by using fgets/sscanf you can make an independent validation of (1) the read of the line; and (2) the parse of the wanted information from the line. If you have a malformed-line, it prevents the matching-failure from impacting the read of the remaining lines in the input file.
Look things over and let me know if you have further questions.
Oops, many little problems here...
First even if unrelated, you consistenly fail to check the result of input functions. It can lead to hide problems...
Next, the rule is when you do not get what you would expect, trace intermediary values.
Had you happen those lines:
// uncomment next block for debugging
printf("n=%d\n);
for (i = 0; i < n; i++) {
printf("%d %d\n", wt[i], val[i]);
}
You would have seen
n = 20
3 2
5 2
2 0
1 1
5 4
4 0
5 5
0
showing that:
n was 20 (unsure whether you expected it)
you had read your values one digit at a time instead of one integer value (because of the %1d formats)
My advice:
for (i = 0; i<sizeof(val); i++) { // do not try to read more than array capacity
if (2 != fscanf(myFile, "%d%d", &wt[i], &val[i])) break; // stop when no more data
}
n = i; // number of actual values
// uncomment next block for debugging
/*
printf("n=%d\n);
for (i = 0; i < n; i++) {
printf("%d %d\n", wt[i], val[i]);
}
*/

C Flatten 2-D Array of Char* to 1-D

Say I have the following code:
char* array[1000]; // An array containing 1000 char*
// So, array[2] could be 'cat', array[400] could be 'space', etc.
Now, how could I flatten this array into 1-D? Is it possible to do this such that new_1D_array[2] would still be 'cat', new_1D_array[400] would still be 'space', etc.?
You have a one-dimensional array of type pointer-to-char, with 1000 such elements. It's already 1D as far as arrays go, though it could be interpreted as a "jagged 2D array". If you want to convert this into one massive character array, you could do something like so, which requires calculating the size of the buffer you'll need to store it, and then flattening the array.
Note that if you opt to use malloc instead of calloc, you'll have to manually set the last character to '\0' or 0 so that the final result is a NULL-delimited C-style string, and not just a character array, as the latter of the two won't work with C string operations, and the former will.
Code Listing
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
const char* array[] = { "one", "two", "three", "four" };
size_t length = sizeof(array) / sizeof(char*);
int i;
int sum = 0;
int count = 0;
for (i=0; i<length; i++) {
printf("Element #%d - %s\n", i, array[i]);
sum += strlen(array[i]) + 1;
}
sum++; // Make room for terminating null character
char* buf;
if ((buf = calloc(sum, sizeof(char))) != NULL) {
for (i=0; i<length; i++) {
memcpy(buf+count, array[i], strlen(array[i]));
count += strlen(array[i]) + 1;
buf[count-1] = '#';
}
printf("Output Buffer: %s\n", buf);
printf("Buffer size:%d - String Length:%d\n", sum, strlen(buf));
free(buf);
buf = NULL;
}
return 0;
}
Sample Output
Element #0 - one
Element #1 - two
Element #2 - three
Element #3 - four
Output Buffer: one#two#three#four#
Buffer size:20 - String Length:19
The above answer beat me to the puch and looks great, but I'll finish my thoughts here.
In this version every 11th byte of flatArray will contain the start of the strings from the original array, making it very easy to find the original strings from the ones before - i.e. string index 2 would start at 2*11 == index 22 and string 400 would start at 400 * 11 == index 4400. In this way, you would not need to iterate through the flat array counting terminators to find your old strings. The cost of this being that the flat array takes a bit more memory than the original.
char* array[1000];
// Malloc new buffer
char *flatArray = malloc(length * 1100);
for(i=0; i<1000; i++)
{
strncpy(&flatArray[i * 11], array[i], 10);
flatArray[i * 11 + 10] = '\0';
}

Print dynamically allocated array of integers on single line

I'm trying to do the following:
Open a file with up to 1000 ints in a text file, 1 int per line.
Read the file line by line
Store the ints in a dynamically allocated array of 1000
Print the contents of the array on a single line
Yes this is homework, I'm stuck on this.
Output should be:
$ 1 2 3 4 5 6 7 8 9 ....
What I have so far prints the integers on a new line after each iteration.
int x = 0;
char buf[1000];
int *array = (int *) malloc(1000 * sizeof(int));
FILE *fp = fopen("test.txt", "r");
while(fgets(buf, 1000, fp) != NULL) {
array[x] = buf;
printf("%s ", array[x]);
x++;
}
fclose(fp);
return 0;
}
Your code puts the buffer address in the integer array and then uses that as a string pointer for printf(), but you'll find it won't work if you try printing the array in a loop separate from the reading loop, because every element of the array holds this same buffer address. You should have been given a compiler warning about this.
This answer uses two loops, as you want to print the numbers after reading them.
while(fgets(buf, 1000, fp) != NULL && x < 1000)
array[x++] = atoi(buf);
for (y=0; y<x; y++)
printf("%d ", array[y]);
printf("\n");

Resources