I am very new to C, and I have created a function that removes special characters from a string and returns a new string (without the special characters).
At first glance, this seemed to be working well, I now need to run this function on the lines of a (huge) text file (1 Million sentences). After a few thousand lines/sentences (About 4,000) I get a seg fault.
I don't have much experience with memory allocation and strings in C, I have tried to figure out what the problem with my code is, unfortunately without any luck.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char *preproccessString(char *str) {
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = malloc(sizeof(str));
// Array of allowed chars with a 0 on the end to know when the end of the array is reached, I don't know if there is a more elegant way to do this
// Changed from array to string for sake of simplicity
char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Initalize two integers
// i will be increased for every char in the string
int i = 0;
// j will be increased every time a new char is added to the result
int j = 0;
// Loop over the input string
while (str[i] != '\0') {
// l will be increased for every char in the allowed chars array
int l = 0;
// Loop over the chars in the allowed chars array
while (allowedCharsArray[l] != '\0') {
// If the char (From the input string) currently under consideration (index i) is present in the allowed chars array
if (allowedCharsArray[l] == toupper(str[i])) {
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = toupper(str[i]);
j++;
}
l++;
}
i++;
}
return result;
}
Here is the rest of the program, I think the problem is probably here.
int main(int argc, char *argv[]) {
char const * const fileName = argv[1];
FILE *file = fopen(fileName, "r");
char line[256];
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preproccessString(line));
}
fclose(file);
return 0;
}
You have several problems.
You're not allocating enough space. sizeof(str) is the size of a pointer, not the length of the string. You need to use
char *result = malloc(strlen(str) + 1);
+ 1 is for the terminating null byte.
You didn't add a terminating null byte to the result string. Add
result[j] = '\0';
before return result;
Once you find that the character matches an allowed character, there's no need to keep looping through the rest of the allowed characters. Add break after j++.
Your main() function is never freeing the results of preprocessString(), so you might be running out of memory.
while (fgets(line, sizeof(line), file)) {
char *processed = preproccessString(line);
printf("%s\n", processed);
free(processed);
}
You could address most of these problems if you have the caller pass in the result string, instead of allocating it in the function. Just use two char[256] arrays in the main() function.
int main(int argc, char *argv[])
{
char const* const fileName = argv[1];
FILE* file = fopen(fileName, "r");
char line[256], processed[256];
while (fgets(line, sizeof(line), file)) {
processString(line, processed);
printf("%s\n", processed);
}
fclose(file);
return 0;
}
Then just change the function so that the parameters are:
void preprocessString(const char *str, char *result)
A good rule of thumb is to make sure there is one free for every malloc/calloc call.
Also, a good tool to keep note of for the future is Valgrind. It's very good at catching these kinds of errors.
There are some major issues in your code:
the amount of memory allocated is incorrect, sizeof(str) is the number of bytes in a pointer, not the length of the string it points to, which would also be incorrect. You should write char *result = malloc(strlen(str) + 1);
the memory allocated in preproccessString is never freed, causing memory leaks and potentially for the program to run out of memory on very large files.
you do not set a null terminator at the end of the result string
Lesser issues:
you do not check if filename was passed nor if fopen() succeeded.
there is a typo in preproccessString, it should be preprocessString
you could avoid memory allocation by passing a properly sized destination array.
you could use isalpha instead of testing every letter
you should cast the char values as unsigned char when passing them to toupper because char may be a signed type and toupper is undefined for negative values except EOF.
there are too many comments in your source file, most of which are obvious but make the code less readable.
Here is a modified version:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
// transform the string in `str` into buffer dest, keeping only letters and uppercasing them.
char *preprocessString(char *dest, const char *str) {
int i, j;
for (i = j = 0; str[i] != '\0'; i++) {
if (isalpha((unsigned char)str[i])
dest[j++] = toupper((unsigned char)str[i]);
}
dest[j] = '\0';
return dest;
}
int main(int argc, char *argv[]) {
char line[256];
char dest[256];
char *filename;
FILE *file;
if (argc < 2) {
fprintf(stderr, "missing filename argument\n");
return 1;
}
filename = argv[1];
if ((file = fopen(filename, "r")) == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preprocessString(dest, line));
}
fclose(file);
return 0;
}
The following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
properly checks for length of input string parameter
makes use of characteristic of strchr() also checking the terminating NUL byte
limits scope of visibility of local variables
the calling function is expected to properly cleaning up by passing the returned value to free()
the calling function is expected to check the returned value for NULL
informs compiler the user knows and accepts when an implicit conversion is made.
moves allowedCharsArray to 'file static scope' so does not have to be re-initialized on each pass through the loop and marks as 'const' to help the compiler catch errors
and now the proposed code: (note: edited per comments)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
char *preproccessString(char *str)
{
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = calloc( sizeof( char ), strlen(str)+1);
if( !result )
{
perror( "calloc failed" );
return NULL;
}
// Array of allowed chars
static const char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Loop over the input string
for( int j=0, i=0; str[i]; i++)
{
if( strchr( allowedCharsArray, (char)toupper( str[i] ) ) )
{
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = (char)toupper(str[i]);
j++;
}
}
return result;
}
I think the problem is you are using malloc which allocates memory from the heap and since you are calling this function again and again you are running out of memory.
To solve this issue you have to call the free() function on the pointer returned by your preprocessString function
In your main block
char *result=preprocessString(inputstring);
//Do whatever you want to do with this result
free(result);
Related
To study for the exam we are trying to do some exercise from past exams.
In this exercise we get a header file and we have to create a function that read an input file and print onto the stdout only the parts of strings that do not contain digits.
(We have to pass the pointer of the string red to the main function).
We tried to do it with a an array but when printing the first word is empty or has strange characters. Instead doing a malloc allocation works fine.
What is also strange is that printing before everything an empty string will fix the code.
Therefore we don't understand why using an array of char the first word is not printed correctly, although it is saved in the buffer.
Including a printf before the while loop in the main function will reset the problem.
Using dynamic allocation (malloc) and not static allocation (array) will fix the print.
Iterating over the whole array and set all the memory to 0 does not fix the problem.
Therefore the pointer is correct as with printing an empty string it prints it correctly, but I really cannot understand what cause the issue.
Question are:
How it is possible that printing an empty string the print is correct?
Array is allocated on the stack therefore it is deallocated when the program exit the scope, why is only the first broken and not all the words?
#include "word_reader.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
const char * read_next_word(FILE * f) {
char buffer[WORD_MAX_LEN];
char * word = buffer;
for (int i = 0; i < WORD_MAX_LEN; ++i)
buffer[i] = 0;
//char * buffer = malloc(sizeof(char) * WORD_MAX_LEN);
int found = 0;
int c = 0;
int i = 0;
while (!found && c != EOF) {
while ((c = fgetc(f)) != EOF && isalpha(c)) {
found = 1;
buffer[i] = c;
++i;
}
buffer[i] = '\0';
}
if (found) {
return word;
//return buffer; // when use malloc
}
return 0;
}
int main(int argc, char * argv[]) {
FILE * f = fopen(argv[1], "r");
if(!f) {
perror(argv[1]);
return EXIT_FAILURE;
}
const char * word = 0;
//printf(""); // adding this line fix the problem
while ((word = read_next_word(f))) {
printf("%s\n", word);
}
fclose(f);
return 0;
}
the header file contain only the read_next_word declaration and define WORD_MAX_LEN to 1024. (Also include
the file to read (a simple .txt file)
ciao234 44242 toro
12Tiz23 where333
WEvo23
expected result:
ciao
toro
Tiz
where
WEvo
actual result
�rǫs+)co�0�*�E�L�mзx�<�/��d�c�q
toro
Tiz
where
WEvo
the first line is always some ascii characters or an empty line.
I am newer in C language. I want to create an array for my code to make some operation. As I said above, I am trying to learn how to use C language efficiently. My problem is this: I have a input file, let's say input.txt. I know that every line have 4 different things, 2 of them are string and 2 of them number. Also, I want to create a 2D array. But I do not know how many lines will be in input file. It depends on the user. So, I have to use malloc to make my array dynamically. So, can you help me about this problem? Maybe this is so easy, but I think reading file and create some array in C more difficult than other languages. It was so easy in Python :( I am leaving my code below. If you tell me my mistakes, I will be happy :)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[]) {
char *arrChar;
int i;
char *str;
char *token;
arrChar = (char *) malloc( sizeof( char ) );
str = (char *) malloc( sizeof( char ) );
FILE *FileChars;
FileChars = fopen( argv[1], "r");
i = 0;
while ( fgets(str, sizeof str, FileChars) != NULL) {
int j;
for ( j = 0; j < 4; j++ ) {
token = strtok(str, ",");
arrChar[i][j] = token;
}
i++;
}
}
You need to understand precisely what the sizeof operator does, it doesn't return the size of a dynamically allocated memory block, it returns the size of a type, in case of arrays — roughly speaking — the size is part of the type specification and so it returns the number of bytes the array occupies.
In your case sizeof(char) is the size of the type char which is required to be exactl 1 by the (c-standard C Standard).
And sizeof(str) is the size of the type of str which is char *, that is, the size of a pointer. It's probably 4 or 8 depending on your current platform.
To solve this, you have to define a length to be used throughout your program as the length of the allocated chunk of memory, that after you make sure that the allocation was successful (see below).
A pointer to char can point to a sequence of elements that can be interpreted as a string if it is the correct sequence. A sequence of "printable" characters followed by a '\0' or null character is considered a string.
You have to pass NULL to strtok() after the first time, if you are going to be processing the same string.
You should CHECK that fopen() did return a valid stream, by comparing the return value to NULL.
The same as (5), for malloc() when allocation is not possible NULL is returned and using it as a valid pointer is undefined behavior.
All that said, here is what you probably wanted to write
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_WORDS 100
int main(int argc, char const *argv[])
{
char *token;
char **words;
char line[100];
char *ptr;
size_t count;
FILE *file;
file = fopen( argv[1], "r");
// Check that we DID open the file
if (file == NULL) {
perror(argv[1]);
return EXIT_FAILURE;
}
// Allocate space for `NUM_WORDS' pointers
words = malloc(NUM_WORDS * sizeof(*words));
// Check that we did allocate enough memory
if (words == NULL) {
fclose(file);
return EXIT_FAILURE;
}
// We use `sizeof' here because `line' is an array
count = 0;
while ((count < NUM_WORDS) && (fgets(line, sizeof(line), file) != NULL)) {
ptr = line;
do {
token = strtok(ptr, ",");
// We need to copy the `token' because
// it lies within `line' and the '\0' will
// be replaced by the original character
// in subsequent callse to `strtok()'
//
// The `strdup()' will allocate enough space with
// `malloc()' then copy the contents of `token' to the
// allocated buffer, and return it, so we will
// have to call `free()' on every `words' element.
words[count++] = strdup(token);
// Subsequent calls to `strtok()' with the same
// string require that the first parameter is
// NULL
ptr = NULL;
} while ((count < NUM_WORDS) && (token != NULL));
}
// Now we may print the words and free the memory
for (size_t index = 0; index < count; ++index) {
puts(words[index]);
free(words[index]);
}
free(words);
return 0;
}
Note that the code above, makes sure that we don't exceed the capacity of the array of pointers words1. If you need to resize it, you will need to learn how to use realloc() and do it in a specialized routine so that your code doesn't become too complex.
1Note that the allocated space has no predefined interpretation, we do interpret it as an array but it's not an array in the c sense of an array definition, which line IS, having elements of type char, line can also be interpreted as a string given it has contents compatible with the defintion given in the (2) second point above.
I am writing a program that takes a list of path ( environmental variable), splits the paths and prints it. When compiling it I get a segfault. The following is my output on GDB :
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400eb0 in dest (name=0x7fffffffbce0 "PATH") at executables.c:100
100 dest[i] = malloc(srclen+1);
On valgrind:
==21574== 1 errors in context 2 of 3:
==21574== Use of uninitialised value of size 8
==21574== at 0x400EB0: dest (executables.c:100)
==21574== by 0x400B5B: main (main.c:9)
This is my function:
char** dest(char *name){
int i=0;
char *vp;
const char s[2]=":";
char *token;
char **dest;
name[strlen(name)-1]='\0';
vp=getenv(name);
if(vp == NULL){
exit(1);
}
token =strtok(vp,s);
while( token != NULL ){
size_t srclen = strlen(token);
dest[i] = malloc(srclen+1);
strcpy(dest[i], token);
token = strtok(NULL, s);
i++;
}
dest[i]=NULL;
return dest;
}
And this is my main:
#include "executables.h"
int main(int argc, char **argv){
char *path;
char name[BUFSIZ];
printf("enter name of environment variable:\n");
fgets(name,BUFSIZ,stdin);
char **p=dest(name);
int j=0;
while(p[j]!=NULL){
printf("%s\n",p[j]);
j++;
}
return(0);
}
Use strdup(). Saves steps (accounts for
'\0' too). You have to allocate some memory before hand for the approach you're using. Otherwise you might want a linked list and allocate packets instead of using the array pattern. When you say dest[i] = <ptr value> you're indexing to an offset of unallocated memory and storing something there, so it's a segvio.
#include <string.h>
#define MAXTOKENS 10000
char **get_dest(char *name) {
// Since dest has to be exposed/persist beyond this function all
// need dynamically allocate (malloc()) rather than stack allocate
// of the form of: char *dest[MAXTOKENS].
char *dest = malloc(MAXTOKENS * sizeof (char *)); // <--- need to allocate storage for the pointers
char *vp;
if ((vp = getenv(name)) == NULL)
exit(-1); // -1 is err exit on UNIX, 0 is success
int i = 0;
char *token = strtok(vp, ":");
while (token != NULL) {
dest[i] = strdup(token); // <=== strdup()
token = strtok(NULL, ":");
i++;
}
// dest[i] = NULL; // Why are you setting this to NULL after adding token?
return dest;
}
It's better if main() takes care of passing a proper null-terminated string to the get_dest() function because main is where the finicky fgets() is handled. Generally you want to do things locally where it makes the most sense and is most relevant. If you ever took your get_dest() function and used it somewhere where the strings were not read by fgets() it would just be a wasted step to overwrite the terminator there. So by initializing the char array to zeroes before fgets() you don't have to worry about setting the trailing byte to '\0'.
And finally probably not good to have your function name dest the same name as the variable it returns dest. In some situations having multiple symbols in your program with the same name can get you into trouble.
#include "executables.h"
int main(int argc, char **argv) {
char *path;
char name[BUFSIZ] = { 0 }; // You could initialize it to zero this way
printf("enter name of environment variable:\n");
// bzero(name, BUFSIZ); //... or you could initialize it to zero this way then
fgets(name, BUFSIZ, stdin);
char **p = get_dest(name);
int j = 0;
while(p[j] != NULL) {
printf("%s\n", p[j]);
j++;
free(p[j]); // like malloc(), strdup'd() strings must be free'd when done
}
free(p);
return 0;
}
dest[i] = malloc(srclen + 1);
You need to allocate memory for the pointer to char pointers (dest) as well as each char pointer stored in dest. In the code you provided, neither step is taken.
From the manpage of getenv:
Notes
...
As typically implemented, getenv() returns a pointer to a string
within the environment list. The caller must take care not to modify
this string, since that would change the environment of the process.
Your code violates that rule:
vp=getenv(name);
...
token =strtok(vp,s);
This is an illegal memory write operation.
I have this piece of code outside the main function
mystr * arrstr[] = {
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};
I modified it so that it can read the values from a text file. for this purpose i modified this working code to read line from file into array named string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int i=0,j;
char* string[100];
char line[100];
FILE *file;
file = fopen("patt", "r");
while(fgets(line, sizeof(line), file)!=NULL) {
printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return 0;
}
so the final code is now something like this.
..
..
char *getpatterns(const char *filename) {
int i=0;
char* string[100];
char line[100];
FILE *file;
file = fopen(filename, "r");
while(fgets(line, sizeof(line), file)!=NULL) {
//printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return(string);
}
mystr * arrstr[] = getpatterns("patt");/*{
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};*/
..
..
But i get errors like this.
example1.c: In function ‘getpatterns’:
example1.c:43:2: warning: return from incompatible pointer type [enabled by default]
example1.c:43:2: warning: function returns address of local variable [enabled by default]
example1.c: At top level:
example1.c:45:1: error: invalid initializer
make: *** [example1.o] Error 1
Here line 45 is this line
mystr * arrstr[] = getpatterns("patt");/*{
Please suggest corrective action.
The first warnings are that you are trying to return a char ** as a char * (which is not a good idea), and that you are returning a local variable which is deallocated when the function returns (also not a good idea). The last is telling you that you can't use function calls in initializers of global variables in C (you can do some of that in C++, though I'm not convinced you can do this one).
Fixing it will take some rethinking. You need the function to return allocated memory, or you need to pass the memory to the function. And you'll have to change the type of the global variable. And you'll need to know how many entries there are in the array, somehow.
mystr **arrstr = 0; // Either
mystr *arrstr[100]; // Or
On the whole, I'd probably go with memory allocation and the 'either' declaration:
mystr **arrstr = 0;
char **getpatterns(const char *file)
{
char **array = 0;
...code similar to yours that allocates entries in the array...
...include space for a null pointer to mark the end of the list of strings...
return(array);
}
int main(void)
{
arrstr = getpatterns("patt");
...
}
(Another 'cheat' mechanism would use static char *string[100]; in getpatterns(); you still have to fix the return type and the type of the global variable.)
I tried these but, errors were not resolved: ...
It's impossible to tell exactly what was wrong without your code. However, the code below works for me. The source code was in a file gp.c; the source code prints itself, and releases the memory. Checked under valgrind with a clean bill of health.
Note that your original code did not allocate enough space for the strings it was copying (because you retained the newline read by fgets() — but you were at least using fgets() and not gets(), which is very important). This code uses memmove() — it could use memcpy() instead since there's guaranteed to be no overlap, but memmove() always works and memcpy() doesn't necessarily work when the source data overlaps the target data. It knows how long the string is, so the copy function doesn't need to test for whether the character being copied is a NUL '\0'. The code carefully ensures that there's a null pointer at the end of the list of pointers; that's how you know when you've reached the end of the list of strings. The code also works when gp.c is an empty file.
The algorithm using three items num_xxx, max_xxx, and xxx is a typical way to handle incremental allocation. It typically over-allocates slightly; if you're concerned about the space, you could use strings = realloc(strings, (num_strings+1) * sizeof(*strings)); max_strings = num_strings + 1; at the end of the loop to release the extra space. The + 1 is to allow for the null pointer. By roughly doubling the size allocated each time you allocate, you avoid quadratic behaviour compared with incrementing by one each time.
Notice too that the code carefully avoids losing the allocated space if the realloc() fails. You should 'never' use space = realloc(space, new_size); to avoid losing your pointer. The code carefully avoids dereferencing null pointers, and simply stops reading when there is a memory shortage.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **getpatterns(const char *filename);
char **getpatterns(const char *filename)
{
size_t num_strings = 0;
size_t max_strings = 0;
char **strings = 0;
FILE *file = fopen(filename, "r");
if (file != 0)
{
char line[4096];
while (fgets(line, sizeof(line), file) != NULL)
{
if (max_strings == 0 || num_strings >= max_strings - 1)
{
size_t new_num = max_strings * 2 + 2;
char **new_space = realloc(strings, new_num * sizeof(*new_space));
if (new_space == 0)
break;
strings = new_space;
max_strings = new_num;
}
size_t len = strlen(line); /* Includes '\n' at end */
strings[num_strings] = (char*)malloc(len);
memmove(strings[num_strings], line, len - 1);
strings[num_strings][len] = '\0';
strings[++num_strings] = 0; /* Null terminate list of strings */
}
fclose(file);
}
return(strings);
}
int main(void)
{
char **data = getpatterns("gp.c");
char **argp = data;
if (argp != 0)
{
/* Print data */
while (*argp != 0)
puts(*argp++);
/* Free space */
argp = data;
while (*argp != 0)
free(*argp++);
free(data);
}
return(0);
}
I'm trying to write a program that takes in a plaintext file as it's argument and parses through it, adding all the numbers together and then print out the sum. The following is my code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
static int sumNumbers(char filename[])
{
int sum = 0;
FILE *file = fopen(filename, "r");
char *str;
while (fgets(str, sizeof BUFSIZ, file))
{
while (*str != '\0')
{
if (isdigit(*str))
{
sum += atoi(str);
str++;
while (isdigit(*str))
str++;
continue;
}
str++;
}
}
fclose(file);
return sum;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Please enter the filename as the argument.\n");
exit(EXIT_FAILURE);
}
else
{
printf("The sum of all the numbers in the file is : %d\n", sumNumbers(argv[1]));
exit(EXIT_SUCCESS);
}
return 0;
}
And the text file I'm using is:
This a rather boring text file with
some random numbers scattered
throughout it.
Here is one: 87 and here is another: 3
and finally two last numbers: 12
19381. Done. Phew.
When I compile and try to run it, I get a segmentation fault.
You've not allocated space for the buffer.The pointer str is just a dangling pointer. So your program effectively dumps the data read from the file into memory location which you don't own, leading to the segmentation fault.
You need:
char *str;
str = malloc(BUFSIZ); // this is missing..also free() the mem once done using it.
or just:
char str[BUFSIZ]; // but then you can't do str++, you'll have to use another
// pointer say char *ptr = str; and use it in place of str.
EDIT:
There is another bug in:
while (fgets(str, sizeof BUFSIZ, file))
The 2nd argument should be BUFSIZ not sizeof BUFSIZ.
Why?
Because the 2nd argument is the maximum number of characters to be read into the buffer including the null-character. Since sizeof BUFSIZ is 4 you can read max upto 3 char into the buffer. That is reason why 19381 was being read as 193 and then 81<space>.
You haven't allocated any memory to populate str. fgets takes as its first argument a buffer, not an unassigned pointer.
Instead of char *str; you need to define a reasonably sized buffer, say, char str[BUFSIZ];
Because you've not allocated space for your buffer.
A number of people have already addressed the problem you asked about, but I've got a question in return. What exactly do you think this accomplishes:
if (isdigit(*str))
{
if (isdigit(*str))
{
sum += atoi(str);
str++;
while (isdigit(*str))
str++;
continue;
}
}
What's supposed to be the point of two successive if statements with the exact same condition? (Note for the record: neither one has an else clause).
You have declared char* str, but you have not set aside memory for it just yet. You will need to malloc memory for it.
Many memory related errors such as this one can be easily found with valgrind. I'd highly recommend using it as a debugging tool.
char *str;
str has no memory allocated for it. Either use malloc() to allocate some memory for it, or declared it with a predefined size.
char str[MAX_SIZE];
Your program has several bugs:
It does not handle long lines correctly. When you read a buffer of some size it may happen that some number starts at the end of the buffer and continues at the beginning of the next buffer. For example, if you have a buffer of size 4, there might be the input The |numb|er 1|2345| is |larg|e., where the vertical lines indicate the buffer's contents. You would then count the 1 and the 2345 separately.
It calls isdigit with a char as argument. As soon as you read any "large" character (greater than SCHAR_MAX) the behavior is undefined. Your program might crash or produce incorrect results or do whatever it wants to do. To fix this, you must first cast the value to an unsigned char, for example isdigit((unsigned char) *str). Or, as in my code, you can feed it the value from the fgetc function, which is guaranteed to be a valid argument for isdigit.
You use a function that requires a buffer (fgets) but you fail to allocate the buffer. As others noted, the easiest way to get a buffer is to declare a local variable char buffer[BUFSIZ].
You use the str variable for two purposes: To hold the address of the buffer (which should remain constant over the whole execution time) and the pointer for analyzing the text (which changes during the execution). Make these two variables. I would call them buffer and p (short for pointer).
Here is my code:
#include <ctype.h>
#include <stdio.h>
static int sumNumbers(const char *filename)
{
int sum, num, c;
FILE *f;
if ((f = fopen(filename, "r")) == NULL) {
/* TODO: insert error handling here. */
}
sum = 0;
num = 0;
while ((c = fgetc(f)) != EOF) {
if (isdigit(c)) {
num = 10 * num + (c - '0');
} else if (num != 0) {
sum += num;
num = 0;
}
}
if (fclose(f) != 0) {
/* TODO: insert error handling here. */
}
return sum;
}
int main(int argc, char **argv) {
int i;
for (i = 1; i < argc; i++)
printf("%d\t%s\n", sumNumbers(argv[i]), argv[i]);
return 0;
}
Here is a function, that does your job:
static int sumNumbers(char* filename) {
int sum = 0;
FILE *file = fopen(filename, "r");
char buf[BUFSIZ], *str;
while (fgets(buf, BUFSIZ, file))
{
str=buf;
while (*str)
{
if (isdigit(*str))
{
sum += strtol(str, &str, 10);
}
str++;
}
}
fclose(file);
return sum;
}
This doesn't includes error handling, but works quite well. For your file, output will be
The sum of all the numbers in the file is : 19483