This function is supposed to get a file of vectors. The first line
contains the dimension. All other line are in the form of
"P:3,5,2". The letter is P or N and the numbers are coordinates.
This function read a line each time it is being invoked and it saves the P/N char and the
coordinates into an array of double.
void readSamplesFile(FILE *sample_p, double result[])
{
if (dimension == 0)
{
fscanf(sample_p,"%lf",&dimension);
}
printf("dimentions: %lf\n",dimension);
printf("LINE \n");
int index;
for (index = 0; index<dimension; index++)
{
printf("%d",index);
fscanf(sample_p,"%lf%*[:,]",&result[index]);
printf("%lf",result[index]);
}
}
when i run it i get an endless loop. the dimension is read correctly but the
it prints
LINE
00.00000010.000000dimentions: 2.000000
endlessly. any ideas why?
hope i was clear
EDIT:
I've added the calling function:
void fillArray(FILE *sample_p,FILE *separators_p){
double coordinates[MAX_DIMENSION];
while (!feof(sample_p)){
readSamplesFile(sample_p,coordinates);
}
}
p.s.
fscanf is set to read : and , but to ignore them.
Neither 'P' nor 'N' is a valid double, nor are they ':' or ',', so the fscanf() fails. You should always check the return value from fscanf().
We can also debate whether you'd be better off using fgets() to read a line and sscanf() to parse it. Doing so avoids some issues; it is the way I'd code it automatically.
This code seems to work on the input file:
3
P:3,5,2
N:21.12,2.345e6,1.9132e-34
yielding the output:
dimension: 3.000000
LINE: P:3,5,2
P:offset=2:0=3(2):1=5(4):2=2(6):
LINE: N:21.12,2.345e6,1.9132e-34
N:offset=2:0=21.12(2):1=2.345e+06(8):2=1.9132e-34(16):
I'm still not keen on the (mis)use of a floating point dimension, but it works.
#include <stdio.h>
enum { MAX_DIMENSION = 6 };
enum { MAX_BUFFSIZE = 4096 };
static double dimension = 0.0;
static int get_dimension(FILE *fin)
{
char buffer[MAX_BUFFSIZE];
if (fgets(buffer, sizeof(buffer), fin) == 0)
return -1;
if (sscanf(buffer, "%lf", &dimension) != 1)
return -1;
printf("dimension: %lf\n", dimension);
return 0;
}
static int readSamplesFile(FILE *sample_p, double result[])
{
char buffer[MAX_BUFFSIZE];
if (fgets(buffer, sizeof(buffer), sample_p) == 0)
return -1;
printf("LINE: %s", buffer);
char c;
int offset;
if (sscanf(buffer, " %c:%n", &c, &offset) != 1)
return -1;
printf("%c:", c);
printf("offset=%d:", offset);
for (int index = 0; index < dimension; index++)
{
int newoff;
if (sscanf(&buffer[offset], "%lf%*[:,]%n", &result[index], &newoff) < 1)
return -1;
printf("%d=%g(%d):", index, result[index], offset);
offset += newoff;
}
putchar('\n');
return 0;
}
static void fillArray(FILE *sample_p)
{
double coordinates[MAX_DIMENSION];
while (readSamplesFile(sample_p, coordinates) == 0)
;
}
int main(void)
{
if (get_dimension(stdin) == 0)
fillArray(stdin);
return 0;
}
Note that the fillArray() function, as written, does not do anything with the line of data. There is no checking that the dimension specified is positive and not greater than MAX_DIMENSION (that would go in get_dimension()). It feels cleaner to separate get_dimension() into a separate function than to hide it inside readSampleFile(). There is an argument that readSampleFile() should be renamed readSampleLine() since it does only process one line at a time, not a whole file at a time.
The use of the %n format specifier is a little tricky, but the code needs to know where to resume reading the buffer on the next cycle.
Related
I am writing code to sort a txt file of int's and then display them when the user asks for a number at a certain index but every time I run my code I get a segmentation fault.
What can I do to fix this?
void insert_sorted(long *sorted, int count, long value)
{
int i = 0;
sorted[1024] = value;
if (count == 0) return;
for (i = count; i >= 0; i--) {
if (value < sorted[i - 1])
sorted[i] = sorted[i - 1];
else break;
}
sorted[i] = value;
}
int main(int argc, char *argv[])
{
FILE *infile = NULL;
int count = 0;
long sorted[1024];
long value;
int i = 0;
if (argc < 2) {
fprintf(stderr, "Usage : %s <file_name>/n", argv[0]);
return 1;
}
infile = fopen(argv[1], "r");
if (NULL == infile) {
perror("fopen");
return -1;
}
/* while file not ends */
while (!feof(infile)) {
fscanf(infile, "%ld\n", &value); /* fetch value */
insert_sorted(sorted, count, value); /* sort */
++count; /* increase number of sorted values */
}
/* display values */
printf("Enter Index : ");
int index;
scanf("%d", &index);
if (index == -1)
fclose(infile);
printf("%d ", sorted[index]);
/* cleanup */
if (infile) {
fclose(infile);
infile = NULL;
}
return 0;
}
The operation in sorted[1024] =value; will crash your program. The size of sorted array is only 1024 so that the bigest index is 1023.
One way to fix that is to change the size of sorted to 1025 in the main() function.
An other operation in the for loop that can crash the program : while i =0, access to sorted [ i - 1 ] will catch an exception.
The size of array sorted is 1024:
long sorted[1024];
which means the valid index of array sorted range from 0 to 1023.
In insert_sorted() function, you are trying to access sorted array beyond its range:
sorted[1024] = value;
^^^^
Accessing an element of array beyond its size is undefined behavior which includes program may give segmentation fault.
In fact, there is a logical error in insert_sorted() function. It is never storing the first value at appropriate location in the sorted array because of this count value check:
if (count == 0) return;
For the first time count value will be 0 and the function return without storing value at appropriate location in sorted array. Also, you should put a check on count value and if it is greater than the array size, function should give appropriate error message. You can do:
#define ARR_SZ 1024
void insert_sorted(long *sorted, int count, long value) {
int i = 0;
if (count > ARR_SZ) {
fprintf (stderr, "Array is exhausted");
//may you want to change the return type of function from void to
//int and return a value in case of error.
//Based on the return value, the caller of this function can take
//appropriate action.
return;
}
for (i = count; i > 0; i--) {
if (value < sorted[i - 1])
sorted[i] = sorted[i - 1];
else
break;
}
sorted[i] = value;
}
int main(int argc, char *argv[])
{
FILE *infile = NULL;
int count = 0;
long sorted[ARR_SZ];
......
......
......
}
When you are taking index input from user, make sure to add a check whether it is less than 0 or greater than or equal to count. If it is, you should not access sorted[index].
scanf("%d", &index);
if ((index >= count) || (index < 0)) {
//print error message and do not access sorted[index]
}
You don't need this:
if (index == -1)
fclose(infile);
because of this when user enters -1, the fclose is getting called twice for infile. Make sure once you have closed the FILE * of a opened file, don't call fclose again on it.
Segmentation fault appear when you try to write in an element array with a key that is bigger than his dimension.
In this case you are writing on
sorted[1024] = value
that has as biggest key 1023 (long sorted[1024] means from 0 to 1023).
You should change
long sorted[1024];
with
long sorted[1025];
else you should change
sorted[1024] = value;
with
sorted[1023] = value;
I didn't read your code for telling you which is better.
What can I do to fix this?
You can run the program in a debugger.
You can either step through the code, one instruction at a time, inspecting the values of your variables as you go, or you can simply let the program run under the watchful eye of the debugger. If and when the program causes a seg-fault, the debugger will interrupt it at the exact point of failure, allowing you to see which code is responsible. It will even let you see the values of the variables involved, if there's any doubt about why the code did what it did.
I made this code:
/*here is the main function*/
int x , y=0, returned_value;
int *p = &x;
while (y<5){
printf("Please Insert X value\n");
returned_value = scanf ("%d" , p);
validate_input(returned_value, p);
y++;
}
the function:
void validate_input(int returned_value, int *p){
getchar();
while (returned_value!=1){
printf("invalid input, Insert Integers Only\n");
getchar();
returned_value = scanf("%d", p);
}
}
Although it is generally working very well but when I insert for example "1f1" , it accepts the "1" and does not report any error and when insert "f1f1f" it reads it twice and ruins the second read/scan and so on (i.e. first read print out "invalid input, Insert Integers Only" and instead for waiting again to re-read first read from the user, it continues to the second read and prints out again "invalid input, Insert Integers Only" again...
It needs a final touch and I read many answers but could not find it.
If you don't want to accept 1f1 as valid input then scanf is the wrong function to use as scanf returns as soon as it finds a match.
Instead read the whole line and then check if it only contains digits. After that you can call scanf
Something like:
#include <stdio.h>
int validateLine(char* line)
{
int ret=0;
// Allow negative numbers
if (*line && *line == '-') line++;
// Check that remaining chars are digits
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1; // Remember that at least one legal digit was found
++line;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin)) // Read the whole line
{
if (validateLine(line)) // Check that the line is a valid number
{
// Now it should be safe to call scanf - it shouldn't fail
// but check the return value in any case
if (1 != sscanf(line, "%d", &x))
{
printf("should never happen");
exit(1);
}
// Legal number found - break out of the "while (y<5)" loop
break;
}
else
{
printf("Illegal input %s", line);
}
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Input
1f1
f1f1
-3
Output
Please Insert X value
Illegal input 1f1
Please Insert X value
Illegal input f1f1
Please Insert X value
Illegal input
Please Insert X value
x=-3
Another approach - avoid scanf
You could let your function calculate the number and thereby bypass scanf completely. It could look like:
#include <stdio.h>
int line2Int(char* line, int* x)
{
int negative = 0;
int ret=0;
int temp = 0;
if (*line && *line == '-')
{
line++;
negative = 1;
}
else if (*line && *line == '+') // If a + is to be accepted
line++; // If a + is to be accepted
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1;
// Update the number
temp = 10 * temp;
temp = temp + (*line - '0');
++line;
}
if (ret)
{
if (negative) temp = -temp;
*x = temp;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin))
{
if (line2Int(line, &x)) break; // Legal number - break out
printf("Illegal input %s", line);
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Generally speaking, it is my opinion that you are better to read everything from the input (within the range of your buffer size, of course), and then validate the input is indeed the correct format.
In your case, you are seeing errors using a string like f1f1f because you are not reading in the entire STDIN buffer. As such, when you go to call scanf(...) again, there is still data inside of STDIN, so that is read in first instead of prompting the user to enter some more input. To read all of STDIN, you should do something the following (part of code borrowed from Paxdiablo's answer here: https://stackoverflow.com/a/4023921/2694511):
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // Used for strtol
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
#define NaN 3 // Not a Number (NaN)
int strIsInt(const char *ptrStr){
// Check if the string starts with a positive or negative sign
if(*ptrStr == '+' || *ptrStr == '-'){
// First character is a sign. Advance pointer position
ptrStr++;
}
// Now make sure the string (or the character after a positive/negative sign) is not null
if(*ptrStr == NULL){
return NaN;
}
while(*ptrStr != NULL){
// Check if the current character is a digit
// isdigit() returns zero for non-digit characters
if(isdigit( *ptrStr ) == 0){
// Not a digit
return NaN;
} // else, we'll increment the pointer and check the next character
ptrStr++;
}
// If we have made it this far, then we know that every character inside of the string is indeed a digit
// As such, we can go ahead and return a success response here
// (A success response, in this case, is any value other than NaN)
return 0;
}
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
// (Per Chux suggestions in the comments, the "buff[0]" condition
// has been added here.)
if (buff[0] && buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
void validate_input(int responseCode, char *prompt, char *buffer, size_t bufferSize){
while( responseCode != OK ||
strIsInt( buffer ) == NaN )
{
printf("Invalid input.\nPlease enter integers only!\n");
fflush(stdout); /* It might be unnecessary to flush here because we'll flush STDOUT in the
getLine function anyway, but it is good practice to flush STDOUT when printing
important information. */
responseCode = getLine(prompt, buffer, bufferSize); // Read entire STDIN
}
// Finally, we know that the input is an integer
}
int main(int argc, char **argv){
char *prompt = "Please Insert X value\n";
int iResponseCode;
char cInputBuffer[100];
int x, y=0;
int *p = &x;
while(y < 5){
iResponseCode = getLine(prompt, cInputBuffer, sizeof(cInputBuffer)); // Read entire STDIN buffer
validate_input(iResponseCode, prompt, cInputBuffer, sizeof(cInputBuffer));
// Once validate_input finishes running, we should have a proper integer in our input buffer!
// Now we'll just convert it from a string to an integer, and store it in the P variable, as you
// were doing in your question.
sscanf(cInputBuffer, "%d", p);
y++;
}
}
Just as a disclaimer/note: I have not written in C for a very long time now, so I do apologize in advance if there are any error in this example. I also did not have an opportunity to compile and test this code before posting because I am in a rush right now.
If you're reading an input stream that you know is a text stream, but that you are not sure only consists of integers, then read strings.
Also, once you've read a string and want to see if it is an integer, use the standard library conversion routine strtol(). By doing this, you both get a confirmation that it was an integer and you get it converted for you into a long.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool convert_to_long(long *number, const char *string)
{
char *endptr;
*number = strtol(string, &endptr, 10);
/* endptr will point to the first position in the string that could
* not be converted. If this position holds the string terminator
* '\0' the conversion went well. An empty input string will also
* result in *endptr == '\0', so we have to check this too, and fail
* if this happens.
*/
if (string[0] != '\0' && *endptr == '\0')
return false; /* conversion succesful */
return true; /* problem in conversion */
}
int main(void)
{
char buffer[256];
const int max_tries = 5;
int tries = 0;
long number;
while (tries++ < max_tries) {
puts("Enter input:");
scanf("%s", buffer);
if (!convert_to_long(&number, buffer))
break; /* returns false on success */
printf("Invalid input. '%s' is not integer, %d tries left\n", buffer,
max_tries - tries);
}
if (tries > max_tries)
puts("No valid input found");
else
printf("Valid input: %ld\n", number);
return EXIT_SUCCESS;
}
ADDED NOTE: If you change the base (the last parameter to strtol()) from 10 to zero, you'll get the additional feature that your code converts hexadecimal numbers and octal numbers (strings starting with 0x and 00 respectively) into integers.
I took #4386427 idea and just added codes to cover what it missed (leading spaces and + sign), I tested it many times and it is working perfectly in all possible cases.
#include<stdio.h>
#include <ctype.h>
#include <stdlib.h>
int validate_line (char *line);
int main(){
char line[256];
int y=0;
long x;
while (y<5){
printf("Please Insert X Value\n");
if (fgets(line, sizeof(line), stdin)){//return 0 if not execute
if (validate_line(line)>0){ // check if the string contains only numbers
x =strtol(line, NULL, 10); // change the authentic string to long and assign it
printf("This is x %d" , x);
break;
}
else if (validate_line(line)==-1){printf("You Have Not Inserted Any Number!.... ");}
else {printf("Invalid Input, Insert Integers Only.... ");}
}
y++;
if (y==5){printf("NO MORE RETRIES\n\n");}
else{printf("%d Retries Left\n\n", (5-y));}
}
return 0;}
int validate_line (char *line){
int returned_value =-1;
/*first remove spaces from the entire string*/
char *p_new = line;
char *p_old = line;
while (*p_old != '\0'){// loop as long as has not reached the end of string
*p_new = *p_old; // assign the current value the *line is pointing at to p
if (*p_new != ' '){p_new++;} // check if it is not a space , if so , increment p
p_old++;// increment p_old in every loop
}
*p_new = '\0'; // add terminator
if (*line== '+' || *line== '-'){line++;} // check if the first char is (-) or (+) sign to point to next place
while (*line != '\n'){
if (!(isdigit(*line))) {return 0;} // Illegal char found , will return 0 and stop because isdigit() returns 0 if the it finds non-digit
else if (isdigit(*line)){line++; returned_value=2;}//check next place and increment returned_value for the final result and judgment next.
}
return returned_value; // it will return -1 if there is no input at all because while loop has not executed, will return >0 if successful, 0 if invalid input
}
I have a problem with sscanf function. To be more specific I want read a float from a txt file (I want only one float for line) but sscanf doesn't recognize these situations:
3.4t
or
4.t6
or
4.5 6.5
or
(this is an empty line)
I use this code:
#define LUNRIGA 200
char riga[LUNRIGA+1];
while (fgets(riga,LUNRIGA,f) != NULL) {
r = sscanf(riga,"%f",&numeri[i]);
if (r == 1) { /* riga valida */
printf("OK");
}else{
printf("Error");
return 1;
}
}
sscanf is notoriously picky about the input. You will have better luck using strtod or strtol - they can read a value even if it's followed by junk. Change your code as follows:
#define LUNRIGA 200
char riga[LUNRIGA+1];
char* tempPtr;
while (fgets(riga,LUNRIGA,f) != NULL) {
numeri[i] = strtof( riga, &tempPtr );
if (tempPtr > riga) { /* riga valida */
printf("OK");
}
else {
printf("Error");
return 1;
}
}
Note by the way that you don't seem to increment i in your loop - you might want to see if that's actually what you want, or if you would like to increment it every time you get a valid number (assuming you don't just want the last value, but all of them...)
As a little demo of the behavior of strtod, I wrote a few lines of code:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char* s1="123.45t";
char* p1;
char* s2 = " notanumber";
double d1, d2;
d1 = strtod(s1, &p1);
printf("the number is %.2lf; the pointer is now %p; string is at %p\n", d1, s1, p1);
d2 = strtod(s2, &p1);
printf("the number is %.2lf; the pointer is now %p; string is at %p\n", d2, s2, p1);
}
The output of this is:
The number is 123.45; the pointer is now 0x400668; string is at 0x40066e
The number is 0.00; the pointer is now 0x400670; string is at 0x400670
As you can see, when reading garbage the pointer returned points to the start of the string - indicating "failed". When it is successful, the pointer is pointing to "where I stopped reading", which is "after successfully converting a bit of string to double.
If you want to be sure there's no junk on the line, you can use the %n directive to find out where conversion stops and decide what to do about what comes afterwards on the line:
char riga[LUNRIGA];
float numeri[NUM_VALUES];
int offset;
int i;
for (i = 0; i < NUM_VALUES && fgets(riga, sizeof(riga), f) != NULL; i++)
{
int r = sscanf(riga, "%f%n", &numeri[i], &offset);
if (r == 1 && riga[offset] == '\n')
printf("OK (got %f from <<%s>>\n", numeri[i], riga);
else
{
printf("Error processing <<%s>>\n", riga);
return 1;
}
}
You can do some extra processing before the sscanf(), for example to check that you read a newline (so there isn't the residue of an extra long line still to be read) and to remove the newline (but then you need to change the condition).
Note the use of sizeof(riga), and the check for not overflowing numeri by imposing a for loop on the code, and the presence of a newline at the end of each message printed.
I have a function to read a text file with the following format
string int int
string int int
string int int
I want to write a function that will assign the values from the text file into variables, but there will also be some cases where the format of the text file will be
string int
string int
string int
In that case, I'd like to set the value of the last int variable to 1. My code I have so far works with the first example but I'm a bit stuck on getting the second scenario to work:
void readFile(LinkedList *inList, char* file)
{
char tempName[30];
int tempLoc, tempNum;
FILE* f;
f = fopen(file, "r");
if(f==NULL)
{
printf("Error: could not open file");
}
else
{
while (fscanf(f, "%s %d %d\n", tempName, &tempLoc, &tempNum) != EOF)
{
insertFirst (inList, tempName, tempLoc, tempNum);
}
}
}
In the second case, fscanf will return 2 instead of 3. So you can rewrite the code like this:
while (1) {
int ret = fscanf(f, "%s %d %d\n", tempName, &tempLoc, &tempNum);
if (ret == EOF) {
break;
}
if (ret == 2) {
tempNum = 1;
} else if (ret != 3) {
// line appear invalid, deal with the error
}
insertFirst (inList, tempName, tempLoc, tempNum);
}
A more hacky way would be to set tempNum to 1 before calling fscanf and just check for EOF as you did above. But I think the code above is clearer.
Edit: to avoid overflows, this would be better. The code would perform better but this is harder to write. Just like above, I did not write any code for the error conditions but you definitely want to handle them
char lineBuf[255];
while (fgets(lineBuf, sizeof(lineBuf), f) != NULL) {
int spaceIdx, ret;
const int len = strlen(lineBuf);
if (len == (sizeof(lineBuf) - 1) {
// line is too long - either your buf is too small and you should tell the user
// that its input is bad
// I recommend to treat this as an error
}
lineBuf[len - 1] = '\0'; // remove \n
--len; // update len, we've removed one character
if (isspace(*lineBuf)) {
// error, line should not start with a space
}
spaceIdx = strcspn(lineBuf, "\t ");
if (spaceIdx == len) {
// error, no space in this line
}
// Ok, we've found the space. Deal with the rest.
// Note that for this purpose, sscanf is a bit heavy handed (but makes the code
// simpler). You could do it with strtol.
// Also, the first space in the format string is important, so sscanf skips
// all the space at the beginning of the string. If your format requires only
// one space between fields, you can do sscanf(lineBuf + spaceIdx + 1, "%d %d"...
ret = sscanf(lineBuf + spaceIdx, " %d %d", &tempLoc, &tempNum);
if (0 == ret) {
// error, no ints
}
else if (1 == ret) {
tempNum = 1;
}
// at that point, you could copy the first part of lineBuf to tempName, but then
// you have to deal with a potential overflow (and spend time on an useless copy),
// so use lineBuf instead
lineBuf[spaceIdx] = '\0';
insertFirst (inList, lineBuf, tempLoc, tempNum);
}
I'm very new to C and I'm still learning the basics. I'm creating an application that reads in a text file and breaks down the words individually. My intention will be to count the amount of times each word occurs.
Anyway, the last do-while loop in the code below executes fine, and then crashes. This loop prints memory address to this word (pointer) and then prints the word. It accomplishes this fine, and then crashes on the last iteration. My intention is to push this memory address into a singly linked list, albeit once it's stopped crashing.
Also, just a quick mention regarding the array sizes below; I yet figured out how to set the correct size needed to hold the word character array etc because you must define the size before the array is filled, and I don't know how to do this. Hence why I've set them to 1024.
#include<stdio.h>
#include<string.h>
int main (int argc, char **argv) {
FILE * pFile;
int c;
int n = 0;
char *wp;
char wordArray[1024];
char delims[] = " "; // delims spaces in the word array.
char *result = NULL;
result = strtok(wordArray, delims);
char holder[1024];
pFile=fopen (argv[1],"r");
if (pFile == NULL) perror ("Error opening file");
else {
do {
c = fgetc (pFile);
wordArray[n] = c;
n++;
} while (c != EOF);
n = 0;
fclose (pFile);
do {
result = strtok(NULL, delims);
holder[n] = *result; // holder stores the value of 'result', which should be a word.
wp = &holder[n]; // wp points to the address of 'holder' which holds the 'result'.
n++;
printf("Pointer value = %d\n", wp); // Prints the address of holder.
printf("Result is \"%s\"\n", result); // Prints the 'result' which is a word from the array.
//sl_push_front(&wp); // Push address onto stack.
} while (result != NULL);
}
return 0;
}
Please ignore the bad program structure, as I mentioned, I'm new to this!
Thanks
As others have pointed out, your second loop attempts to dereference result before you check for it being NULL. Restructure your code as follows:
result = strtok( wordArray, delims ); // do this *after* you have read data into
// wordArray
while( result != NULL )
{
holder[n] = *result;
...
result = strtok( NULL, delims );
}
Although...
You're attempting to read the entire contents of the file into memory before breaking it up into words; that's not going to work for files bigger than the size of your buffer (currently 1K). If I may make a suggestion, change your code such that you're reading individual words as you go. Here's an example that breaks the input stream up into words delimited by whitespace (blanks, newlines, tabs, etc.) and punctuation (period, comma, etc.):
#include <stdio.h>
#include <ctype.h>
int main(int argc, char **argv)
{
char buffer[1024];
int c;
size_t n = 0;
FILE *input = stdin;
if( argc > 1 )
{
input = fopen( argv[1], "r");
if (!input)
input = stdin;
}
while(( c = fgetc(input)) != EOF )
{
if (isspace(c) || ispunct(c))
{
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
n = 0;
}
}
else
{
buffer[n++] = c;
}
}
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
}
fclose(input);
return 0;
}
No warranties express or implied (having pounded this out before 7:00 a.m.). But it should give you a flavor of how to parse a file as you go. If nothing else, it avoids using strtok, which is not the greatest of tools for parsing input. You should be able to adapt this general structure to your code. For best results, you should abstract that out into its own function:
int getNextWord(FILE *stream, char *buf, size_t bufsize)
{
int c;
size_t n = 0;
while(( c = fgetc(input)) != EOF && n < bufsize)
{
if (isspace(c) || ispunct(c))
{
if (n > 0)
{
buf[n] = 0;
n = 0;
}
}
else
{
buffer[n++] = c;
}
}
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
}
if (n == 0)
return 0;
else
return 1;
}
and you would call it like
void foo(void)
{
char word[SOME_SIZE];
...
while (getNextWord(inFile, word, sizeof word))
{
do_something_with(word);
}
...
}
If you expect in your do...while code, that result could be null (this is the condition for loop break), how do you think this code-line:
holder[n] = *result;
must work? It seems to me, that it is the reason for crashing in your program.
Change do while loop to while
use
while (condition)
{
}
instead of
do {
}while(condition)
It is crashing because you are trying to derefrance a NULL pointer result in do while loop.
I work mostly with Objective-C and was just looking at your question for fun, but I may have a solution.
Before setting n=0; after your first do-while loop, create another variable called totalWords and set it equal to n, totalWords can be declared anywhere within the file (except within one of the do-while loops), but can be defined at the top to the else block since its lifetime is short:
totalWords = n;
then you can set n back to zero:
n = 0;
Your conditional for the final do-while loop should then say:
...
} while (n <= ++totalWords);
The logic behind the application will thus say, count the words in the file (there are n words, which is the totalWords in the file). When program prints the results to the console, it will run the second do-while loop, which will run until n is one result past the value of totalWords (this ensures that you print the final word).
Alternately, it is better practice and clearer for other programmers to use a loop and a half:
do {
result = strtok(NULL, delims);
holder[n] = *result;
wp = &holder[n];
printf("Pointer value = %d\n", wp);
printf("Result is \"%s\"\n", result);
//sl_push_front(&wp); // Push address onto stack.
if (n == totalWords) break; // This forces the program to exit the do-while after we have printed the last word
n++; // We only need to increment if we have not reached the last word
// if our logic is bad, we will enter an infinite loop, which will tell us while testing that our logic is bad.
} while (true);