Extract character from a string bug - c

I read in a temp variable from a file, this is one word, e.g. "and", however, when I extract the first character, e.g. temp[1], the program crashes when running, I have tried break points, and it is on this line.
This is what happens when I run the code: http://prntscr.com/2vzkmp
These are the words when I don't try to extract a letter: http://prntscr.com/2vzktn
This is the error when I use breakpoints: http://prntscr.com/2vzlr3
This is the line that is messing up: " printf("\n%s \n",temp[0]);"
Here is the code:
int main(void)
{
char **dictmat;
char temp[100];
int i = 0, comp, file, found = 0, j = 0, foundmiss = 0;
FILE* input;
dictmat = ReadDict();
/*opens the text file*/
input = fopen("y:\\textfile.txt", "r");
/*checks if we can open the file, otherwise output error message*/
if (input == NULL)
{
printf("Could not open textfile.txt for reading \n");
}
else
{
/*allocates the memory location to the rows using a for loop*/
do
{
/*temp_line is now the contents of the line in the file*/
file = fscanf(input, "%s", temp);
if (file != EOF)
{
lowercase_remove_punct(temp, temp);
for (i = 0; i < 1000; i++)
{
comp = strcmp(temp, dictmat[i]);
if (comp == 0)
{
/*it has found the word in the dictionary*/
found = 1;
}
}
/*it has not found a word in the dictionay, so the word must be misspelt*/
if (found == 0 && (strcmp(temp, "") !=0))
{
/*temp is the variable that is misspelt*/
printf("\n%s \n",temp[0]);
/*checks for a difference of one letter*/
//one_let(temp);
}
found = 0;
foundmiss = 0;
}
} while (file != EOF);
/*closes the file*/
fclose(input);
}
free_matrix(dictmat);
return 0;
}

When printing a character, use %c, not %s.
There is a fundamental difference between the two. The latter is for strings.
When printf encounters a %c it inserts one byte in ASCII format into the output stream from the variable specified.
When it sees a %s it will interpret the variable as a character pointer, and start copying bytes in ASCII format from the address specified in the variable, until it encounters a byte that contains zero.

print char - not string:
printf("\n%c \n",temp[0]);

temp[0] is a charater. Thus if you are using
printf("\n%s \n",temp[0]);
it will print the string from address i.e. temp[0]. May be this location is not accessible, So it is crashing.
This change it to
printf("\n%c \n",temp[0]);

Why are you using %s as modifier, use %c

Related

Scanf multiple lines until reach specific character

I have input like the following:
Someting sth
example
5 15
3
I want to scanf input by lines to get whole content of the line. But when reaching first digit (there can be spaces/tabs before it) I want to scanf it as int.
That's what I have come up with but it does not work as expected - cursor still does not stop at digit character.
char person_name[1000];
int n;
while (scanf("%[^\n/D]%*c", person_name) > 0) {
if (checkIfContainsNumber(person_name) == 0) {
appendToLinkedList(&head_ref, person_name);
} else {
break;
}
}
while (scanf("%d", &n) > 0) {
printf("%d ", n);
}
As far as I understand the problem, each line could be considered either as
a sequence of names or a sequence of integers.
So I would try to read the file line by line and analyse each extracted line
as one sequence or another (spaces are implicitly consumed).
The trick here is the usage of "%n" to go further in the analyse of the same line.
#include <stdio.h>
int
main(void)
{
FILE *input=fopen("input.txt", "r");
if(!input)
{
return 1;
}
char line[1024];
while(fgets(line, sizeof(line), input))
{
int pos=0;
int value, count;
char name[256];
if(sscanf(line+pos, "%d%n", &value, &count)==1)
{
pos+=count;
printf("a line with values: <%d>", value);
while(sscanf(line+pos, "%d%n", &value, &count)==1)
{
pos+=count;
printf(" <%d>", value);
}
printf("\n");
}
else if(sscanf(line+pos, "%255s%n", name, &count)==1)
{
pos+=count;
printf("a line with names: <%s>", name);
while(sscanf(line+pos, "%255s%n", name, &count)==1)
{
pos+=count;
printf(" <%s>", name);
}
printf("\n");
}
}
fclose(input);
return 0;
}
Read the input line-wise with fgets and keep a mode: TEXT for text, NUMBER for numbers and ERROR for an error condition. (The error condition is undescribed. It could occur when you encounter non-numeric data in NUMBER mode, for example.)
Start out with TEXT. Before processing a line in text mode, check whether it could be a digit by a simple sscanf into the line. If you can read a number, switch to number mode, where you scan all numbers from a line.
char line[80];
enum {TEXT, NUMBER, ERROR = -1} mode = TEXT;
while (mode != ERROR && fgets(line, sizeof(line), stdin)) {
if (mode == TEXT) {
int n;
if (sscanf(line, "%d", &n) > 0) mode = NUMBER;
}
if (mode == TEXT) {
line[strcspn(line, "\n")] = '\0';
process_string(line);
} else if (mode == NUMBER) {
char *p = line;
char *end;
int n = strtol(p, &end, 0);
if (end == p) mode = ERROR;
while (end > p) {
process_number(n);
p = end;
n = strtol(p, &end, 0);
}
}
}
(But this approach will fail if the numbers are all in one very long. fgets truncates the input so that the specified size will nor be exceeded.)
Consider changing the scan strategy - ignore all characters that are non-digit, and then read the integer from that digits forward
if ( scanf("%*[^0-9]%d", &n) == 1 ) { ... }
The first field '%*[...]' will skip over anything that is non-digit. Note that it's possible to reach EOF before finding a digit - if statement is needed to check.

C file handling query

So I have a program that takes user input and compares it to a specific line in a file, however the final line will always be credited as incorrect, so can someone solve this for me?, thanks.
File content (just a list of random words)
Baby
Milk
Car
Face
Library
Disc
Lollipop
Suck
Food
Pig
(libraries are stdio,conio and string)
char text[100], blank[100];
int c = 0, d = 0;
void space(void);
int main()
{
int loop = 0;
char str[512];
char string[512];
int line = 1;
int dis = 1;
int score = 0;
char text[64];
FILE *fd;
fd = fopen("Student Usernames.txt", "r"); // Should be test
if (fd == NULL)
{
printf("Failed to open file\n");
exit(1);
}
do
{
printf("Enter the string: ");
gets(text);
while (text[c] != '\0')
{
if (!(text[c] == ' ' && text[c] == ' '))
{
string[d] = text[c];
d++;
}
c++;
}
string[d] = '\0';
printf("Text after removing blanks\n%s\n", string);
getch();
for(loop = 0;loop<line;++loop)
{
fgets(str, sizeof(str), fd);
}
printf("\nLine %d: %s\n", dis, str);
dis=dis+1;
str[strlen(str)-1] = '\0';
if(strcmp(string,str) == 0 )
{
printf("Match\n");
score=score+2;
}
else
{
printf("Nope\n");
score=score+1;
}
getch();
c=0;
d=0;
}
while(!feof(fd));
printf("Score: %d",score);
getch();
}
For any input on the last line, the output will always be incorrect, I believe this is something to do with the for loop not turning it into the next variable, but seeing as the <= notation makes this program worse, I really just need a simple fix for the program thanks.
Some observations:
You must never use gets (it is not even in the C11 standard anymore). Instead of gets(text) use fgets(text, sizeof(text), stdin) – this way a long input will not overflow the text array.
There will be stuff printed at the end because you don't check the return value of either the gets or the fgets, so when end of file occurs for either the file or for user input the rest of that iteration still runs. fgets returns NULL if it didn't read anything – check for that instead of using feof.
You remove newlines from the file input but not from the user input, so the comparison will always fail when you switch from gets to fgets (which doesn't strip linefeeds). The second (otherwise pointless) comparison of text[c] against ' ' should be against '\n'.
edit: Also, in case the last line of your file does not end in a linefeed, the comparison will fail on the last line because you don't check if the last character is a linefeed before you remove it.
The for (loop = 0; loop < line; ++loop) -loop is pointless because line is always 1, so the body is only executed once.
You have unnecessarily global variables which the program hard to follow. And, for instance, your local text[64] overshadows the global text[100], so if you think you are modifying the global buffer, you are not. If your code is complete, none of the variables should be global.
The function getch() is non-standard. There is no easy direct replacement, so you may just accept that you are not writing portable code, but it's something to be aware of.

I am trying to delete a word from a file in C

I wrote a program to remove a certain word (taken as user input) from a file. I read the word, opened the file from which I wanted to delete the word, read each line in the file and split it into words, and compared each word with my keyword. If they are not a match, I printed them into a temporary file.
But when I open the temp file, the entire text has been copied. Can somebody help me????
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
main()
{
int i;
FILE *fp1,*fp2;
char key[1000],a[1000],temp[1000];
printf("\nEnter the word to be deleted from file : ");
scanf("%s",key);
fp1 = fopen("a.txt","r");
fp2 = fopen("b.txt","w");
while(fgets(a,1000,fp1))
{
for(i=0;a[i]!='\0';++i)
if(a[i]==' ')
break;
else
temp[i]=a[i];
temp[i]='\0';
if(strcmp(temp,key)!=0)
{
fputs(temp,fp2);
}
bzero(a,1000);
}
fclose(fp1);
fclose(fp2);
printf("\n\n");
/*remove("a.txt");
rename("b.txt","a.txt");*/
}
Yet another solution. It has some of the same problems mentioned above, for example the boundaries of a word do not really match English grammar. It also has an inefficiency of doing more copies than is strictly necessary in some cases.
However, I prefer this version over yours because it abstracts the word removal task rather than doing it inline with the rest of the code. Plus it works better.
#include <stdio.h>
#include <string.h>
void remove_str2(char* buffer, char* find, int lfind) {
char* paste = strstr(buffer, find);
if (!paste) { return; }
char* start = paste + lfind;
while (1) {
char* end = strstr(start, find);
if (!end) {
end = start + strlen(start) + 1;
}
memmove(paste, start, end - start);
paste += end - start;
start = strstr(end, find);
if (!start) { return; }
start += lfind;
}
}
void remove_str(char* buffer, char* find, int lfind) {
char* p = strstr(buffer, find);
if (p) {
remove_str(p + lfind, find, lfind);
// shift everything left
do { *p = *(p + lfind); } while (*(++p));
}
}
int main() {
FILE *fp1, *fp2;
char key[1000], a[1000];
// require key to be preceeded by a space
key[0] = ' ';
printf("\nEnter the word to be deleted from file : ");
scanf("%s", &key[1]);
fp1 = fopen("a.txt","r");
fp2 = fopen("b.txt","w");
while (fgets(a, 1000, fp1)) {
remove_str(a, key, strlen(key));
fputs(a, fp2);
}
fclose(fp1);
fclose(fp2);
printf("\n\n");
return 0;
}
Tried to compile it and it works almost fine except it doesn't insert spaces in output file ;)
if(strcmp(temp,key)!=0)
{
temp[i++] = ' ';
temp[i] = '\0';
fputs(temp,fp2);
}
Also, its' very sensitive code. It doesn't work if I have a line of words. And it shouldn't work if your test file has a spaces before word and your keyword doesn't.
Some observations,
main()
{
//you will need input index, output index
int in, on; //in: input index, on: output index
FILE *fp1,*fp2;
char key[1000],iray[1000],oray[1000];
char* here; //clarifies the comparison
printf("\nEnter the word to be deleted from file : ");
scanf("%s",key);
printf("key: %s",key); fflush(stdout);
if(!(fp1 = fopen("a.txt","r"))) { printf("open error\n"); exit(1); }
if(!(fp2 = fopen("b.txt","w"))) { printf("open error\n"); exit(2); }
while(fgets(iray,sizeof(iray),fp1))
{
printf("line: %s",iray); fflush(stdout);
for(in=on=0; iray[in]; ++in) //'\0' = 0 = false //please indent
{ //please use braces
//printf("%d: %c\n",in,iray[in]); fflush(stdout);
if(iray[in]==' ')
{
oray[on++] = iray[in];
continue; //break gets you out of loop
}
here = &(iray[in]);
//printf("here: %s\n",here); fflush(stdout);
if( !strcmp(here,key) ) //have word?
{
in+=strlen(key); //skip word
continue;
}
oray[on++] = iray[in];
}
oray[on] = '\0';
printf("line: %s",oray); fflush(stdout);
fputs(oray,fp2);
iray[0]=0; //bzero(iray,1000);
oray[0]=0; //bzero(oray,1000);
}
fclose(fp1);
}
Part of the problem is that as soon as you find a space you process the word, but you don't finish processing the remainder of the line. After the fputs, you go to the top of the loop and read another line. When I compile and run your snippet, it indeed ouputs the first word of each line as long as it's not the selected word.
Also when outputting your words, you don't output any space between them and there is no carriage return at the end of lines. Thus all non-matching words are output back-to-back on one line.
There's a few more issues that the following snippet fixes (there's some remaining problems that I didn't solve-- for example, the last non-matching word on a line is not output if there is no trailing space on the line. You can solve this by looking for '\0' as well as space. I didn't do it because it requires changing the loop structure to not exit when '\0' is seen so you get a chance to test for it):
while(fgets(a,1000,fp1))
{
int j = 0;
for(i=0;a[i]!='\0';++i)
if((a[i]==' '){
temp[j++]='\0';
if(strcmp(temp,key)!=0) {
fprintf (fp2, "%s ", temp);
}
j = 0;
}
else
temp[j++]=a[i];
fprintf (fp2, "\n");
}
EDIT: a complete solution:
this is how your loop should look like:
while(fgets(a,1000,fp1))
{
int j=0;
for(i=0;a[i]!='\0';++i)
{
if(a[i]==' ')
{
temp[j]=0;
if(strcmp(temp,key)!=0)
{
fputs(temp,fp2);
}
j=0;
//this should be after fputs if you want to remove the space after obsolete word.
fputs(" ",fp2);
}
else
temp[j++]=a[i];
}
//last word if there is no space after!
if(strcmp(temp,key)!=0)
{
fputs(temp,fp2);
}
fputs("\n",fp2);
a[0] = 0;
}

C skipping line starts with '#'

im trying read a file and using fscanf to get some values down and store it in an array, however in the file, there will be some line starts with '#" (e.g. #this is just a command), and i want to skip them how should i do that? those lines that contains # will appear at random lines. got some of my code here:
//do line counts of how many lines contain parameters
while(!EOF) {
fgets(lines, 90, hi->agentFile);
count++;
if (lines[0] == '#') {
count--;
}
}
//mallocing an array of struct.
agentInfo* array = malloc(count*sizeof(agentInfo));
for (i = 0; i < count; i++) {
fscanf(hi->agentFile,"%d %d %c %s %c",&array[i].r,&array[i].c,
&array[i].agent_name,&array[i].function[80],
&array[i].func_par);
so i need to add something so i can skip lines start with '#', how?
Your EOF test is wrong. You also need to rewind the file between the fgets() loop and the fscanf() loop. And you need to replace the fscanf() loop with a second fgets() loop using sscanf() to read the data. Or you need to allocate the memory as you go while reading the file once. Let's leave that for later, though:
while(fgets(lines, sizeof(lines), hi->agentFile) != EOF)
{
if (lines[0] != '#')
count++;
}
agentInfo *array = malloc(count*sizeof(agentInfo));
if (array != 0)
{
int i;
rewind(hi->agentFile);
for (i = 0; fgets(lines, sizeof(lines), hi->agentFile) != EOF && i < count; i++)
{
if (lines[0] != '#')
{
if (sscanf(lines, "%d %d %c %s %c",&array[i].r,&array[i].c,
&array[i].agent_name,&array[i].function[80],
&array[i].func_par) != 5)
...format error in non-comment line...
}
}
assert(i == count); // else someone changed the file, or ...
}
Note that this checks for a memory allocation error and for format errors for non-comment lines.

Using fscanf to scan a value or use default if no value exists

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);
}

Resources