fscanf in a structure which has a string - c

I'm in trouble with my code.
I want to fscanf result.txt to structures, but it don't work;
result.txt format:
point name (for examples)
623 john
457 peter
312 chuck
etc.
First I count the lines then I malloc. For the structures' strings I have to malloc again, but I don't know the string's length. So I count it in the for loop.
#include <stdio.h>
#include <stdlib.h>
typedef struct ranklist {
int point;
char* name;
} ranklist;
int how_many_lines (FILE *fp){
rewind(fp);
int ch;
int line=0;
while (EOF != (ch = fgetc(fp)))
if (ch=='\n')
++line;
rewind(fp);
return line+1;
}
int how_many_letter(FILE *fp){
int ch;
int letter = 0;
int space=0;
while ('\n' != (ch = fgetc(fp)))
if (ch==' ')
space=1;
if (space == 1)
letter++;
return letter;
}
int main()
{
FILE *fp;
int y;
int name_length;
ranklist** r;
fp = fopen("result.txt","r");
int lines;
lines = how_many_lines(fp);
r = (ranklist**) malloc(lines * sizeof(ranklist*));
for (y = 0; y < lines; ++y){
name_length = how_many_letter(fp);
r[y] = (ranklist*) malloc(name_length * sizeof(ranklist));
}
for(y = 0; y < lines; y++){
fscanf(fp,"%d %s", &(r[y]->point), r[y]->name);
}
for( y = 0; y < lines; y++){
printf("%d %s", (r[y]->point), r[y]->name);
}
for (y = 0; y < lines; ++y)
free(r[y]);
free(r);
fclose(fp);
return 0;
}

sample to fix
int how_many_records(FILE *fp){
char ch;
int line=0;
int status;
rewind(fp);
while((status=fscanf(fp, "%*d %*[^\n]%c", &ch))==1)
++line;
if(status != EOF){
++line;
}
rewind(fp);
return line;
}
int how_many_letter(FILE *fp){
int letter = 0;
long pos = ftell(fp);
//fscanf(fp, " %*[^\n]%n", &letter);
fscanf(fp, " %*s%n", &letter);
fseek(fp, pos, SEEK_SET);
return letter;
}
int main(void){
FILE *fp = fopen("result.txt","r");
int y;
int name_length;
int lines = how_many_records(fp);
ranklist *r = malloc(lines * sizeof(*r));//just pointer, Do not need double pointer
for (y = 0; y < lines; ++y){
fscanf(fp, "%d", &r[y].point);
name_length = how_many_letter(fp);
r[y].name = malloc(name_length + 1);
fscanf(fp,"%s", r[y].name);
}
fclose(fp);
for( y = 0; y < lines; y++){
printf("%d %s\n", r[y].point, r[y].name);
free(r[y].name);
}
free(r);
return 0;
}

I did a few changes into your code and add some comments to explain. Just comment bellow any doubts.
int how_many_lines (FILE *fp){
rewind(fp);
int ch;
int line=0;
while (EOF != (ch = fgetc(fp)))
if (ch=='\n')
++line;
rewind(fp);
return line+1;
}
int how_many_letter(FILE *fp){
int ch;
int letter = 0;
// find first space since we
// know the input format <number> <name>
while ((ch = fgetc(fp)) != ' ');
// count the number of characters until end of file
// or '\n'
while ((ch = fgetc(fp)) != '\n' && ch != EOF )
letter++;
return (letter);
}
int main()
{
FILE *fp;
int y;
int name_length;
ranklist** r;
fp = fopen("file.txt","r");
int lines;
lines = how_many_lines(fp);
printf("[%d]\n", lines);
r = (ranklist**) malloc(lines * sizeof(ranklist*));
for (y = 0; y < lines; ++y){
name_length = how_many_letter(fp);
// you must allocate memory for ranklist element and string name
r[y] = (ranklist *) malloc(sizeof(ranklist));
r[y]->name = (char *) malloc(name_length * sizeof(char));
}
// rewind pointer again
rewind(fp);
for(y = 0; y < lines; y++){
fscanf(fp,"%d %s", &(r[y]->point), r[y]->name);
}
for( y = 0; y < lines; y++){
printf("%d %s\n", (r[y]->point), r[y]->name);
}
for (y = 0; y < lines; ++y)
free(r[y]);
free(r);
fclose(fp);
return 0;
}

Related

how to tokenize a c program if the data types are like unsigned long int etc

My problem is given below -
Write a program which gets in input, a program (from a file) and prints all variables in it as output.
Ex. Inputprogram.txt
#include<stdio.h>
int main(void)
{
int a, b, c=10;
char p, *ptr=NULL;
unsigned long long int t;
}
Output must contain: a,b,c,p,ptr,t.
My program is -
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
int isDataType(char buffer[]){
char types[19][10] = {"char","const","double","float","int","long","register","short","signed",
"static","struct","union","unsigned","void","volatile","int*","char*","float*","enum"};
int i, flag = 0;
for(i = 0; i < 19; ++i){
if(strcmp(types[i], buffer) == 0){
flag = 1;
break;
}
}
return flag;
}
int main(){
char ch, buffer[15], operators[] = "+-*/%=.#";
FILE *fp;
int i,j=0;
fp = fopen("inputprogram.txt","r");
if(fp == NULL){
printf("error while opening the file\n");
exit(0);
}
while((ch = fgetc(fp)) != EOF){
for(i = 0; i < 8; ++i){
if(ch == operators[i])
printf("%c is an operator\n", ch);
}
if(isalpha(ch)){
buffer[j++] = ch;
}
else if((ch == ' ' || ch == '\n' || ch == ',' || (isdigit(ch))== 1) && (j != 0)){
buffer[j] = '\0';
j = 0;
if(isDataType(buffer) == 1)
printf("%s is a data type\n", buffer);
else
printf("%s is a variable\n", buffer);
}
}
fclose(fp);
return 0;
}
This code is not giving desired result if the data like unsigned long long int comes in the txt file.
How to optimize the code in such a way that if the data types like unsigned long int or int * (int space star) comes then also it is able to print the exact variable.

Alphabetical order error in C code

I have created a program that reads a series of strings from a .txt file and after compiling a new .txt file is created where the strings should be in alphabetical order.The problem is that I can't write more than 10 words, the compiler just stops/crashes, WHY? Does it depend by the type of compiler? I am currently using Code-Bloks.How can I optimize the code to run more smoothly?
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void arrange(int n, char *x[])
{
char *temp;
int i,str;
for(str = 0; str < n-1; ++str)
{
for(i = str+1; i < n; ++i)
{
if(strcmp(x[str],x[i]) > 0)
{
temp = x[str];
x[str] = x[i];
x[i] = temp;
}
}
}
return;
}
int number_of_lines = 0;
void countOfLinesFromFile(char *filename){
FILE* myfile = fopen(filename, "r");
int ch;
do
{
ch = fgetc(myfile);
if(ch == '\n')
number_of_lines++;
}
while (ch != EOF);
if(ch != '\n' && number_of_lines != 0)
number_of_lines++;
fclose(myfile);
return number_of_lines;
}
int main()
{
int i , ts=0;
char *x[10];
char *fileName = "WORDS.txt";
countOfLinesFromFile(fileName);
printf("%d",number_of_lines);
FILE * fp;
fp = fopen ("WORDS.txt", "r");
for(i = 0; i < number_of_lines; i++)
{
x[i] = (char*) malloc (1200*sizeof(char));
fscanf(fp, "%s", x[i]);
}
FILE *fPointer;
fPointer=fopen("Alphabetical.txt","w+");
arrange(i,x);
for(i = 0; i < number_of_lines; i++)
{
fprintf(fPointer,"%s\n",x[i]);
}
fclose(fPointer);
fclose(fp);
return 0;
}
char *x[10];
The buffer size is too small
These two lines define how much information you can store
char *x[10]; // 10 strings
x[i] = (char*) malloc (1200*sizeof(char)); // 1200 characters each
As it is written now, you can only hold a maximum of 10 strings with each string being no longer than 1200 characters.
The crash is caused when number_of_lines >= 11 in the following for loop:
for(i = 0; i < number_of_lines; i++)
{
x[i] = (char*) malloc (1200*sizeof(char));
fscanf(fp, "%s", x[i]);
}
When i is 11 you write to x[11] which is past the end of x.

Read a text file into a struct array with functions

so I have this file:
Jane
18
5.3
John
23
5.8
and I need to create a program to store this two persons details on a array of structs.
I have done this:
#include <stdio.h>
typedef struct
{
char name[100];
int age;
float height;
} PERSON;
main()
{
PERSON *X = NULL;
FILE *f;
char ch;
int lines = 1;
f = fopen("filename.txt", "r");
while ((ch = fgetc(f)) !=EOF)
{
if (ch == '\n')
lines++;
}
rewind(f);
X = (PERSON*) malloc ((lines/3) * sizeof(PERSON));
StoreInArray(X, f, lines);
}
StoreInArray(PERSON *X, FILE *f, int lines)
{
int i = 0;
for (i=0; i < lines/3; i++)
{
fscanf(f, "%s%d%f", (*(X+i)).name[100], &(*(X+i)).age, &(*(X+i)).height);
}
//for testing//
for (i=0; i < lines/3; i++)
printf("%s\n%d \n%f\n",X[i].name[100], X[i].age, X[i].height);
}
But all it prints is:
(null)
0
0.000000
If you could help me figure out what is wrong I'd be very appreciated!
Thank you in advance.
I'd compile with -Wall -O2 to catch warnings, which would have pointed out an error.
In your fscanf and printf, you don't want name[100] but name. You want to point to the name array, but name[100] is a single char [fetching past the end of the array (i.e.) undefined behavior], and would be flagged by the compiler.
Here's a version of your code with the bugs annotated/corrected:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
void StoreInArray(PERSON * X, FILE * f, int lines);
int
main()
{
PERSON *X = NULL;
FILE *f;
char ch;
int lines = 1;
f = fopen("filename.txt", "r");
while ((ch = fgetc(f)) != EOF) {
if (ch == '\n')
lines++;
}
rewind(f);
X = (PERSON *) malloc((lines / 3) * sizeof(PERSON));
StoreInArray(X, f, lines);
return 0;
}
void
StoreInArray(PERSON * X, FILE * f, int lines)
{
int i = 0;
for (i = 0; i < lines / 3; i++) {
#if 0
fscanf(f, "%s%d%f", (*(X + i)).name[100], &(*(X + i)).age, &(*(X + i)).height);
#else
fscanf(f, "%s%d%f", (*(X + i)).name, &(*(X + i)).age, &(*(X + i)).height);
#endif
}
//for testing//
for (i = 0; i < lines / 3; i++) {
#if 0
printf("%s\n%d \n%f\n", X[i].name[100], X[i].age, X[i].height);
#else
printf("%s\n%d \n%f\n", X[i].name, X[i].age, X[i].height);
#endif
}
}
Note that using *(X + i)).name is cumbersome, so here's a simplified and more readable version that uses an extra pointer variable:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
void StoreInArray(PERSON * X, FILE * f, int lines);
int
main()
{
PERSON *X = NULL;
FILE *f;
char ch;
int lines = 1;
f = fopen("filename.txt", "r");
while ((ch = fgetc(f)) != EOF) {
if (ch == '\n')
lines++;
}
rewind(f);
X = (PERSON *) malloc((lines / 3) * sizeof(PERSON));
StoreInArray(X, f, lines);
return 0;
}
void
StoreInArray(PERSON * X, FILE * f, int lines)
{
int i = 0;
PERSON *p;
p = X;
for (i = 0; i < lines / 3; i++, p++)
fscanf(f, "%s%d%f", p->name, &p->age, &p->height);
//for testing//
p = X;
for (i = 0; i < lines / 3; i++, p++)
printf("%s\n%d \n%f\n", p->name, p->age, p->height);
}
It isn't really necessary to preread the file to get a line count. It is possible to grow your array dynamically, as lines are read in, using realloc:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
int
main()
{
PERSON *X = NULL;
FILE *f;
int i;
PERSON *p;
int count = 0;
f = fopen("filename.txt", "r");
while (1) {
X = realloc(X,sizeof(PERSON) * (count + 1));
p = &X[count];
if (fscanf(f, "%s%d%f", p->name, &p->age, &p->height) == EOF)
break;
++count;
}
fclose(f);
// trim the array
X = realloc(X,sizeof(PERSON) * count);
p = X;
for (i = 0; i < count; i++, p++)
printf("%s\n%d \n%f\n", p->name, p->age, p->height);
return 0;
}
Here's a further refinement that reduces the number of realloc calls needed:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[100];
int age;
float height;
} PERSON;
int
main()
{
PERSON *X = NULL;
FILE *f;
int i;
PERSON *p;
size_t count = 0;
size_t alloc = 0;
f = fopen("filename.txt", "r");
while (1) {
if (count >= alloc) {
alloc += 100;
X = realloc(X,sizeof(PERSON) * alloc);
}
p = &X[count];
if (fscanf(f, "%s%d%f", p->name, &p->age, &p->height) == EOF)
break;
++count;
}
fclose(f);
// trim the array to what was actually used
X = realloc(X,sizeof(PERSON) * count);
p = X;
for (i = 0; i < count; i++, p++)
printf("%s\n%d \n%f\n", p->name, p->age, p->height);
return 0;
}

Base change program in C complies but does not show any output

I'm recently new to programming but this assignment has proven to be my most difficult. The program is suppose to read in a .txt file with the following format
input_base number output_base
and outputs the results. the bases can only range from 2-36 For example:
input: 2 10 4
output: 2
My program reads in each part of the line and stores them respectively. I then go through the steps to convert the number into the output base and then print it backwards. My problem is that the program runs fine and prints all the stored values and calculated values, but once I add in my "base_conversion" function the program no longer works and my friend even said it gave him a segmentation fault. I don't really know what could be causing it. Any help would be appreciated. Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void read_file_to_buffer(FILE *file);
char *buffer = NULL;
void string_check(char *buffer);
int char_to_int(char c);
int int_conversion(int x, int y);
void base_conversion(int end_number, int out_base);
int main(int argc, char *argv[]){
FILE *file = NULL;
if ( argc != 2 ){
fprintf(stderr, "Error: To many/few arguments\n");
exit(EXIT_FAILURE);
}
else {
file = fopen(argv[1], "rb");
if (file == NULL) {
fprintf(stderr, "Error: could not open file '%s'\n", argv[1]);
exit(EXIT_FAILURE);
}
else{
printf("File %s opened!\n", argv[1]);
}
}
read_file_to_buffer(file);
string_check(buffer);
fclose(file);
free(buffer);
buffer = NULL;
exit(EXIT_SUCCESS);
}
void read_file_to_buffer(FILE *file) {
long file_size = 0;
if(buffer != NULL){
fprintf(stderr, "buffer in use\n");
exit(EXIT_FAILURE);
}
rewind(file);
if (fseek(file, 0, SEEK_END) != 0){
perror("Could not seek to end of file");
exit(EXIT_FAILURE);
}
file_size = ftell(file);
if (file_size < 0){
perror("could not tell size of file");
exit(EXIT_FAILURE);
}
rewind(file);
buffer = (char *)malloc(sizeof(char) * (file_size + 1));
if (buffer == NULL){
fprintf(stderr, "Could not allocate memory");
exit(EXIT_FAILURE);
}
if(fread(buffer, sizeof(char), (size_t)file_size, file) != file_size){
fprintf(stderr, "Could not read file\n");
exit(EXIT_FAILURE);
}
buffer[file_size] = '\0';
return;
}
void string_check(char *buffer){
int i = 0;
int j = 0;
char base_in[2];
char number[50];
char base_out[2];
int actual_number[100];
int end_number = 0;
int x = 0;
int y = 0;
int in_base;
int out_base;
while( buffer[i] != '\0'){
if (buffer[i] != '#' && buffer[i] != ' ' && buffer[i] != '\n' && buffer[i] != '\r'){
while (buffer[i] != ' '){
base_in[j] = buffer[i];
i++;
j++;
}
j = 0;
i++;
if (base_in[1] != '\0'){
x = char_to_int(base_in[0]);
y = char_to_int(base_in[1]);
in_base = int_conversion(x, y);
x = 0;
y = 0;
}
else{
in_base = char_to_int(base_in[0]);
}
while (buffer[i] != ' '){
number[j] = buffer[i];
i++;
j++;
}
int q;
q=j;
j=0;
while (number[j] != '\0'){
actual_number[j] = char_to_int(number[j]);
end_number = end_number + pow(in_base, q-1) * actual_number[j];
j++;
q--;
}
j = 0;
i++;
while (buffer[i] != '\n' && buffer[i] != '\0'){
base_out[j] = buffer[i];
i++;
j++;
}
if (base_out[1] != '\0'){
x = char_to_int(base_out[0]);
y = char_to_int(base_out[1]);
out_base = int_conversion(x, y);
x = 0;
y = 0;
}
else{
out_base = char_to_int(base_out[0]);
}
j = 0;
i++;
base_conversion(end_number, out_base);
}
else{
while (buffer[i] != '\n' && buffer[i] != '\0'){
i++;
}
}
i++;
}
return;
}
int char_to_int(char c){
char map[] = "0123456789abcdefghijklmnopqrstuvwxyz";
int result = -1;
char *next = map;
while(*next != '\0'){
if(*next == c){
result = next - map;
break;
}
next++;
}
return result;
}
int int_conversion(int x, int y){
int value;
value = (x * 10) + y;
return value;
}
void base_conversion(int end_number, int out_base){
int remainder[100];
char map[] = "0123456789abcdefghijklmnopqrstuvwxyz";
int index = 0;
int i;
while (end_number != 0){
remainder[index] = end_number % out_base;
end_number = end_number / out_base;
index++;
}
for (i=0; i<index; i++){
printf("%c", map[remainder[index-1]]);
}
printf("\n");
return;
}
OP's base_conversion() is messed.
Loop prints same character repeatedly.
for (i=0; i<index; i++){
printf("%c", map[remainder[index-1]]); // Why same character?
}
Code is using signed math and % and can create negative remainders which may be used as array index.
remainder[index] = end_number % out_base; // result may be negative.
...
printf("%c", map[remainder[index-1]]); // accessing out of bounds with negative
Suggested simplification.
void base_conversion_helper(unsigned end_number, int out_base){
if (end_number >= out_base) base_conversion_helper(end_number/out_base, out_base);
putchar("0123456789abcdefghijklmnopqrstuvwxyz"[end_number % outbase]);
}
void base_conversion(unsigned end_number, int out_base){
assert(out_base >= 2 && out_base <= 36);
base_conversion_helper(end_number, out_base);
putchar('\n');
}

Dynamically allocating array of structs C

I'm trying to make an array of structs in c, but I can't make it work. When I try to run it, the program crashes.
typedef struct{
char name[20];
char manufacturer[20];
unsigned int price;
} product;
unsigned int stringToNr(char *numbers){
unsigned int nr = 0;
unsigned int i;
for (i = 0; i < strlen(numbers); i ++)
{
nr *= 10; nr += numbers[i] - '0';
}
return nr;
}
I have a function that would print the list to a file, sometimes it reaches this function, sometimes it crashes before.
void printList(product *products, unsigned int nr){
unsigned int i;
FILE *f;
f = fopen("output.txt", "w");
for (i = 0; i < nr; i ++){
fprintf(f, "%s ", products[i].name);
fprintf(f, "%s ", products[i].manufacturer);
fprintf(f, "%d\n", products[i].price);
}
fclose(f);
}
I have to use a separate function to read the list from file.
void readList(product **products, unsigned int *nr){
FILE *f;
f = fopen("input.txt", "r");
char *row;
row = malloc(sizeof(char) * 45);
unsigned int rowLength;
fgets(row, 45, f);
rowLength = strlen(row);
if (row[rowLength - 1] == '\n'){
rowLength--;
row[rowLength ] = '\0';
}
*nr = stringToNr(row);
products = malloc((*nr) * sizeof(product*));
unsigned int i;
char *rowElement;
for (i = 0; i < *nr; i ++){
fgets(row, 45, f);
rowElement = strtok(row, " ");
strcpy((*products)[i].name, rowElement);
rowElement = strtok(NULL, " ");
strcpy((*products)[i].manufacturer, rowElement);
rowElement = strtok(NULL, " ");
rowLength = strlen(row);
if (row[rowLength- 1] == '\n'){
rowLength--;
row[rowLength] = '\0';
}
(*products)[i].price = stringToNr(rowElement);
}
free(row);
fclose(f);
}
Obviously the program has more features, but those work fine.
int main(){
product *products;
unsigned int nr;
readList(&products, &nr);
printList(products, nr);
free(products);
return 0;
}
My input file looks like this:
3
AAA FactoryA 300
BBB FactoryC 550
ZZZ Factory5 100
Code ignores value of products.
What ever readList() receives in products is overwritten with the malloc() call.
void readList(product **products, unsigned int *nr){
...
// bad
products = malloc((*nr) * sizeof(product*));
Instead, use *products. Also allocate by the size of the referenced variable, not by the size of the type. Easier to code, review and maintain.
*products = malloc(sizeof *(*products) * (*nr));
if (*products == NULL) Handle_OOM();
Minor: After fgets(row, ..., ...); , following is not safe from a hacker exploit of reading an initial null character.
rowLength = strlen(row);
// What happens when rowLength == 0
if (row[rowLength- 1] == '\n'){
...
Instead code could use below to rid the optional trailing '\n'.
row[strcspn(row, "\n")] = '\0';

Resources