How to read Text Files into 2d Arrays? - arrays

Alright, I'm making a grading program/code that will have its own text file where it stores all the grades. And I thought of making a 2d array where the first "dimension" will be the student and second "dimension" the individual grade (if there's a smarter way of doing grades tell me, by the way I chose this method because it is the only way I know how I could later on just add more students or more grades) keep in mind that the number of grades and students isn't always set so there's no easy way out. Anyways I've tried something, and I think it only works with characters and not with integers (even though the grades will be 1-5).Also I want a way to print it out but I think this is the bigger problem. Anyways THANKS.
typedef char string [20];
string row;
int i=0,j=0;
char arr[20][20];
FILE *fp;
fp=fopen("grades.txt","r");
for(i=0;arr[i-1][j]!=EOF;i++)
{
fgets(row,sizeof(row),fp);//I used fgets so I could get the size of the line
for(j=0;j<strlen(row);j++)
{
fscanf(fp,"%c ",&arr[i][j]);
}
}
I don't know if it will help but I thought the text file would look something like this:
54455
43544
22443
21232
21121

fgets reports if it worked or not. So, reading till end of file or till the buffer is full:
for(i = 0; i < sizeof arr / sizeof *arr && fgets(arr[i], sizeof arr[i], fp); i++)
{
// probably remove the \n that fgets writes into the buffer
// otherwise nothing else to do
}
Site notes:
fgets reads from the file, no need for additional reads with fscanf
fgets reads a line including the newline character, remove it if you don't want it
you need to check if fopen worked

I found out the best way to store grades of students in struct. Every student in general has first name, last name, grades,... You can add whatever you want. I am just fine with fname, lname, grades.
typedef struct student_s {
char fname[25];
char lname[25];
int* grades;
int count_of_grades; // Track number of grades for each student
} student_t;
By allocating a dynamic array of student_t you can get as many students as you want.
// Allocate array of structs
student_t* students = (student_t *) malloc(sizeof(student_t));
By using getline() you can read the whole line from file at once (line ends with \n). getline() is not a standart C function therefore you need to put #define _GNU_SOURCE at the beginning of your script.
while ((read_len = getline(&line, &len, fp)) != -1)
Every time function getline() reads next line of file, the array size count will be incremented and reallocated array.
++count;
// Increase size of array beacause of new student to add
students = realloc(students, sizeof(student_t) * count);
if (students == NULL)
{
printf("Couldn't allocated memmory\n");
return 1;
}
Next step is to allocate grades array which will store all grades of specific student. Looping through line you can extract each grade. Then by just defining members of struct you can add grades for each student.
// Allocate array to store all grades from file for one student
// Count of grades does not have to be the same for every student
students[index].grades = (int *) malloc(sizeof(int) * (read_len-1));
// Iterate grades read from file
for (int i=0; i<read_len-1; ++i)
{
// char --> char *
char grade[2] = "\0";
grade[0] = line[i];
// Add grade to the array of grades
students[index].grades[i] = atoi(grade);
}
At the end you should store number of grades are in array for a simple manipulation with data later in your script.
// Track number of grades
students[index].count_of_grades = read_len-1;
++index
Full code:
#define _GNU_SOURCE // necessery to use getline()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student_s {
char fname[25];
char lname[25];
int* grades;
int count_of_grades;
} student_t;
int main(int argc, char const *argv[])
{
// Allocate array of structs
student_t* students = (student_t *) malloc(sizeof(student_t));
int count = 0;
int index = 0;
FILE* fp;
char* line = NULL;
size_t len = 0;
ssize_t read_len;
fp = fopen("data.txt", "r");
if (fp == NULL)
{
return 1;
}
// Read line by line from file until fp reaches end of file
while ((read_len = getline(&line, &len, fp)) != -1)
{
++count;
// Increase size of array beacause of new student to add
students = realloc(students, sizeof(student_t) * count);
if (students == NULL)
{
printf("Couldn't allocated memmory\n");
return 1;
}
// Replace with your code, which adds name to struct or get rid of it (also from struct)
memcpy(students[index].fname, "John", 4);
memcpy(students[index].lname, "Wash", 4);
// Allocate array to store all grades from file for one student
// Count of grades does not have to be the same for every student
students[index].grades = (int *) malloc(sizeof(int) * (read_len-1));
// Iterate grades read from file
for (int i=0; i<read_len-1; ++i)
{
// char --> char *
char grade[2] = "\0";
grade[0] = line[i];
// Add grade to the array of grades
students[index].grades[i] = atoi(grade);
}
// Track number of grades
students[index].count_of_grades = read_len-1;
++index;
}
fclose(fp);
if (line)
{
free(line);
}
// Print data from structs
for (int i=0; i<count; ++i)
{
printf("%s: ", students[i].fname);
for (int j=0; j<students[i].count_of_grades; ++j)
{
printf("%d ", students[i].grades[j]);
}
printf("\n");
}
return 0;
}

Related

C / Getting line string from text file and storing them in array/pointer

I want to get all lines from the text file and store them in my char** pointer (array of strings). The problem is that when I try to set indices for pointer's strings, the program assigns the last scanned sentence for all indices.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 10000
int main()
{
FILE *fp = NULL;
char line[MAX_LINE];
char** lines = (char**) malloc(10000*200*sizeof(char));
int count = 0;
fp = fopen("test.txt","r");
while(fgets(line,10000,fp)) {
lines[count] = line;
count++;
}
fclose(fp);
for(int i =0; i<2000;i++){
printf("%s",lines[i]);
}
return 0;
}
lets assume test.txt is like this:
Alice was beginning to get very tired of sitting by her sister on the
bank, and of having nothing to do: once or twice she had peeped into the
book her sister was reading, but it had no pictures or conversations in
it, and what is the use of a book, thought Alice without pictures or
conversations?
When I print like this, every time I get the last sentence (in this case conversations? ) in my text file. However, I want to set every scanned sentence from the text file to the different index in my char**. For example, I want to set like this:
lines[0] gives "Alice was beginning to get very tired of sitting by her sister on the"
lines[1] gives "bank, and of having nothing to do: once or twice she had peeped into the"
and so on.
You can't copy characters from one string buffer to another simply by assigning a pointer (all that does is to make the destination point to the source, as you have noticed).
Instead, you must actually copy the characters, using the strcpy function. So, instead of:
lines[count] = line; // Just makes each pointer point to the same buffer
use:
strcpy(lines[count], line); // Copies the CURRENT contents of "line"
You also have a severe problem in the way you are using your char** lines buffer. If you want an array of 200 lines, each with a maximum length of 10000 characters, you should allocate them as follows:
char** lines = malloc(200 * sizeof(char*)); // Make 200 pointers
// Now allocate 10000 chars to each of these pointers:
for (int i = 0; i < 200; ++i) lines[i] = malloc(10000 * sizeof(char));
Note: The 200 buffers will be uninitialized (contain random data) so, in your print loop, you should only use those you have copied real data to, using the count variable as the loop limit:
for(int i = 0; i < count; i++) {
printf("%s", lines[i]);
}
Also, don't forget to free the memory allocated when you're done:
for (int i = 0; i < 200; ++i) free(lines[i]); // Free each line buffer...
free(lines); // ... then free the array of pointers itself
strdup resolve the issue, free resources as said by Adrian when finished.
int main()
{
FILE *fp = NULL;
char line[MAX_LINE];
char** lines = (char**) malloc(10000*200*sizeof(char));
int count = 0;
fp = fopen("test.txt","r");
while(fgets(line,10000,fp)) {
lines[count] = strdup(line);
count++;
}
fclose(fp);
for(int i =0; i<count;i++){
printf("%s",lines[i]);
}
for (int i = 0; i < count; ++i) free(lines[i]);
free(lines);
return 0;
}
If you are looking for better performance look at my repo (https://github.com/PatrizioColomba/strvect)

Can't scan in .txt file using fgets into flexible data structure

I have a homework task that requires me to process .txt files by scanning them into a flexible data structure and then searching the files for words with capital letters. I'm having issues scanning them in this flexible data structure I'm using. The reason that the data structure needs to be flexible is that it needs to be able to process any .txt files.
The data structure I want to use is an array that points to arrays that contains the content of the line. I'm open to using a different structure if it's easier.
I've tried to scan it in line by line using fgets, and using malloc to allocate just enough to store the line, but it doesn't seem to work.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STEPSIZE 100
int main()
{
FILE *inputFile;
//Opens the file in read mode
inputFile = fopen("testfile.txt", "r");
//Error message if file cannot open
if (inputFile == NULL)
{
printf("Unable to open file");
return 1;
}
int arrayLen = STEPSIZE;
// Allocate space for 100 lines. The **lines is the data structure used to store all the lines
char **lines = (char **)malloc(STEPSIZE * sizeof(char*));
char buffer[3000];
int i = 0;
while (fgets(buffer, 3000, inputFile))
{
//Checks if the array is full, and extends it
if(i == arrayLen)
{
arrayLen += arrayLen;
char ** newLines = realloc(lines, 200 * sizeof(char*));
if(!newLines)
{
printf("cant realloc\n");
}
lines= newLines;
}
// Get length of buffer
int lengthOfBuffer = strlen(buffer);
//Allocate space for string. The +1 is for the terminating character
char *string = (char *)malloc((lengthOfBuffer + 1) * sizeof(char));
//copy string from buffer to string
strcpy(string, buffer);
//Attach string to data structure
lines[i] = string;
//Increment counter
i++;
printf("%s", lines[i]);
}
//Closes the file
fclose(inputFile);
for (int j = 0; j < 100; j++){
printf("%s \n", lines[i]);
}
return 0;
}
When the final for loop runs, ideally the contents of the file gets printed, just to show that it has been stored and is able to be processed, but currently i get exit code 11.
Any help would be appreciated.
There is a problem is here:
//Increment counter
i++;
printf("%s", lines[i]); // you're printing the next file that does not yet exist
Correct code:
printf("%s", lines[i]);
//Increment counter
i++;
And another one here:
for (int j = 0; j < 100; j++) { // your loop variable is j
printf("%s \n", lines[i]); // but you use i here.
}
Correct code:
for (int i = 0; i < 100; i++) {
printf("%s \n", lines[i]);
}
And still another one here:
arrayLen += arrayLen;
char ** newLines = (char**)realloc(lines, 200 * sizeof(char*));
// here the new length of your array is inconditionally 200
// but actually the new array length is arrayLen
Correct code:
arrayLen += arrayLen;
char ** newLines = (char**)realloc(lines, arrayLen * sizeof(char*));
There may be more problems though, I didn't check everything.
BTW: sizeof(char) is 1 by definition, so you can just drop it.
BTW2: arrayLen += arrayLen; are you sure this is what you want? You double the size of your array each time. This is not necessarily wrong but using this method the array length will very quickly grow to a very big number. You probably wanted this: arrayLen += STEPSIZE;
BTW3:
while (fgets(buffer, 3000, inputFile))
this is not actually wrong, but you'd better write this:
while (fgets(buffer, sizeof buffer, inputFile))
which eliminates one of the two hard coded constants 3000.
BTW4: at the end you only print the first 100 lines yo've read. You should be able to correct this yorself.
BTW5: you should also free all the memory you have allocated. I leave this as an exercise to you. Hint: it's about three lines of code to add at the end of main.

Trouble loading input into vector type function in C

Ive created a vector in C that holds chars. The point is to ask a user for input from keyboard and have them input one char at a time, then add it to the vector using calloc.It currently does not load anything and quits after any input. I want to be able to keep adding characters until the user inputs return in place of a character then, the program will turn the characters into a string and append to text that is preset. After it will print out the result. My issue is that my program won't load the user input into the vector array. The final result should look like HW4 Input: 123456
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//declare a new vector structure
int arraySize =1;
char *vector(char *Array){
arraySize++;
void *temp=(char*) calloc(arraySize, sizeof(char));
Array= (char*) temp;
return Array;
}
int main(void){
int i, m;
int h=0;
int index=0;
char c;
char *array = (char *) calloc(arraySize, sizeof(char));
array[0] = '\0';
char ch[]= "HW4 input: ";
printf("\nEnter a single character to add to the string\n");
printf("Enter one character at a time, when done press enter without any other input.\n");
while((c=getc(stdin))!='\n'){
array[index]=c;
index++;
if(index<arraySize){
char *temp=(char*) calloc(arraySize, sizeof(char));
memcpy(temp,array, arraySize);
array=vector(array);
memcpy(array, temp, arraySize);
}
}
arraySize++;
array=vector(array);
index++;
array[index]='\0';
char output[arraySize+14];
for (i=0; i<strlen(ch); i++){
output[i]= ch[i];
}
for(m=i; m<strlen(output); m++){
output[m]=array[h];
}
printf("\n%c\n", array[0]);
free(array);
return 0;
}
In your code you write
index++;
if (index<arraySize) {
...
however at start arraySize==1 so that condition will never be true.
Allocating memory is a costly operation therefore to allocate every byte and then copy over the contents is not effective.
Instead allocate say 100 characters and then if the user input is longer add another 100 character. The most convenient way to do this is to use realloc.
e.g.
if (index < arraySize)
{
char* tmp = realloc(array, arraySize + 100)
if (tmp != NULL)
{
array = tmp;
arraySize += 100;
}
else
{
fprintf(stderr, "out of memory");
exit(EXIT_FAILURE);
}
}
you can always reallocate again with the correct size once the user is finished with his input to get the right buffer size.

How to read pointer values into an array of structs

I have following struct
typedef struct
{
char* city;
int temp;
} Place;`
I am attempting to read in two values from a line into an array of structs.
The lines look like:
Los Angeles; 88
I am attempting to read data into the array. Assuming my memory allocation is correct what is the correct way to read in these values.
my code
void readData(FILE** fpData, FILE** fpOutput)
{
char s[100];
int index = 0;
Place *values;
values=malloc(size * sizeof(Place));
if (values == NULL)
{
MEM_ERROR;
exit(1);
}
for (int a = 0; a < size; a++)
{
(values[a]).city = (char *) malloc(100 * sizeof(char));
if(values[a].city == NULL)
{
MEM_ERROR;
exit(100);
}
}
while(fgets(s, sizeof(s), *fpData)!=NULL)
{
sscanf(s, "%[^:]%*c%d\n", values[index].city, &values[index].temp);
index++;
}
sortInsertion(values, size, fpOutput);
free(values);
return;
}
The city is not going into the array so I am assuming the part where it says values[index].city is incorrect.
How can I fix this ?
Your data using semicolon ; while your sscanf format using colon :, make sure this is same character. If your data really use semicolon, change %[^:] part in the sscanf format to %[^;]
Here my code and how I run it to show you that it works:
#include <stdio.h>
struct Place {
char city[100];
int temp;
} values[30];
int main() {
char s[100];
int i=0, n=0;
while ( fgets(s, sizeof(s), stdin) != NULL ) {
sscanf(s, "%[^;]%*c%d\n", values[n].city, &values[n].temp);
n++;
}
printf("n=%d\n", n);
for ( i=0; i<n; i++ ) {
printf("values[%d] = (%s, %d)\n", i, values[i].city, values[i].temp);
}
}
This is how I run it on Linux:
% for a in `seq 1 3`; do echo "City-$a; $a$a"; done | ./a.out
n=3
values[0] = (City-1, 11)
values[1] = (City-2, 22)
values[2] = (City-3, 33)
sscanf(s, "%[^:]%*c%d\n", values[index].city, &values[index].temp);
This will copy everything from the start of the line read up to the first colon (:) into the city array you allocated. Your example input would seem to have a semi-colon (;), so you'll get the entire line in the city array and nothing in the temp field.
You don't do any input checking, so any too-long input line will get split into multiple (probably corrupted) cities. You'll also have problems if there are not exactly size lines in the file, as you don't check to make sure index doesn't go past size while reading, and you assume you have size entries at the end without checking.

c array malloc unknown size

How can I create an array of unique strings without knowing how many strings there are until I process the input file? There can be as many as 2 million strings, max length of 50.
My program is something like this. This works for 51 items then overwrites other data. I don't know how to add an element to the array, if possible.
main() {
char *DB_NAMES[51]; // i thought this gave me ptrs to chunks of 51
// but it's 51 pointers!
char *word;
while not eof {
...function to read big string
...function to separate big sting into words
...
processWord(ctr, DB_NAMES, word);
...
}
}
processWord(int ndx, char *array1[], char *word){
...function to find if word already exists...
//if word is new, store in array
array1[ndx]= (char *)malloc(sizeof(51)); // isn't this giving me a char[51]?
strcpy(array1[ndx],word);
...
}
You can first get the number of words in your file using the below logic and when you get the number of words in the file you can initialize the array size with the word count.
#include<stdio.h>
#define FILE_READ "file.txt"
int main()
{
FILE * filp;
int count = 1;
char c;
filp = fopen(FILE_READ, "r");
if(filp == NULL)
printf("file not found\n");
while((c = fgetc(filp)) != EOF) {
if(c == ' ')
count++;
}
printf("worrds = %d\n", count);
return 0;
}
Regards,
yanivx
Better not use a fixed string length; save memory space.
char **DB_NAMES = 0; // pointer to first char * ("string") in array; initially 0
Pass pointer by reference so that it can be altered. Moreover, you'll want the new ctr value in case a new word has been stored.
ctr = processWord(ctr, &DB_NAMES, word);
Change function processWord accordingly.
int processWord(int ndx, char ***array1a, char *word)
{ char **array1 = *array1a;
...function to find if word already exists...
// if word is new, store in array
{
array1 = realloc(array1, (ndx+1)*sizeof*array1); // one more string
if (!array1) exit(1); // out of memory
array1[ndx++] = strdup(word); // store word's copy
*array1a = array1; // return new array
}
return ndx; // return count
}

Resources