How do I compare string pointers? - c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void sort(char *array[1000][3], int size){
char *temp;
temp = malloc(30);
int j, i;
for(i = 0; i < size-1; i ++){
for(j = j+1; j < size; j ++){
if(strcmp(array[i][0],array[j][0]) > 0){
strcpy(temp, array[i][0]);
strcpy(array[i][0], array[j][0]);
strcpy(array[j][0], temp);
}
}
}
}
int main(){
FILE * myfile;
myfile = fopen("/public/lab4/hurricanes.csv", "r");
char line[100];
char *token;
char *array[1000][3];
int counter = 0;
if(myfile == NULL){
perror("Could not open file");
return 1;
}
while(fgets(line, 100,myfile) != NULL){
token = (char*) malloc((strlen(line)+1) * sizeof(char));
strcpy(token,line);
token = strtok(token, ",");
for(int i = 0; token != NULL; i ++){
array[counter][i] = token;
token = strtok(NULL, ",");
}
counter ++;
}
printf("%s", array[0][0]);
sort(array, counter);
printf("%s", array[0][0]);
return 0;
}
The file gives info on hurricanes with each line looking similar to this
Easy,Category 4 hurricane,5-Sep,1950
Having trouble being able to compare some of the string pointers to sort them alphabetically. Not sure if I need to use malloc to allocate some memory or what I need to do. Right now the array is staying the exact same.

Well, your program has only a few drawbacks, all of them commented in the corrected version in comments. I have edited on top of your source code and tried to conserver as much of your original code as possible, so it is coded as you did in the first place. Please, read the code and don't hesitate to make any comment you want:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* better use constants, so in case you have to change them, you
* only change them here. */
#define MAX 1000
#define NFIELDS 3
/* this is for pretty formatting error messages. */
#define F(_fmt) __FILE__":%d:%s: "_fmt, __LINE__, __func__
/* if you pass the field number used to sort, then you
* have a more flexible way to sort (any field can be used)
*/
void sort(char *array[MAX][NFIELDS], int size, int field)
{
/* space for NFIELDS pointers, no need to malloc it */
char *temp[NFIELDS];
int j, i;
for(i = 0; i < size - 1; i++) {
/* ---v--- oops!!! (there was a j here) :) */
for(j = i+1; j < size; j++) {
if(strcmp(array[i][field], array[j][field]) > 0){
/* exchange the arrays of NFIELD pointers */
memcpy(temp, array[i], sizeof temp);
memcpy(array[i], array[j], sizeof array[i]);
memcpy(array[j], temp, sizeof array[j]);
}
}
}
}
void print_array(char *array[MAX][NFIELDS], int size)
{
int i;
for (i = 0; i < size; i++) {
int j;
printf("#%d:", i);
for (j = 0; j < NFIELDS; j++) {
printf(" %s", array[i][j]);
}
printf("\n");
}
}
int main()
{
char *filename = "/public/lab4/hurricanes.csv";
/* better do the declaration and the initialization at the same time */
FILE *myfile = fopen(filename, "r");
char line[100];
char *array[MAX][NFIELDS];
int counter = 0;
if(myfile == NULL){
fprintf(stderr,
F("FOPEN: %s: %s(errno = %d)\n"),
filename, strerror(errno), errno);
return 1;
}
/* don't hesitate to use the sizeof operator below, it will save you a
* lot of nightmares */
while(fgets(line, sizeof line, myfile) != NULL) {
/* don't do anything if we don't have enough space */
if (counter >= MAX) {
fprintf(stderr, F("MAX NUMBER OF LINES EXHAUSTED (%d)\n)"), MAX);
exit(1);
}
/* NEVER, NEVER, NEVER... cast the result of malloc. See text
* below */
/* by the way, why don't use strdup(3) instead? (it does the
* allocation and the copy in one shot) */
char *token = strdup(line);
if (!token) {
fprintf(stderr, F("Not enough memory: %s (errno = %d)\n"),
strerror(errno), errno);
exit(1);
}
/* don't use strtok, because it considers a sequence of commas as
* a single delimiter, use strsep(3), that will consider ",," as
* three empty strings. By the way, be careful because in your
* sample data you have included commas in the dates. */
int i;
char *s;
/* Add also \n to the separator string, so you don't get the
* last \n included in the last field.
/* Here: ---vv--- */
for (i = 0;
(i < NFIELDS) && (s = strsep(&token, ",\n")) != NULL;
i++)
{
array[counter][i] = s;
}
counter++;
}
print_array(array, counter);
sort(array, counter, 2);
printf("\n");
print_array(array, counter);
return 0;
}
Note: Never cast the result of malloc(3) this is a legacy from ancient times, when there was no void type to allow for automatic pointer conversion. Casting malloc makes the compiler to silently comply if you forget to #include <stdlib.h> and this can make an error if pointer types are not the same size as integer types (you get an undefined behaviour on the compiler assuming by mistake that malloc() returns an int, but as you have stated so in the source, there will be no message from the compiler) Casting malloc(3) is a very bad habit, and makes you more difficult to search for errors.

Related

How to populate Dynamic array with Strings in C

I am doing a project where I have to read in text from a file and then extract every word that is 4 characters long and allocate it into dynamic array.My approach is to create int function that will get number of 4 letter words and return that number , then create another function that will grab that number and create dynamic array consisting of that many elements. The problem with this approach is how to populate that array with words that meet the requirement.
int func1(FILE *pFile){
int counter = 0;
int words = 0;
char inputWords[length];
while(fscanf(pFile,"%s",inputWords) != EOF){
if(strlen(inputWords)==4){
#counting 4 letter words
counter++;
}
}
}
return counter;
}
int main(){
#creating pointer to a textFile
FILE *pFile = fopen("smallDictionary.txt","r");
int line = 0;
#sending pointer into a function
func1(pFile);
fclose(pFile);
return 0;
}
I would suggest reading lines of input with fgets(), and breaking each line into tokens with strtok(). As each token is found, the length can be checked, and if the token is four characters long it can be saved to an array using strdup().
In the code below, storage is allocated for pointers to char which will store the addresses of four-letter words. num_words holds the number of four-letter words found, and max_words holds the maximum number of words that can currently be stored. When a new word needs to be added, num_words is incremented, and if there is not enough storage, more space is allocated. Then strdup() is used to duplicate the token, and the address is assigned to the next pointer in words.
Note that strdup() is not in the C Standard Library, but that it is POSIX. The feature test macro in the first line of the program may be needed to enable this function. Also note that strdup() allocates memory for the duplicated string which must be freed by the caller.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SZ 1000
#define ALLOC_INC 100
int main(void)
{
FILE *fp = fopen("filename.txt", "r");
if (fp == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char buffer[BUF_SZ];
char **words = NULL;
size_t num_words = 0;
size_t max_words = 0;
char *token;
char *delims = " \t\r\n";
while (fgets(buffer, sizeof buffer, fp) != NULL) {
token = strtok(buffer, delims);
while (token != NULL) {
if (strlen(token) == 4) {
++num_words;
if (num_words > max_words) {
max_words += ALLOC_INC;
char **temp = realloc(words, sizeof *temp * max_words);
if (temp == NULL) {
perror("Unable to allocate memory");
exit(EXIT_FAILURE);
}
words = temp;
}
words[num_words-1] = strdup(token);
}
token = strtok(NULL, delims);
}
}
if (fclose(fp) != 0) {
perror("Unable to close file");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < num_words; i++) {
puts(words[i]);
}
/* Free allocated memory */
for (size_t i = 0; i < num_words; i++) {
free(words[i]);
}
free(words);
return 0;
}
Update
OP has mentioned that nonstandard functions are not permitted in solving this problem. Though strdup() is POSIX, and both common and standard in this sense, it is not always available. In such circumstances it is common to simply implement strdup(), as it is straightforward to do so. Here is the above code, modified so that now the function my_strdup() is used in place of strdup(). The code is unchanged, except that the feature test macro has been removed, the call to strdup() has been changed to my_strdup(), and of course now there is a function prototype and a definition for my_strdup():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SZ 1000
#define ALLOC_INC 100
char * my_strdup(const char *);
int main(void)
{
FILE *fp = fopen("filename.txt", "r");
if (fp == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char buffer[BUF_SZ];
char **words = NULL;
size_t num_words = 0;
size_t max_words = 0;
char *token;
char *delims = " \t\r\n";
while (fgets(buffer, sizeof buffer, fp) != NULL) {
token = strtok(buffer, delims);
while (token != NULL) {
if (strlen(token) == 4) {
++num_words;
if (num_words > max_words) {
max_words += ALLOC_INC;
char **temp = realloc(words, sizeof *temp * max_words);
if (temp == NULL) {
perror("Unable to allocate memory");
exit(EXIT_FAILURE);
}
words = temp;
}
words[num_words-1] = my_strdup(token);
}
token = strtok(NULL, delims);
}
}
if (fclose(fp) != 0) {
perror("Unable to close file");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < num_words; i++) {
puts(words[i]);
}
/* Free allocated memory */
for (size_t i = 0; i < num_words; i++) {
free(words[i]);
}
free(words);
return 0;
}
char * my_strdup(const char *str)
{
size_t sz = strlen(str) + 1;
char *dup = malloc(sizeof *dup * sz);
if (dup) {
strcpy(dup, str);
}
return dup;
}
Final Update
OP had not posted code in the question when the above solution was written. The posted code does not compile as is. In addition to missing #includes and various syntax errors (extra braces, incorrect comment syntax) there are a couple of more significant issues. In func1(), the length variable is used uninitialized. This should be large enough so that inputWords[] can hold any expected word. Also, width specifiers should be used with %s in scanf() format strings to avoid buffer overflow. And, OP code should be checking whether the file opened successfully. Finally, func1() returns a value, but the calling function does not even assign this value to a variable.
To complete the task, the value returned from func1() should be used to declare a 2d array to store the four-letter words. The file can be rewound, but this time as fscanf() retrieves words in a loop, if a word has length 4, strcpy() is used to copy the word into the array.
Here is a modified version of OP's code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_WORD 100
int func1(FILE *pFile){
int counter = 0;
char inputWords[MAX_WORD];
while(fscanf(pFile,"%99s",inputWords) != EOF) {
if(strlen(inputWords) == 4) {
counter++;
}
}
return counter;
}
int main(void)
{
FILE *pFile = fopen("filename.txt","r");
if (pFile == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char inputWords[MAX_WORD];
int num_4words = func1(pFile);
char words[num_4words][MAX_WORD];
int counter = 0;
rewind(pFile);
while(fscanf(pFile,"%99s",inputWords) != EOF) {
if(strlen(inputWords) == 4) {
strcpy(words[counter], inputWords);
counter++;
}
}
if (fclose(pFile) != 0) {
perror("Unable to close file");
}
for (int i = 0; i < num_4words; i++) {
puts(words[i]);
}
return 0;
}

Can't eliminate one character in my array while parsing it even though I handle that character

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.

Array result are correct within while loop, but broken outside of it

I'm currently writing a program that is doing what is a simple task; read a file line by line, parse it, and store the results into an array where the structure would be array[lineNumber][lineElement]. And for the most part, it's working, except for one odd issue that I've ran into.
In the code below, any access to the array that is housing the data outside of the while loop that's populating it, only returns the last entry. This occurs regardless of the key for lineNumber. Basically it acts like it's overwriting, even though within the while loop its accessible just fine. The only two items that I think could be at fault I've outlined in bold, although for char *processData[100];, it shouldn't be an issue as it's stored within an array that's declared outside the while loop (and if I remember right while loops shouldn't have scope?), and the other line **char **processArray[100];
**, it might be the double star for an array of pointers, but returning that to just one star introduces a whole wave of bugs, namely the aforementioned array structure breaks completely.
So in a nutshell, not being a C expert by any means and exhausting my resources for this issue, I wonder if the C coders here might have some advice as to what the heck is going on, and how I can get this to work as intended....if I even can.
As mentioned previously, the code.
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
FILE *ifp;
char line[80];
int returnValue = 0;
//Open file
ifp = fopen("dataFile", "rt");
if (ifp == NULL) {
fprintf(stderr, "Can't open input file!\n");
returnValue = 1;
}
int lineCounter = 0;
char **processArray[100];
while(fgets(line, 80, ifp) != NULL) {
char *processData[100];
char *p = strtok(line, " ,\n");
int keyCounter = 0;
while (p != NULL) {
processData[keyCounter] = p;
p = strtok(NULL, " ,\n");
keyCounter++;
}
processArray[lineCounter] = processData;
printf("%d\n", lineCounter);
printf("Inside -> %s\n", processArray[0][0]);
lineCounter++;
}
printf("Outside %s\n", processArray[0][0]);
fclose(ifp);
int i;
int j;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
printf("%d-%d => %s\n ", i, j, processArray[i][j]);
}
}
return returnValue;
}
[Just about] everything gets overwritten on the outer while loop, so only the last processed line remains. The intermediate results must be preserved
I've fixed the program with annotations as to the bugs. The style is #if 0 /* original code */ #else /* fixed code */ #endif
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
FILE *ifp;
char line[80];
int returnValue = 0;
//Open file
ifp = fopen("dataFile", "rt");
if (ifp == NULL) {
fprintf(stderr, "Can't open input file!\n");
returnValue = 1;
}
int lineCounter = 0;
char **processArray[100];
// NOTE/BUG: things get lost on each iteration of this loop
while(fgets(line, 80, ifp) != NULL) {
char *processData[100];
char *p = strtok(line, " ,\n");
int keyCounter = 0;
while (p != NULL) {
// NOTE/BUG: p will get overwritten -- so we must save the string
#if 0
processData[keyCounter] = p;
#else
processData[keyCounter] = strdup(p);
#endif
p = strtok(NULL, " ,\n");
keyCounter++;
}
// NOTE/BUG: processData must be duplicated -- it is overwritten
// on the outer loop
#if 0
processArray[lineCounter] = processData;
#else
char **pA = malloc(sizeof(char *) * keyCounter);
processArray[lineCounter] = pA;
for (int copyidx = 0; copyidx < keyCounter; ++copyidx)
pA[copyidx] = processData[copyidx];
#endif
printf("%d\n", lineCounter);
printf("Inside -> %s\n", processArray[0][0]);
lineCounter++;
}
printf("Outside %s\n", processArray[0][0]);
fclose(ifp);
int i;
int j;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
printf("%d-%d => %s\n ", i, j, processArray[i][j]);
}
}
return returnValue;
}
processData is being allocated on the stack and the memory address is not valid after you leave the while loop, regardless of you storing it in processArray. You need to allocate from the heap instead (using malloc or some other memory allocation function)

Reading each line of file into array

I'm reading a file and want to put each line into a string in an array. The length of the file is arbitrary and the length of each line is arbitrary (albeit assume it will be less than 100 characters).
Here's what I've got and it's not compiling. Essentially this is an array to an array of characters, right? So shouldn't it be char** words = (**char)malloc(sizeof(*char));?
#include <stdio.h>
#include <stdlib.h>
int main(){
int BUFSIZE = 32767;//max number of lines to read
char** words = (**char)malloc(sizeof(*char));//gives error: expected expression before 'char'
FILE *fp = fopen("coll.txt", "r");
if (fp == 0){
fprintf(stderr, "Error opening file");
exit(1);
}
int i = 0;
words[i] = malloc(BUFSIZE);
while(fscanf(fp, "%100s", words[i]) == 1)//no line will be longer than 100
{
i++;
words[i] = realloc(words, sizeof(char*)*i);
}
int j;
for(j = 0; j < i; j++)
printf("%s\n", words);
return 0;
}
Note: I've read "Reading from a file and storing in array" but it doesn't answer my question.
There are a few issues with your program. The realloc() statement is not used correctly. I also prefer fgets() for getting a line. Here is my solution. This also uses realloc() to increase the allocation of the buffer lines so that you neither have to know the number of lines in advance nor do you have to read the file in two passes (faster that way). This is a common technique to use when you don't know how much memory you'll have to allocate in advance.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int lines_allocated = 128;
int max_line_len = 100;
/* Allocate lines of text */
char **words = (char **)malloc(sizeof(char*)*lines_allocated);
if (words==NULL)
{
fprintf(stderr,"Out of memory (1).\n");
exit(1);
}
FILE *fp = fopen("coll.txt", "r");
if (fp == NULL)
{
fprintf(stderr,"Error opening file.\n");
exit(2);
}
int i;
for (i=0;1;i++)
{
int j;
/* Have we gone over our line allocation? */
if (i >= lines_allocated)
{
int new_size;
/* Double our allocation and re-allocate */
new_size = lines_allocated*2;
words = (char **)realloc(words,sizeof(char*)*new_size);
if (words==NULL)
{
fprintf(stderr,"Out of memory.\n");
exit(3);
}
lines_allocated = new_size;
}
/* Allocate space for the next line */
words[i] = malloc(max_line_len);
if (words[i]==NULL)
{
fprintf(stderr,"Out of memory (3).\n");
exit(4);
}
if (fgets(words[i],max_line_len-1,fp)==NULL)
break;
/* Get rid of CR or LF at end of line */
for (j=strlen(words[i])-1;j>=0 && (words[i][j]=='\n' || words[i][j]=='\r');j--)
;
words[i][j+1]='\0';
}
/* Close file */
fclose(fp);
int j;
for(j = 0; j < i; j++)
printf("%s\n", words[j]);
/* Good practice to free memory */
for (;i>=0;i--)
free(words[i]);
free(words);
return 0;
}
You should change the line:
char** words = (**char)malloc(sizeof(*char));
into this:
char** words=(char **)malloc(sizeof(char *)*Max_Lines);

Put a string in a matrix in C

I need to put a string (from a file) in a matrix and print out the result. I have some issue in understanding the right way to do this so:
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[])
{
const int MAX = 50;
char mat[MAX][MAX];
char str[MAX];
char word[MAX];
int row = 0;
int i = 0;
FILE * fp;
fp = fopen ("file.txt", "r");
if (fp == NULL)
printf ("Error!\n");
while (fgets(str, MAX, fp) != NULL)
{
sscanf (str, "%s\n", word);
strcpy(mat[i][0], word);
row++;
}
for (i = 0; i <= row; i++)
{
puts(mat[i][0]);
}
return 0;
}
I'm obliviously doing something wrong but... what?
I have a file like this:
One
Two
Three
Four
Five
Six
Hello
If you compile this with gcc, it will give you two warnings: each warning points to one of the three major errors in the code:
main.c: In function 'main':
main.c:24: warning: passing argument 1 of 'strcpy' makes pointer from integer without a cast
main.c:31: warning: passing argument 1 of 'puts' makes pointer from integer without a cast
Each of those line numbers -- 24 and 31 -- is a line where you're using mat[i][0], which is a character, when you should instead use mat[i], which is a character array. Fix those, and then there's just one problem: you use i, which is always 0, in the while loop. Use row, which is incremented as the row progresses, and the program should work exactly as designed.
There are a couple of other things I would change to improve the program: your while loop reads a string into one buffer, copies it into a second buffer, then copies it into the matrix; you could just scan it directly into the matrix and be done with it!
A matrix usually contains numbers. Yours contains chars. There are problems with your code but to get a good answer you should tell us the format of the file you are reading (maybe paste a small one in your question).
EDIT:
This file contains an array of strings separated by newline. You can read it like this (if the strings don't have any whitespace in them):
while (fscanf(fp, "%s\n", mat[row]) > 0)
{
row++;
}
for (i = 0; i <= row; i++)
{
printf( "%s\n", mat[i]);
}
You should copy the string to the element of matrix with the given index. You should pass the pointer to the first element of matrix to strcpy(i.e. remove the [0]).
Do something like:
while (fgets(str, MAX, fp) != NULL)
{
sscanf (str, "%s\n", word);
strcpy(mat[i], word);
row++;
}
EDIT: also when printing the strings use only mat[i] not mat[i][0].
I have done some changes to your code. First lets pin point your mistakes.
char mat[MAX][MAX]; will not serves your intention of creating string matrix.
It is unnecessary to do read sscanf (str, "%s\n", word);. Already you read it from file. you can directly use it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
const int MAX = 50;
char *mat[MAX][MAX];
char str[MAX];
int i = 0, j = 0;
int now = 0;
FILE * fp;
fp = fopen ("file.txt", "r");
if (fp == NULL)
printf ("Error!\n");
while (fgets(str, MAX, fp) != NULL)
{
//sscanf (str, "%s\n", word);
mat[i][j] = malloc(sizeof(str));
strcpy(mat[i][j], str);
j++;
now++; //Tracks no.of elements
if(j == MAX)
{
j = 0;
i++; //store in next row
}
}
for (i = 0; i < MAX; i++)
for (j = 0; j < MAX; j++)
{
if(now == 0)
break;
now--;
puts(mat[i][j]);
free(mat[i][j]);//Avoids memory leak
}
return 0;
}

Resources