Put a string in a matrix in C - 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;
}

Related

string of undetermined length c

Hi I was trying to create an array of string of an undetermined length in c.
This is my code :
int main()
{
int lineCount=linesCount();
char text[lineCount][10];
printf("%d",lineCount);
FILE * fpointer = fopen("test.txt","r");
fgets(text,10,fpointer);
fclose(fpointer);
printf("%s",text);
return 0;
}
I would like to replace 10 in
char text[lineCount][10];
My code reads out a file I already made the amount of lines dynamic.
Since the line length is unpredictable I would like to replace 10 by a something dynamic.
Thanks in advance.
To do this cleanly, we want a char * array rather than an 2D char array:
char *text[lineCount];
And, we need to use memory from the heap to store the individual lines.
Also, don't "hardwire" so called "magic" numbers like 10. Use an enum or #define (e.g) #define MAXWID 10. Note that with the solution below, we obviate the need for using the magic number at all.
Also, note the use of sizeof(buf) below instead of a magic number.
And, we want [separate] loops when reading and printing.
Anyway, here's the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
linesCount(void)
{
return 23;
}
int
main(void)
{
int lineCount = linesCount();
char *text[lineCount];
char buf[10000];
printf("%d", lineCount);
// open file and _check_ the return
const char *file = "test.txt";
FILE *fpointer = fopen(file, "r");
if (fpointer == NULL) {
perror(file);
exit(1);
}
int i = 0;
while (fgets(buf, sizeof(buf), fpointer) != NULL) {
// strip newline
buf[strcspn(buf,"\n")] = 0;
// store line -- we must allocate this
text[i++] = strdup(buf);
}
fclose(fpointer);
for (i = 0; i < lineCount; ++i)
printf("%s\n", text[i]);
return 0;
}
UPDATE:
The above code is derived from your original code. But, it assumes that the linesCount function can predict the number of lines. And, it doesn't check against overflow of the fixed length text array.
Here is a more generalized version that will allow an arbitrary number of lines with varying line lengths:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
int lineCount = 0;
char **text = NULL;
char buf[10000];
// open file and _check_ the return
const char *file = "test.txt";
FILE *fpointer = fopen(file, "r");
if (fpointer == NULL) {
perror(file);
exit(1);
}
int i = 0;
while (fgets(buf, sizeof(buf), fpointer) != NULL) {
// strip newline
buf[strcspn(buf,"\n")] = 0;
++lineCount;
// increase number of lines in array
text = realloc(text,sizeof(*text) * lineCount);
if (text == NULL) {
perror("realloc");
exit(1);
}
// store line -- we must allocate this
text[lineCount - 1] = strdup(buf);
}
fclose(fpointer);
// print the lines
for (i = 0; i < lineCount; ++i)
printf("%s\n", text[i]);
// more processing ...
// free the lines
for (i = 0; i < lineCount; ++i)
free(text[i]);
// free the list of lines
free(text);
return 0;
}

How do I compare string pointers?

#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.

Using malloc, and transferring what's in the allocated memory to a file

so I'm testing out a program that I made and for some reason some things are not working.
Please note that the code that I typed in the first and second problem are not related.
So although the names for some things are the same, they are not in the same .c file, neither do they #include each other
First is this: I FIXED IT!
Second problem is this: I have a for loop that is based on two factors. The first factor affects the value based on the character (reading argv[2]), the second factor affects the value based on how many copies of it self it has. I'm trying to get the data in that for loop into memory, and then from memory to a file.
For some reason, nothing is being transferred into the file.
It looks something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *filename;
float length, rate, bytesper;
char *data;
data = malloc(sizeof(strlen(argv[2]) * length * rate * bytesper);
if(data == NULL)
{
printf("\nMemory could not be allocated.");
return -1;
}
filename = fopen(argv[1], "wb");
if(filename == NULL)
{
printf("File %s was not created", argv[1]);
return -1;
}
for(size_t i = 0; argv[3][i] != '\0'; i++)//reads each character until NULL
{
for(size_t j = 0;
{
int a[i][j];
a[i][j] = value(argv[3][i], j);
//value is the function I made that takes the character and the j value into account
*ptr = a[i][j] + '0';//did + '0' to make sure the int is converted into char
}
}
if( (fwrite(ptr, sizeof(char), rate*strlen(argv[3])*length, filename)) != rate*strlen(argv[3])*length)
{
printf("\nWas not able to copy data to file.");
}
fclose(filename);
free(ptr);
return 0;
}

What is the cause of my segment fault in C?

When I compile my code, I get no errors. However, when I attempt to run it, I get a segmentation fault (core dumped). Here is my main:
Original code
void main(int argc, char *argv[]){
if(argc < 3){
return;
}
char *stop_list_name = argv[1];
char *doc_names[argc - 2];
int i;
for(i = 0; i < argc; i++){
doc_names[i] = argv[i];
}
//create the array of stop words
char *stopWords[50];
char *word;
int word_counter = 0;
FILE *fp;
fp = fopen(stop_list_name, "r");
if(fp != NULL){
while(!feof(fp)){
fscanf(fp, "%s", word);
stopWords[word_counter] = word;
word_counter++;
}
}
fclose(fp);
for(i = 0; stopWords[i] != '\0'; i++){
printf("%s", stopWords[i]);
}
}
I'm pretty sure something is wrong in my while loop, but I don't exactly know what, or how to fix it.
Amended code
After seeing the answers, I modified my code so it looks like this, but it still crashes. What's wrong now?
int main(int argc, char *argv[]){
if(argc < 3){
return;
}
char *stop_list_name = argv[1];
char *doc_names[argc - 2];
int i;
for(i = 2; i < argc; i++){
doc_names[i-2] = argv[i];
}
//create the array of stop words
enum {MAX_STOP_WORDS = 50};
char *stopWords[MAX_STOP_WORDS];
int word_counter = 0;
FILE *fp = fopen(stop_list_name, "r");
if(fp != NULL){
char word[64];
int i;
for(i = 0; i < MAX_STOP_WORDS && fscanf(fp, "%63s", word) == 1; i++){
stopWords[i] = strdup(word);
}
word_counter = i;
fclose(fp);
}
for(i = 0; stopWords[i] != '\0'; i++){
printf("%s", stopWords[i]);
}
}
Problems in the original code
One possible source of problems is:
char *doc_names[argc - 2];
int i;
for(i = 0; i < argc; i++){
doc_names[i] = argv[i];
}
You allocate space for argc-2 pointers and proceed to copy argc pointers into that space. That's a buffer overflow (in this case, a stack overflow too). It can easily cause the trouble. A plausible fix is:
for (i = 2; i < argv; i++)
doc_names[i-2] = argv[i];
However, you really don't need to copy the argument list; you can just process the arguments from index 2 to the end. I note that the code shown doesn't actually use doc_names, but the out-of-bounds assignment can still cause trouble.
You are not allocating space to read a word into, nor allocating new space for each stop word, nor do you ensure that you do not overflow the bounds of the array in which you're storing the words.
Consider using:
enum { MAX_STOP_WORDS = 50 };
char *stopWords[MAX_STOP_WORDS];
int word_counter = 0;
FILE *fp = fopen(stop_list_name, "r");
if (fp != NULL)
{
char word[64];
for (i = 0; i < MAX_STOP_WORDS && fscanf(fp, "%63s", word) == 1; i++)
stopWords[i] = strdup(word);
word_counter = i;
fclose(fp);
}
This diagnosed problem is definitely a plausible cause of your crash. I used i (declared earlier in the code) in the loop because word_counter makes the loop control line too long for SO.
Strictly, strdup() is not a part of standard C, but it is a part of POSIX. If you don't have POSIX, you can write your own:
#include <stdlib.h>
#include <string.h>
char *strdup(const char *str)
{
size_t len = strlen(str) + 1;
char *result = malloc(len);
if (result != 0)
memmove(result, str, len);
return result;
}
You also have some other bad practices on display:
while (!feof(file)) is always wrong.
What should main() return in C and C++?
You should only call fclose(fp) if the fopen() worked, so you need to move the fclose() inside the if statement body.
Problems in the amended code
There's one important and a couple of very minor problems in the amended code:
Your loop that prints the stop words depends on a null pointer (curiously spelled as '\0' — it is a valid but unconventional spelling for a null pointer), but the initialization code doesn't set a null pointer.
There are (at least) two options for fixing that:
Add a null pointer:
for (i = 0; i < MAX_STOP_WORDS-1 && fscanf(fp, "%63s", word) == 1; i++)
stopWords[i] = strdup(word);
stopWords[i] = 0;
fclose(fp);
}
for (i = 0; stopWords[i] != '\0'; i++)
printf("%s\n", stopWords[i]);
Note that the upper bound is now MAX_STOP_WORDS - 1.
Or you can use wordCount instead of a condition:
for (i = 0; i < wordCount; i++)
printf("%s\n", stopWords[i]);
I'd choose the second option.
One reason for doing that is it avoids warnings about wordCount being set and not used — a minor problem.
And doc_names is also set but not used.
I worry about those because my default compiler options generate errors for unused variables — so the code doesn't compile until I fix it. That leads to:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if (argc < 3)
{
fprintf(stderr, "Usage: %s stop-words docfile ...\n", argv[0]);
return 1;
}
char *stop_list_name = argv[1];
char *doc_names[argc - 2];
int i;
for (i = 2; i < argc; i++)
{
doc_names[i - 2] = argv[i];
}
int doc_count = argc - 2;
// create the array of stop words
enum { MAX_STOP_WORDS = 50 };
char *stopWords[MAX_STOP_WORDS];
int word_counter = 0;
FILE *fp = fopen(stop_list_name, "r");
if (fp != NULL)
{
char word[64];
int i;
for (i = 0; i < MAX_STOP_WORDS && fscanf(fp, "%63s", word) == 1; i++)
stopWords[i] = strdup(word);
word_counter = i;
fclose(fp);
}
for (i = 0; i < word_counter; i++)
printf("stop word %d: %s\n", i, stopWords[i]);
for (i = 0; i < doc_count; i++)
printf("document %d: %s\n", i, doc_names[i]);
return 0;
}
And, given a stop words file containing:
help
able
may
can
it
should
do
antonym
prozac
and compiling it (source file sw19.c, program sw19) with:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror sw19.c -o sw19
and running it as:
$ ./sw19 stopwords /dev/null
stop word 0: help
stop word 1: able
stop word 2: may
stop word 3: can
stop word 4: it
stop word 5: should
stop word 6: do
stop word 7: antonym
stop word 8: prozac
document 0: /dev/null
$
You are trying to store the scanned string to an uninitialized pointer,
fscanf(fp, "%s", word);
and word, is not even initialized.
You could use a static buffer for that, just like this
char word[100];
if (fscanf(fp, "%99s", word) != 1)
word[0] = '\0'; /* ensure that `word' is nul terminated on input error */
Also, while (!feof(fp)) is wrong, because the EOF marker wont be set until fscanf() attempts to read past the end of the file, so the code will iterate one extra time. And in that case you would store the same word twice.
Note that you will also need to allocate space for the array of pointers, maybe there you could use malloc().

How to include the spaces?

I am trying to write a program that takes the words from a file, and puts those in a dynamic array. However when I try to run my code the program copies it all except for the spaces. How do I fix this?
This is a test does it work?
But I get the following:
Thisisatestdoesitwork?
char** getWords(char* filename, int* pn){
char** tmp = (char**)malloc( 1000*sizeof(char));
int *temp=(int*)malloc(1000*sizeof(int);
int c;
int counter = 0;
FILE* fileInput = fopen(filename, "r");
if(fileInput == NULL){
return tmp; // return if file open fails
}
while((c=fgetc(fileInput)) != EOF){
result = fscanf(fileInput, "%c", &c); //try to read a character
if(isalpha(c)){ //chararect alphabetical
tmp[counter] = c; // safe int to array
counter ++;
printf("%c", c); fflush(stdout);
}
else{ // if read not succesfull
fscanf(fileInput, ""); // needs to scan anything not a character
}
if(counter > 100){ // to not exceed the array
break;
}
if(feof(fileInput)){ // to check if at the end of the file
break;
}
}
fclose(fileInput); // closing file
*pn = counter;
return tmp;}
My main Function:
int main(int argc, char* argv[]){
int n;
char** a = getWords("opdracht_4_5.c", &n);
if (a != NULL){
puts("gevonden woorden:");
for (int i = 0;i < n; i++){
printf("%3d %s\n",i,a[i]);
}
for (int i = 0;i < n; i++){
free(a);
}
free(a);
}
return (EXIT_SUCCESS);
}
There are quite a few problems with your code. Here's a start:
You don't test the return value of fopen().
You don't test the return value of malloc().
You assign the return value of fgetc() to a variable of type char. Plain char is compatible with either signed char or unsigned char. In order to make a distinction between a character and EOF (which is negative), the fgetc() function returns a character converted to unsigned char (or EOF). You need to test for EOF and then convert the value to a plain char.
The is...() function expects an int argument whose value is in the range of an unsigned char or EOF. If you have a plain char, you first have to cast it to unsigned char, or you can pass the return value of fgetc() straight to isalpha().
You attempt to append an zero-length char array (temp) to an uninitialized char array (s), and you do not test if there is enough room in the target array. This is broken for more reasons than than I care to enumerate.
You allocate memory for an array of 1000 pointers to char, but you never allocate memory for the char pointers themselves.
You try to append your buffer (s) to an uninitialized pointer (*tmp).
You call strlen() on something that is not null-terminated.
You never return the length of the array.
You call a number of functions that have not been declared.
This will read the file, put each word in an array
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
char** getWords(char* filename, int* pn){
char input[100]; // array to hold each word
char** tmp; // double pointer
int counter = 0;
int words = 0;
int c;
tmp = malloc( (*pn)*sizeof(char*)); // allocate pointers for number of words
if ( tmp == NULL) {
printf ( "malloc failed\n");
exit (1);
}
FILE* fileInput = fopen(filename, "r");
if(fileInput == NULL){
printf ( "file open failed\n");
*pn = 0; // no words entered
return tmp; // return if file open fails
}
while(( c = fgetc(fileInput)) != EOF){
if( isalnum(c)){ // is alpha or number
input[counter] = c; // save to array
input[counter + 1] = '\0'; // save a \0 to the end to make a string
counter ++;
}
else{ // not alpha or number
if ( counter > 0) { // if there are characters, save the word
tmp[words] = malloc ( strlen ( input) + 1); // memory for this word
strcpy ( tmp[words], input); // copy the word to the array
words++;
counter = 0;
if ( words >= *pn) { // got all the words wanted
break;
}
}
}
if(counter > 98){ // too many characters for input, start a new word
tmp[words] = malloc ( strlen ( input) + 1);
strcpy ( tmp[words], input);
words++;
counter = 0;
if ( words >= *pn) {
break;
}
}
}
fclose(fileInput); // closing file
*pn = words; // save number of words
return tmp;
}
int main(int argc, char* argv[]){
int n;
int i;
printf ( "enter the number of words to obtain\n");
scanf ( "%d", &n);
char** a = getWords("opdracht_4_5.c", &n);
if (a != NULL){
puts("gevonden woorden:");
for ( i = 0;i < n; i++){
printf("%3d %s\n",i,a[i]);
}
for ( i = 0;i < n; i++){
free(a[i]); // free each word
}
free(a); // free the pointer to the words
}
return (EXIT_SUCCESS);
}
The input file I used had these as the first two lines
#include<stdio.h>
#include<string.h>
I get this output:
enter the number of words to obtain
6
gevonden woorden:
0 include
1 stdio
2 h
3 include
4 string
5 h
This answer is as yet incomplete
Please allow me to finish this before commenting on it -- Thank you
There are a lot if issues with your code, I won't clean it up for you. However I would like to give you some hints on how your program SHOULD be coded:
Your main objective is to read a file and load the content word by word in an array.
Sorting is an incorrect use because that implies you want to sort them alphabetically or in some other order after loading it into an array.
Okay, so first things first, let's figure out the overall operation of our program. We'll call our program kitten, because it's not quite as powerful as cat.
To run our program we will assume that we give it the filename we want to read on the command-line as follows:
$ ./kitten somefile.txt
and expect the output to be:
word1
word2
word3
.
.
.
wordN
Total words: N
So, let's get started, first we make sure that our user specifies a filename:
#include <stdio.h>
int usage(const char *progname);
int main(int argc, char *argv[])
{
if (argc < 2) {
usage(argv[0]);
return -1;
}
return 0;
}
int usage(const char *progname)
{
fprintf(stderr, "Usage is:\n\t%s filename\n", progname);
}
Now that we know that our program can get a filename, let's try to open the text file, if there is an issue with it we use perror to display the error and exit the program, otherwise we are ready to use the file:
#include <stdio.h>
#include <errno.h>
int usage(const char *progname);
int main(int argc, char *argv[])
{
FILE *fp;
if (argc < 2) {
usage(argv[0]);
return -1;
}
fp = fopen(argv[1], "r");
if (!fp) {
perror(argv[1]); /* display system error, with the filename */
return -1;
}
/* TODO: file manipulation goes here */
fclose(fp); /* close the file */
return 0;
}
int usage(const char *progname)
{
fprintf(stderr, "Usage is:\n\t%s filename\n", progname);
}
Now in C each function should perform just one task. The task should make human sense. For example if the function is supposed to read words into an array, then that's all it should do, it should not open a file or close a file, which is WHY the code above does not create a function for opening the file the way you did. Your function should take in FILE * as the file to read.
Because we use the FILE * as input we'll start the function name with an f to keep with the stdio convention. Ideally, the function should take a pointer to char * (strings) to store the words in.
#include <stdio.h>
#include <errno.h>
int usage(const char *progname);
size_t fload(FILE *fp, char **wordlist_p);
int main(int argc, char *argv[])
{
FILE *fp;
if (argc < 2) {
usage(argv[0]);
return -1;
}
fp = fopen(argv[1], "r");
if (!fp) {
perror(argv[1]); /* display system error, with the filename */
return -1;
}
if(fload(fp, wordlist_p) < 0) {
fprintf(stderr, "Something went wrong\n")
}
fclose(fp); /* close the file */
return 0;
}
int usage(const char *progname)
{
fprintf(stderr, "Usage is:\n\t%s filename\n", progname);
}
size_t fload(FILE *fp, char **wordlist_p)
{
size_t rv = -1; /* return value */
return rv;
}
Now we run into a conceptual problem. How do we allocate memory for wordlist_p? I mean we don't have any idea about how big the file is, we also don't know how big the biggest word in the file is.
Crude approach
Let's first try an think about it the simple way:
Point to the beginning of the `wordlist_p` with a `tail_pointer`
Read the file line by line, (we assume no hyphenation)
For each line split the line up along white spaces,
Allocate space for the number of words in the `wordlist_p` array
For each word in the split line
Allocate space for the word itself
Save the pointer to the word at the tail_pointer
Advance wordlist_p tail_pointer
Next word
Next Line
Let's look at what the fload function would look like with these steps above,
More to come ##

Resources