I try to store input from a text file to an array of struct's elements
For example:
#001 Bulbasaur Grass Poison
#002 Ivysaur Grass Poison
#003 Venusaur Grass Poison
#004 Charmander Fire
#005 Charmeleon Fire
#006 Charizard Fire Flying
You can see that pokemons may have 2 types and some have only 1 type and the code went well until a pokemon that has only 1 type.
This a part of the output when I try to print the array of struct's elements:
#001 Bulbasaur Grass Poison
#002 Ivysaur Grass Poison
#003 Venusaur Grass Poison
#004 Charmander Fire #005
Charmeleon Fire #006 Charizard
Fire Flying #007 Squirtle
Water #008 Wartortle Water
#009 Blastoise Water #010
I tried a lot of things from checking a '\n' in the type1 buffer until searching web but I can't find a solution, please help me.
And BTW if I am doing something wrong with the dynamic memory allocation I would love to get suggestions.
These are the structs and the function:
typedef struct pokemon
{
int ID;
char *number;
char *name;
char *type1;
char *type2;
} Pokemon;
struct Trainer
{
char *firstName;
char *lastName;
Pokemon *pokemonsInBank;
Pokemon pokemonHeldByTheTrainer[6];
} Trainer;
struct pokemonDataBase
{
Pokemon *pokemonsDB;
int numOfPokemon;
} pokemonDataBase;
Storing Function
void loadPokemonsToDB()
{
char numBuffer[maxSize];
char nameBuffer[maxSize];
char typeBuffer[maxSize];
char type2Buffer[maxSize];
int i = 0;
char check[maxSize];
FILE *fp = fopen("Pokemons.txt", "r");
if (fp == NULL)
{
printf("Error opening the file\n");
exit(4);
}
pokemonDataBase.pokemonsDB = (Pokemon *)malloc((countPok()) * sizeof(Pokemon));
if (pokemonDataBase.pokemonsDB == NULL)
{
printf("Memory Error\n");
exit(4);
}
for (int i = 0; i < countPok(); i++)
{
fscanf(fp, "%s", numBuffer);
pokemonDataBase.pokemonsDB[i].number = (char *)malloc((strlen(numBuffer) + 1) * sizeof(char));
if (pokemonDataBase.pokemonsDB[i].number == NULL)
{
for (int j = 0; j < i; j++)
{
free(pokemonDataBase.pokemonsDB[j].number);
}
printf("Memory Error at index %d", i);
exit(4);
}
strcpy(pokemonDataBase.pokemonsDB[i].number, numBuffer);
fscanf(fp, "%s", nameBuffer);
pokemonDataBase.pokemonsDB[i].name = (char *)malloc((strlen(nameBuffer) + 1) * sizeof(char));
if (pokemonDataBase.pokemonsDB[i].name == NULL)
{
for (int j = 0; j < i; j++)
{
free(pokemonDataBase.pokemonsDB[j].name);
}
printf("Memory Error at index %d", i);
exit(4);
}
strcpy(pokemonDataBase.pokemonsDB[i].name, nameBuffer);
fscanf(fp, "%s", typeBuffer);
pokemonDataBase.pokemonsDB[i].type1 = (char *)malloc((strlen(typeBuffer) + 1) * sizeof(char));
if (pokemonDataBase.pokemonsDB[i].type1 == NULL)
{
for (int j = 0; j < i; j++)
{
free(pokemonDataBase.pokemonsDB[j].type1);
}
printf("Memory Error at index %d", i);
exit(4);
}
strcpy(pokemonDataBase.pokemonsDB[i].type1, typeBuffer);
fscanf(fp, "%s", type2Buffer);
pokemonDataBase.pokemonsDB[i].type2 = (char *)malloc((strlen(type2Buffer) + 1) * sizeof(char));
if (pokemonDataBase.pokemonsDB[i].type2 == NULL)
{
for (int j = 0; j < i; j++)
{
free(pokemonDataBase.pokemonsDB[j].type2);
}
printf("Memory Error at index %d", i);
exit(4);
}
strcpy(pokemonDataBase.pokemonsDB[i].type2, type2Buffer);
}
}
scanf is not your friend. A whitespace in the format string does not distinguish between space and newline. You can make scanf work, but it's really not worth the effort. I recommend you not do this, but you could try something like:
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct pokemon
{
int ID;
char name[128];
char type1[128];
char type2[128];
};
int
main(int argc, char **argv)
{
FILE *ifp = argc > 1 ? fopen(argv[1], "r") : stdin;
if( ifp == NULL ){
perror(argv[1]);
return 1;
}
struct pokemon poke[128];
struct pokemon *p = poke;
struct pokemon *e = poke + sizeof poke / sizeof *poke;;
int i = 0;
int rv;
while( p < e &&
2 < (rv = fscanf(ifp, " #%d %127s %127s %127[^\n]",
&p->ID,
p->name,
p->type1,
p->type2
)
)
){
assert( rv == 3 || rv == 4 );
if( rv == 3 ){
p->type2[0] = '\0';
}
p += 1;
}
e = p;
p = poke;
for( ; p < e; p++ ){
printf("%d %s %s %s\n", p->ID, p->name, p->type1, p->type2);
}
}
/* sample of expected input
#001 Bulbasaur Grass Poison
#002 Ivysaur Grass Poison
#003 Venusaur Grass Poison
#004 Charmander Fire
*/
Note that this is extremely fragile. If a row contains 10 "words", the remainder will all go into the type2 field. Also, as soon as you have a name that exceeds the 127 character length, parsing will just stop and not give any indication as to why. Handling unruly input is just not easy to do with scanf, and it's generally not worth the effort. Maybe a simple:
if( ! feof(ifp) ){
fprintf(stderr, "invalid input\n");
exit(1);
}
after the input loop would be sufficient, but it would be better to simply stop trying to use scanf. In the end, you will waste a lot of time trying to figure its behavior, and that time would be better spent learning how to use other tools to parse the input.
If you want to minimize changes to your existing code, you could try adding something like the following before you attempt to scan into the second type:
int c;
while( ( c = getc(ifp) ) == ' ' ){
;
}
if( c != '\n' ){
ungetc(c, ifp);
/* Scan the second word */
} else {
/* There is no second word */
}
Note that mixing scanf with getc is a potential source of confusion, and if you don't know exactly what you're doing this can cause great frustration. The basic idea is to consume spaces until you see a non-space (note that the above code will need to be modified if you want to handle tabs or other non-newline whitespace). If the first character you see is a newline, simply move on to the next line. If it is not a newline, put it back on the stream so scanf can see it and read the second type. Again, this cannot be stated often enough, you should abandon scanf now. It will cause you far more headaches than it is worth.
Related
Here is the code that is giving me the error of Segmentation fault..
void hire_skill()
{
int linec = 0;
FILE *p;
p = fopen("/home/suraj/Coding/PBL/Details/hiree.txt", "r");
if (p == NULL)
{
printf("\nFile did not open\n");
}
char c;
c = fgetc(p);
while (c != EOF)
{
if (c == '\n')
{
linec++;
}
c = fgetc(p);
}
fclose(p);
printf("\nNumber of lines :\t%d\n", linec);
FILE *ptrr;
ptrr = fopen("/home/suraj/Coding/PBL/Details/hiree.txt", "r");
if (ptrr == NULL)
{
printf("\nFile did not open\n");
}
rewind(ptrr);
for (int i = 0; i < linec; i++)
{
fscanf(ptrr, "%s", hiree_login[i].name);
fscanf(ptrr, "%d", hiree_login[i].age);
fscanf(ptrr, "%s", hiree_login[i].gender);
fscanf(ptrr, "%d", hiree_login[i].uid);
fscanf(ptrr, "%s", hiree_login[i].skill);
fscanf(ptrr, "%lld", hiree_login[i].phno);
}
for (int i = 0; i < linec; i++)
{
printf("\n%s, %d, %s, %d, %s, %lld\n", hiree_login[i].name, hiree_login[i].age, hiree_login[i].gender, hiree_login[i].uid, hiree_login[i].skill, hiree_login[i].phno);
}
fclose(ptrr);
}
And here is the structure i'm using to get values from the file and store it
struct hireeLogin
{
int age;
char name[20];
char gender[1];
int uid;
char skill[20];
long long int phno;
} hiree_login[MAX1]; //MAX1 = 50..
The whole code is on my github account : https://github.com/Suru-web/PBL/blob/main/Emp.c
I tried a few irrelevant things, but none of them worked. maybe i dont know much about this, so i would like anyone to help me fix my code. Thank you!!
gender is an array of size 1 and can therefore hold a string of up to length 0. In the line fscanf(ptrr, "%s", hiree_login[i].gender);, scanf is probably writing more than zero characters into the buffer, so the behavior is undefined.
Never use "%s" in a format string. Always use a width modifier that is no more than one less than the size of the buffer.
In this line ,
fscanf(p, "%s", hiree_login[i].name);
File pointer p is already closed. You may need to use ptrr
I am attempting to read numbers from a text file (currently 1m+) and am running into a buffer overflow scenario. I imagine it has something to do with my array's memory allocation but I am absolutely sized up as to how to fix it. I realize my code may be (is probably) sloppy and/or incorrect; any guidance to coding style or readability improvements would be greatly appreciated.
I will be removing the printf statement(s) once I can verify that all numbers from the text file can be successfully read into an appropriately sized array.
My code is as follows:
int main(int argc, char* argv[]) {
#ifndef NDEBUG
printf("DBG: argc = %d\n", argc);
for (int i = 1; i < argc; ++i)
printf("DBG: argv[%d] = \"%s\"\n", i, argv[i]);
#endif
FILE* stream = stdin;
if (argc == 2) {
stream = fopen(argv[1], "r");
if (stream == NULL) {
fprintf(stderr, "error, <%s> ", argv[1]);
perror(" ");
return EXIT_FAILURE;
}
}
printf("Enter a list of whitespace-separated real numbers terminated by EOF or \'end\'\n");
char ch = 0;
int cline = 0;
size_t arrsize = cline * sizeof(double);
double* fileArr = (double*)malloc(cline * sizeof(double));
if (fileArr == NULL) {
return NULL;
}
int i = 0;
double number = 0.0;
for (size_t i = 0; i < sizeof(stream); ++i) {
while (fscanf(stream, "%lf", &fileArr[i]) == 1)
{
printf("%.0lf\n", number);
if (fgetc(stream) == '\n') {
cline = cline + 1;
}
}
}
fclose(stream);
/*DELETE AFTER FILEARRAY CAN BE INSTANTIATED WITH THE CONTENTS OF THE FILE*/
printf("\nSTUB\t\t There are %d lines\n\n", cline);
size_t size = cline;
//test print the array to verify it's contents before sorting/ remove after verfied.
for (size_t i = 0; i < size; ++i) {
printf("%.0lf\n", fileArr[i]);
}
return EXIT_SUCCESS;
}
int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
int i = 0;
char name[200][100];
char goods[200][100];
char qty[200][100];
char temp[200][100];
int x = 0;
int result;
while (!feof(fp)) {
fscanf(fp, "%[^,] , %[^,] , %s " , name[i], item[i], qty[i]); /*get file content and store in array */
if (strcmp(item[i], "Football") == 0) { /* only select Football */
temp[x][x] = qty[i];
if (x > 0) {
if (strcmp(temp[x][x], temp[x + 1][x + 1]) > 0) { /*compare who has more football qty */
result = x; /*output the person who have more football*/
}
}
x = x + 1;
}
}
printf("%s is team leader in class.\n", name[result]);
fclose(fp);
getchar();
return 0;
}
Hi all, I don't know why the result not correct.
I want to find out who has more football and print out his/her name.
Seems something wrong on if (strcmp(temp[x], temp[x + 1]) > 0)
I am not clearly on using pointer and address.
the content in the text file are:
Alice,Eating,001
Kitty,Football,006
Ben,Swimming,003
May,Football,004
And I expect the result is :
Kitty is team leader in class.
Thank you.
There are multiple problems in your code:
you do not test if the file is properly open.
you cannot properly parse a file with while (!feof(fp)) {. You should iterate for as long as fscanf() returns 3, or preferably read the input line by line and parse it with sscanf().
you do not tell fscanf() the maximum number of characters to store into the destination arrays. This may cause undefined behavior for invalid input.
you do not increment i for each line read. Every line of input overwrites the previous one.
you do not check if there are more than 200 lines. Undefined behavior in this case.
Your test to find the football fan with the highest quantity is broken: no need for a 2D array here, just keep track of the current maximum and update it when needed.
Here is a modified version:
#include <stdio.h>
int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
char buf[300];
char name[200][100];
char goods[200][100];
char qty[200][100];
int i, qty, max_qty = 0, result = -1;
if (fp == NULL) {
fprintf(stderr, "cannot open file\n");
return 1;
}
for (i = 0; i < 200; i++) {
if (!fgets(buf, sizeof buf, fp))
break;
if (sscanf(buf, " %99[^,], %99[^,],%99s", name[i], item[i], qty[i]) != 3) {
fprintf(stderr, "invalid input: %s\n", buf);
break;
}
if (strcmp(item[i], "Football") == 0) { /* only select Football */
qty = atoi(qty[i]);
if (result == -1 || qty > max_qty) {
result = i; /*store the index of the person who have more football */
}
}
}
if (result < 0)
printf("no Football fan at all!\n");
else
printf("%s is team leader in class with %d in Football.\n", name[result], max_qty);
fclose(fp);
getchar();
return 0;
}
Above Code is not clear as what you want to do in this code block
if ( strcmp(temp [x], temp [x+1]) > 0 ){ /* when matches, accessing temp[x+1] results in undefined behaviour */
result = x;
}
also why char *temp[200][100]; as to store qty[i], char *temp is enough or you can take char temp[200][100];
Here is somewhat better one as requirement is not clear.
int main() {
FILE *fp= fopen("fileA.txt","r"); /* read file */
if(fp == NULL) {
/* not exist.. write something ?? */
return 0;
}
char name [200][100],goods[200][100],qty[200][100],temp[200][100];
int x = 0,result = 0, i = 0;
while ((fscanf(fp, "%[^,] , %[^,] , %s " , name[i], goods [i], qty[i])) == 3) {
if (strcmp(goods[i] , "Football") == 0){
strcpy(temp[x],qty[i]);
if ( strcmp(temp [x], temp [x+1]) > 0 ) { /* UB ? */
result = x;
x+=1;
}
}
}
printf("%s is team leader in class. \n", name[result]);
fclose(fp);
getchar();
return 0;
}
So this is my second time adapting my code to fscanf to get what I want. I threw some comments next to the output. The main issue I am having is that the one null character or space is getting added into the array. I have tried to check for the null char and the space in the string variable and it does not catch it. I am a little stuck and would like to know why my code is letting that one null character through?
Part where it is slipping up "Pardon, O King," output:King -- 1; -- 1
so here it parses king a word and then ," goes through the strip function and becomes \0, then my check later down the road allows it through??
Input: a short story containing apostrophes and commas (the lion's rock. First, the lion woke up)
//Output: Every unique word that shows up with how many times it shows up.
//Lion -- 1
//s - 12
//lion -- 8
//tree -- 2
//-- 1 //this is the line that prints a null char?
//cub -- //3 it is not a space! I even check if it is \0 before entering
//it into the array. Any ideas (this is my 2nd time)?
//trying to rewrite my code around a fscanf function.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
//Remove non-alpha numeric characters
void strip_word(char* string)
{
char* string_two = calloc(80, sizeof(char));
int i;
int c = 0;
for(i = 0; i < strlen(string); i++)
{
if(isalnum(string[i]))
{
string_two[c] = string[i];
++c;
}
}
string_two[i] = '\0';
strcpy(string, string_two);
free(string_two);
}
//Parse through file
void file_parse(FILE* text_file, char*** word_array, int** count_array, int* total_count, int* unique_count)
{
int mem_Size = 8;
int is_unique = 1;
char** words = calloc(mem_Size, sizeof(char *)); //Dynamically allocate array of size 8 of char*
if (words == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
int* counts = calloc(mem_Size, sizeof(int)); //Dynamically allocate array of size 8 of int
if (counts == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
printf("Allocated initial parallel arrays of size 8.\n");
fflush(stdout);
char* string;
while('A')
{
is_unique = 1;
fscanf(text_file, " ,");
fscanf(text_file, " '");
while(fscanf(text_file, "%m[^,' \n]", &string) == 1) //%m length modifier
{
is_unique = 1;
strip_word(string);
if(string == '\0') continue; //if the string is empty move to next iteration
else
{
int i = 0;
++(*total_count);
for(i = 0; i < (*unique_count); i++)
{
if(strcmp(string, words[i]) == 0)
{
counts[i]++;
is_unique = 0;
break;
}
}
if(is_unique)
{
++(*unique_count);
if((*unique_count) >= mem_Size)
{
mem_Size = mem_Size*2;
words = realloc(words, mem_Size * sizeof(char *));
counts = realloc(counts, mem_Size * sizeof(int));
if(words == NULL || counts == NULL)
{
fprintf(stderr, "ERROR: realloc() failed!");
}
printf("Re-allocated parallel arrays to be size %d.\n", mem_Size);
fflush(stdout);
}
words[(*unique_count)-1] = calloc(strlen(string) + 1, sizeof(char));
strcpy(words[(*unique_count)-1], string);
counts[(*unique_count) - 1] = 1;
}
}
free(string);
}
if(feof(text_file)) break;
}
printf("All done (successfully read %d words; %d unique words).\n", *total_count, *unique_count);
fflush(stdout);
*word_array = words;
*count_array = counts;
}
int main(int argc, char* argv[])
{
if(argc < 2 || argc > 3) //Checks if too little or too many args
{
fprintf(stderr, "ERROR: Invalid Arguements\n");
return EXIT_FAILURE;
}
FILE * text_file = fopen(argv[1], "r");
if (text_file == NULL)
{
fprintf(stderr, "ERROR: Can't open file");
}
int total_count = 0;
int unique_count = 0;
char** word_array;
int* count_array;
file_parse(text_file, &word_array, &count_array, &total_count, &unique_count);
fclose(text_file);
int i;
if(argv[2] == NULL)
{
printf("All words (and corresponding counts) are:\n");
fflush(stdout);
for(i = 0; i < unique_count; i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
else
{
printf("First %d words (and corresponding counts) are:\n", atoi(argv[2]));
fflush(stdout);
for(i = 0; i < atoi(argv[2]); i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
for(i = 0; i < unique_count; i++)
{
free(word_array[i]);
}
free(word_array);
free(count_array);
return EXIT_SUCCESS;
}
I'm not sure quite what's going wrong with your code. I'm working on macOS Sierra 10.12.3 with GCC 6.3.0, and the local fscanf() does not support the m modifier. Consequently, I modified the code to use a fixed size string of 80 bytes. When I do that (and only that), your program runs without obvious problem (certainly on the input "the lion's rock. First, the lion woke up").
I also think that the while ('A') loop (which should be written conventionally while (1) if it is used at all) is undesirable. I wrote a function read_word() which gets the next 'word', including skipping blanks, commas and quotes, and use that to control the loop. I left your memory allocation in file_parse() unchanged. I did get rid of the memory allocation in strip_word() (eventually — it worked OK as written too).
That left me with:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
static void strip_word(char *string)
{
char string_two[80];
int i;
int c = 0;
int len = strlen(string);
for (i = 0; i < len; i++)
{
if (isalnum(string[i]))
string_two[c++] = string[i];
}
string_two[c] = '\0';
strcpy(string, string_two);
}
static int read_word(FILE *fp, char *string)
{
if (fscanf(fp, " ,") == EOF ||
fscanf(fp, " '") == EOF ||
fscanf(fp, "%79[^,' \n]", string) != 1)
return EOF;
return 0;
}
static void file_parse(FILE *text_file, char ***word_array, int **count_array, int *total_count, int *unique_count)
{
int mem_Size = 8;
char **words = calloc(mem_Size, sizeof(char *));
if (words == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
int *counts = calloc(mem_Size, sizeof(int));
if (counts == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
printf("Allocated initial parallel arrays of size 8.\n");
fflush(stdout);
char string[80];
while (read_word(text_file, string) != EOF)
{
int is_unique = 1;
printf("Got [%s]\n", string);
strip_word(string);
if (string[0] == '\0')
continue;
else
{
int i = 0;
++(*total_count);
for (i = 0; i < (*unique_count); i++)
{
if (strcmp(string, words[i]) == 0)
{
counts[i]++;
is_unique = 0;
break;
}
}
if (is_unique)
{
++(*unique_count);
if ((*unique_count) >= mem_Size)
{
mem_Size = mem_Size * 2;
words = realloc(words, mem_Size * sizeof(char *));
counts = realloc(counts, mem_Size * sizeof(int));
if (words == NULL || counts == NULL)
{
fprintf(stderr, "ERROR: realloc() failed!");
exit(EXIT_FAILURE);
}
printf("Re-allocated parallel arrays to be size %d.\n", mem_Size);
fflush(stdout);
}
words[(*unique_count) - 1] = calloc(strlen(string) + 1, sizeof(char));
strcpy(words[(*unique_count) - 1], string);
counts[(*unique_count) - 1] = 1;
}
}
}
printf("All done (successfully read %d words; %d unique words).\n", *total_count, *unique_count);
fflush(stdout);
*word_array = words;
*count_array = counts;
}
int main(int argc, char *argv[])
{
if (argc < 2 || argc > 3)
{
fprintf(stderr, "ERROR: Invalid Arguements\n");
return EXIT_FAILURE;
}
FILE *text_file = fopen(argv[1], "r");
if (text_file == NULL)
{
fprintf(stderr, "ERROR: Can't open file");
return EXIT_FAILURE;
}
int total_count = 0;
int unique_count = 0;
char **word_array = 0;
int *count_array = 0;
file_parse(text_file, &word_array, &count_array, &total_count, &unique_count);
fclose(text_file);
if (argv[2] == NULL)
{
printf("All words (and corresponding counts) are:\n");
fflush(stdout);
for (int i = 0; i < unique_count; i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
else
{
printf("First %d words (and corresponding counts) are:\n", atoi(argv[2]));
fflush(stdout);
for (int i = 0; i < atoi(argv[2]); i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
for (int i = 0; i < unique_count; i++)
free(word_array[i]);
free(word_array);
free(count_array);
return EXIT_SUCCESS;
}
When run on the data file:
the lion's rock. First, the lion woke up
the output was:
Allocated initial parallel arrays of size 8.
Got [the]
Got [lion]
Got [s]
Got [rock.]
Got [First]
Got [the]
Got [lion]
Got [woke]
Got [up]
All done (successfully read 9 words; 7 unique words).
All words (and corresponding counts) are:
the -- 2
lion -- 2
s -- 1
rock -- 1
First -- 1
woke -- 1
up -- 1
When the code was run on your text, including double quotes, like this:
$ echo '"Pardon, O King,"' | cw37 /dev/stdin
Allocated initial parallel arrays of size 8.
Got ["Pardon]
Got [O]
Got [King]
Got ["]
All done (successfully read 3 words; 3 unique words).
All words (and corresponding counts) are:
Pardon -- 1
O -- 1
King -- 1
$
It took a little finnagling of the code. If there isn't an alphabetic character, your code still counts it (because of subtle problems in strip_word()). That would need to be handled by checking strip_word() more carefully; you test if (string == '\0') which checks (belatedly) whether memory was allocated where you need if (string[0] == '\0') to test whether the string is empty.
Note that the code in read_word() would be confused into reporting EOF if there were two commas in a row, or an apostrophe followed by a comma (though it handles a comma followed by an apostrophe OK). Fixing that is fiddlier; you'd probably be better off using a loop with getc() to read a string of characters. You could even use that loop to strip non-alphabetic characters without needing a separate strip_word() function.
I am assuming you've not yet covered structures yet. If you had covered structures, you'd use an array of a structure such as struct Word { char *word; int count; }; and allocate the memory once, rather than needing two parallel arrays.
I didn't have any error when running the codes below. But on the command console it seems to be stuck on Processing filename xxxxx as below.
May I ask how do I use if condition to filter out number of data characters of less than 50 in line 67, I try to use strlen(array[count].data) >= 1 && strlen(array[count].data) <= 50 but doesn't seem to work ?
Enter filename: testdata.txt
Processing filename testdata.txt ...
Sample Source txt file:
9001:0002:9003:0021:CLS
0001:0010:0003:0021:CLS
8001:0002:8002:0080:<HTML>
0001:4002:0002:0080:<BODY>
0004:0002:0002:0080:JHJKJBKHJBIUHBKBKHBKHHBKJBKJNKJKHHKUHKJLHBKHBKHBHBHBKHBHBHBHBBHHBHBJKJHKJHKJHKUHIUJ
Source code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
//struct definition
struct record{
int src;
int dest;
int type;
int port;
char data[100];
};
int main()
{
struct record *array;
FILE* inFile; //file handle or pointer
FILE* outFile;
FILE* errorFile;
char filename[100];
int count = 0; //keep track of number of records in memory
int i = 0;
int test;
int n1 = 0; //keep track number of correct records
int n2 = 0; //keep track number of error records
array = (struct record *) malloc(sizeof(struct record));
//User to enter filename
printf("Enter filename: ");
scanf("%s", filename);
printf("Processing filename %s ...\n", filename);
inFile = fopen(filename, "r");
if (inFile == NULL) //check if file handler is invalid
{
printf("Could not open file %s\n", filename);
exit(1); //error status code
}
test = fscanf(inFile, "%d:%d:%d:%d",
&array[count].src, &array[count].dest, &array[count].type, &array[count].port);
fgets(array[count].data, 100, inFile);
while (test != EOF){
count++;
array = (struct record *) realloc(array, (count + 1)*sizeof(struct record));
test = fscanf(inFile, "%d:%d:%d:%d:%s",
&array[count].src, &array[count].dest, &array[count].type, &array[count].port, &array[count].data);
}
fclose(inFile); //must always close file once done
outFile = fopen("data_1.txt", "wt");
errorFile = fopen("data_error.txt", "wt");
if (outFile == NULL) //check if file handler is invalid
{
printf("Could not write to file \n", filename);
exit(1);
}
if (count > 0){
printf("Viewing all records: ", count);
for (i = 0; i < count; i++){
if (array[count].src >= 1 && array[count].src <= 1024 && array[count].dest >= 1 && array[count].dest <= 1024 && array[count].type >= 1 && array[count].type <= 10 && array[count].port >= 1 && array[count].port <= 1024)
n1++;
fprintf(outFile, "%d %d %d %d",
(i + 1),
array[count].src,
array[count].dest,
array[count].type,
array[count].port
);
}
}
else
{
n2++;
fprintf(errorFile, "%d %d %d %d",
(i + 1),
array[count].src,
array[count].dest,
array[count].type,
array[count].port
);
}
fclose(errorFile);
fclose(outFile);
return 0;
}
There are many common errors in this. Which book are you reading?
array = (struct record *) malloc(sizeof(struct record));
test = fscanf(inFile, "%d:%d:%d:%d",
&array[count].src, &array[count].dest, &array[count].type, &array[count].port);
fgets(array[count].data, 100, inFile);
while (test != EOF){
count++;
array = (struct record *) realloc(array, (count + 1)*sizeof(struct record));
test = fscanf(inFile, "%d:%d:%d:%d:%s", &array[count].src, &array[count].dest
, &array[count].type, &array[count].port
, &array[count].data);
}
Forgive me. I took the liberty to clean up a bit of your code and condense some of the logic.
You shouldn't cast the return value of malloc or realloc.
While I'm on the topic of types, isn't your compiler warning you about the type of &array[count].data being incompatible with the type that corresponds to %s? Don't cast it. Just remove the & and recognise how the expression array[count].data that denotes an array is converted to a pointer of the appropriate type.
x = realloc(x, ...); where x is whatever expression is (almost) always incorrect. You need to use a temporary variable so you can handle errors correctly without leaking memory (example below)... While I'm on the topic of realloc usage, the first argument can be NULL and realloc will act like malloc in that case, so technically that first malloc is unnecessary bloat...
struct record *array = NULL;
void *temp = realloc(array, (count + 1) * sizeof *array);
if (!temp) {
puts("ERROR ALLOCATING MEMORY!");
exit(EXIT_FAILURE);
}
array = temp;
Have you considered the difference between fgets and fscanf("%s", ...)? Are you aware that the former is for reading lines, while the latter is for reading words? This would explain your problem; fscanf is halting when it sees a space, leaving everything after the space to be processed by the next fscanf call. The result? An infinite loop of fscanf fails.
Have you considered that not all fscanf fails return EOF? If you ask fscanf to read an int (using %d) and it immediately encounters non-decimal digits it can't proceed any further; a conversion failure occurs.
If you want to ensure that fscanf reads all 5 fields successfully, you need to compare the return value to 5, not to EOF.
I think you most likely meant to read the remainder of the line following your four colon-separated ints, and here's how I'd naively do that:
array = NULL;
struct record record;
while (fscanf(inFile, "%d:%d:%d:%d:%99[^\n]", &record.src, &record.dest
, &record.type, &record.port
, record.data) == 5) {
/* It's possible that a user might enter more than *
* 99 chars for the last field, so when that happens *
* we should discard the remainder... */
if (getchar() != '\n') {
puts("WARNING: Maximum field length for data is 99; user input truncated");
fscanf(inFile, "%*[^\n]");
}
void *temp = realloc(array, (count + 1) * sizeof *array);
if (!temp) {
puts("ERROR ALLOCATING MEMORY!");
exit(EXIT_FAILURE);
}
array = temp;
array[count++] = record;
}