String compare isn't working correctly in C - c

I can't figure out why my string compare isn't comparing correctly. This is for C.
It's reading from a file that is set up like this:
1 - ls
2 - cd
3 - history
If I type !c it's suppose to grab the last used string that started with 'c' and run the command. Yet it never goes into the if(strcmp(())=0) line.
Part of my code is here:
char currLine[MAXINPUTLINE];
else if (isalpha(input[1])){
int count = 1;
fileRead = fopen(".simpleshell_history", "r");
while(fscanf(fileRead, "%s\n", currLine) != EOF){
printf(input+1);
printf(currLine);
if(strcmp((input+1), currLine) == 0){
printf("%s\n", currLine);
parse(currLine);
}
}
}
This is what the printf in the while loop prints, I can't figure out how to fix this and I've been stuck on it for a while. This is when I enter '!c'
c
1c
-c
lsc
2c
-c
cdc
3c
-c
historyc
4c
-c
!c!c

This loop:
while(fscanf(fileRead, "%s\n", currLine) != EOF) {
will read whitespace delimeted tokens into currLine rather than lines. So the first iteration will be 1, the second -, the 3rd ls, etc as you are seeing with your printfs. The name currLine suggests you want to read lines rather than tokens.
You then compare the read tokens with the rest of your input line which is apparently "c\n". Since you never get a token with a newline, it never matches. Even if you got rid of the newline, it would never match, as your file does not contain the token c
edit
You say you want to compare the rest of the input line against a prefix of the command on
the line. To do that you want to first figure out how long the prefix is, then use strncmp. You also want to parse the line from the file to separate the command from the index. So you could do something like:
else if (isalpha(input[1])){
int count = 1;
int pfxlen = 1;
while (!isspace(input[pfxlen+1])) pfxlen++;
fileRead = fopen(".simpleshell_history", "r");
while(fscanf(fileRead, "%d - %[^\n]", &index, currLine) == 2) {
if(strncmp((input+1), currLine, pfxlen) == 0) {
printf("%s\n", currLine);
parse(currLine);
}
}
}

If input is the string !c and you are looking to match that against the line 2 - cd, you will have to be careful. strcmp will definitely not work, as it will only return success if the two strings it is comparing are exact matches.
In order to test whether one string (cd) starts with another string (c), you want to use strncmp(), which will take a limit on the number of characters to compare.
Also: you will need to be careful to start comparing from the second character of input (skipping the !) and from the fifth character of currLine (skipping the 2 - characters).
This should get you there:
while (fgets(currLine, sizeof currLine, fileRead) != NULL) {
printf(input+1);
printf(currLine);
if (strncmp(input + 1, currLine + 4, strlen(input)-1) == 0) {
printf("%s\n", currLine);
parse(currLine);
}
}

One possibility is that input may contain a trailing newline, while curline definitely will not because of the scanf specification.

Your problem is with the manner in which you get input. (notice when you print, it has a line feed). It has a trailing \n and your currLine does not. Hence the compare fails.
Suggest OP uses fgets() for both user and file input.
Something like
char buf[MAXINPUTLINE];
while(fgets(buf, sizeof buf, fileRead) != NULL) {
int LineNo;
if (sscanf(buf, "%d - %s", &LineNo, currLine) != 2) Handle_UnexpectedInput();
...
}
Note: "%s\n" does the same as "%s " does the same as "%s\t": %s skips optional leading whitespace, then scans non-whitespace. The whitespace after s in "%s\n" scans for optional whitespace.

Related

How to split text from file into words?

I'm trying to get the text from a file and split it into words by removing spaces and other symbols. This is part of my code for handling the file:
void addtext(char wordarray[M][N])
{
FILE *fileinput;
char word[N];
char filename[N];
char *pch;
int i=0;
printf("Input the name of the text file(ex \"test.txt\"):\n");
scanf("%19s", filename);
if ((fileinput = fopen(filename, "rt"))==NULL)
{
printf("cannot open file\n");
exit(EXIT_FAILURE);
}
fflush(stdin);
while (fgets(word, N, fileinput)!= NULL)
{
pch = strtok (word," ,'.`:-?");
while (pch != NULL)
{
strcpy(wordarray[i++], pch);
pch = strtok (NULL, " ,'.`:-?");
}
}
fclose(fileinput);
wordarray[i][0]='\0';
return ;
}
But here is the issue. When the text input from the file is:
Alice was beginning to get very tired of sitting by her sister on the bank.
Then the output when I try to print it is this:
Alice
was
beginning
to
get
very
tired
of
sitting
by
her
s
ister
on
the
bank
As you can see, the word "sister" is split into 2. This happens quite a few times when adding a bigger text file. What am I missing?
If you count the characters you'll see that s is the 57th character. 57 is 19 times 3 which is the number of parsed characters in each cycle, (20 -1, as fgets null terminates the string and leaves the 20th character in the buffer).
As you are reading lines in batches of 19 characters, the line will be cuted every multiple of 19 charater and the rest will be read by the next fgets in the cycle.
The first two times you where lucky enough that the line was cutted at a space, character 19 at the end of beggining, character 38 at the end of tired, the third time it was in the midle of sister so it cuted it in two words.
Two possible fixes:
Replace:
while (fgets(word, N, fileinput)!= NULL)
With:
while (fscanf(fileinput, %19s, word) == 1)
Provided that there are no words larger than 19 in the file, which is the case.
Make word large enough to take whole the line:
char word[80];
80 should be enough for the sample line.
What am I missing?
You are missing that a single fgets call at maximum will read N-1 characters from the file, Consequently the buffer word may contain only the first part of a word. For instance it seems that in your case the s from the word sister was read by one fgets call and that the remaining part, i.e. ister was read by the next fgets call. Consequently, your code detected sister as two words.
So you need to add code that can check whether the end of the is a whole word or a part of a word.
To start with you can increase N to a higher number but to make it work in general you must add code that checks the end of the word buffer.
Also notice that long words may require more than 2 fgets call.
As a simple alternative to fgets and strtok consider fread and a simple char-by-char passing of the input.
Below is a simple, low-performance example of how it can be done.
int isdelim(char c)
{
if (c == '\n') return 1;
if (c == ' ') return 1;
if (c == '.') return 1;
return 0;
}
void addtext(void)
{
FILE *fileinput;
char *filename = "test.txt";
if ((fileinput = fopen(filename, "rt"))==NULL)
{
printf("cannot open file\n");
return;
}
char c;
int state = LOOK_FOR_WORD;
while (fread(&c, 1, 1, fileinput) == 1)
{
if (state == LOOK_FOR_WORD)
{
if (isdelim(c))
{
// Nothing to do.. keep looking for next word
}
else
{
// A new word starts
putchar(c);
state = READING_WORD;
}
}
else
{
if (isdelim(c))
{
// Current word ended
putchar('\n');
state = LOOK_FOR_WORD;
}
else
{
// Current word continues
putchar(c);
}
}
}
fclose(fileinput);
return ;
}
To keep the code simple it prints the words using putchar instead of saving them in an array but that is quite easy to change.
Further, the code only reads one char at the time from the file. Again it's quit easy to change the code and read bigger chunks from the file.
Likewise you can add more delimiters to isdelim as you like (and improve the implementation)

how to extaract data from file starting from 2nd line in c

Im very new to this language, can you help me:
Instead of making the user input col, row, and direction(scanf). I want to extract the data from file(format below)
From the file format i do not want to extract the first line(5,6), i only want to extract the remaining lines.
Below is a code of how to extract data from a file(using command line arguments), but this code extract the first line also, and only prints the lines.I do not want to print the line but to extract the data from a file instead of making the user input it.
File format:
colrow direction(starting from 2nd line)
5,6
A0 H
D0 V
C1 V
A4 H
F0 v
code of scanf
yourcolumn = getchar();
col = charToNum(yourcolumn); //function to input column
printf("enter row");
scanf("%d",&row);
printf("h: horizontally or v: vertically?\n");
scanf(" %c",&direction);
Code for extracting data from file:
#include <stdio.h>
int main(int argc, char* argv[])
{
char const* const fileName = argv[1]; /* should check that argc > 1 */
FILE* file = fopen(fileName, "r"); /* should check the result */
char line[256];
while (fgets(line, sizeof(line), file)) {
/* note that fgets don't strip the terminating \n, checking its
presence would allow to handle lines longer that sizeof(line) */
printf("%s", line);
}
/* may check feof here to make a difference between eof and io failure -- network
timeout for instance */
fclose(file);
return 0;
}
Since you are reading line-by-line, I suggest you restructure you file reading to match your logic
while (EOF != fscanf(file, "%[^\n]\n", line)) {
printf("> %s\n", line);
}
Is a way that one can read every line, one at a time. You can lookup the caveats of using fscanf and how to adjust the code to safely read without overflowing your line buffer.
Then, if you want to skip the first line, your code could look like this
if (EOF != fscanf(file, "%[^\n]\n", line)) {
// skip the first line
}
while (EOF != fscanf(file, "%[^\n]\n", line)) {
printf("> %s\n", line);
}
And your processing logic will look a lot like your mental process.
Yes, you could use a line counter, and only process if the counter is high enough; but, it is generally better to avoid introducing variables, if you can live without them. This is because an extra added variable doesn't make the code too hard to reason about; but, after you've repeated that "extra variable" rationale five or six times, the code quickly turns into something that's harder to maintain and harder to reason about. By the time you hit twenty or more extra variables, the odds of maintaining the code quickly without breaking it are lower.
Read the first line also with fgets() into a string and then scan the string for row, direction.
char line[256];
if (fgets(line, sizeof(line), file)) {
if (sscanf(line, "%d %c", &row, &direction) != 2) {
printf("Invalid first line '%s'\n", line);
} else {
while (fgets(line, sizeof(line), file)) {
printf("%s", line);
}
}
}

How to scanf until I get something not of the same form

I am inputting a text file from Stdin, which will have an undetermined number of coordinates, then a #, then another set of coordinates. How do I scanf the coordinates into a loop that stops once I hit the #, that will let me scanf the rest of the coordinates after the #?
I have tried a couple of loops like:
`while(scanf("[%d,%d]\n", &x, &y) == 1){
//do stuff//
}
but I dont feel like im getting any closer to an answer, any help would be greatly appreciated, cheers
input example (the # doesnt have "" around it but it disappears on here if it doenst):
[0,0]
[1,1]
[2,2]
"#"
[1,3]
[3,6]
[9,8]
You probably want to wrap the code into fgets(or readline):.
char buff[2048] ;
while ( fgets(buff, sizeof(buff), stdin )) {
// Check of 'last' marker
if ( buff[0] == '#' ) break ;
// Check if looks like coordinates
if ( sscanf(buff, "[%d,%d]\n", &x, &y) == 2 ) {
// do something
} ;
} ;
Minor fix to sscanf return code (2 instead of 1) - check for parsing of 2 fields
Your input data delimited by a double-quoted "#" complicates matters slightly. Your attempt to read with scanf is destined to fail. Your scanf format string of "[%d,%d]\n" does not read/discard the '\n' at the end of the line. In fact it doesn't match a '\n' at all. scanf does not interpret control characters, so what the '\n' in your format string is looking for is a literal 'n' leading to an input-failure after the two conversions take place.
You have two options:
remove the '\n' from your format string, continue with scanf (not recommended) and then manually read/discard all remaining characters in the line until a '\n' is reached (using either getchar() or fgetc()); or
read each line into a buffer using a line-oriented input function like fgets() or POSIX getline() to ensure a complete line of data is read each time and then parse the information you need from the buffer with sscanf (preferred method).
Taking the preferred approach above, you could do something similar to:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (void) {
char buf[MAXC]; /* buffer for each line */
int x, y, n = 0; /* coordinates & counter */
printf ("set[%d]:", n++); /* initial set[]: label */
while (fgets (buf, MAXC, stdin)) { /* read each line */
if (strncmp (buf, "\"#\"", 3) == 0) /* line starts with "#"? */
printf ("\nset[%d]:", n++); /* output new set[]: label */
else if (sscanf (buf, " [%d,%d]", &x, &y) == 2) /* 2 conversions? */
printf (" %d,%d", x, y); /* output coordinates */
}
putchar ('\n'); /* tidy up with newline */
return 0;
}
(note: if your file simply contained # instead of "#", you could simply check the first character in the buffer instead of using strncmp)
Example Input File
$ cat dat/coordgroups.txt
[0,0]
[1,1]
[2,2]
"#"
[1,3]
[3,6]
[9,8]
Example Use/Output
$ ./bin/readcoords < dat/coordgroups.txt
set[0]: 0,0 1,1 2,2
set[1]: 1,3 3,6 9,8
Look thing over and let me know if you have further questions.

skip to next line of file ignoring content

hi so i have a program where if there is an # at the begining of the first line of the text file it needs to be ignored, how do you jump to the next line of file? ignoring all that there is after the #?
for example:
#1234
5
I want to print 5 and the rest to be ignored.
I only managed to skip the # if there is nothing behind it
while (a == '#' || a == '\r'|| a == '\n') {
fscanf(inp, "%c", &a);
}
As for your previous question, if your want to ignore comment lines with an initial #, it is highly recommended to read the file line by line with fgets() and to handle non comment lines directly while ignoring comment lines.
It is actually non trivial to do it with fscanf because depending on your format lines, the linefeed may or may not have been consumed.
If you are at the start of a line and want to read the next char while ignoring the comment lines, do this:
int c; // Must be int to accommodate for EOF.
while ((c = getc(inp)) == '#') {
while ((c = getc(inp)) != EOF && c != '\n')
continue;
}
// Here c contains the first char from a non comment line or EOF.
Instead of
while (a == '#' || a == '\r'|| a == '\n') {
fscanf(inp,"%c",&a);
}
Try (pseudo code):
If FirstChar == '#'
Loop/scan until '\n'
On nextline here
If you want to use fscanf().
If better performance is needed, work on buffers directly.

fopen() always returns NULL

int main()
{
int i;
FILE *list,*file;
char temp[30];
list=fopen("filelist","rb");
while(fgets(temp,30,list)!=NULL)
{
file=fopen(temp,"r");
{
fclose(list);
return 0;
}
This is my code I basically want to open all files in filelist but my fopen call (exept the first one always returns a NULL am i missing something also this is my filelist
file1
file2
file3
file4
also i dont use file extensions and files exist in the same directory wtih executable.
fgets() stores the new-line character into the buffer it is populating so you need to remove it before calling fopen() within the while.
From the linked reference page for fgets():
Reads at most count - 1 characters from the given file stream and stores them in str. The produced character string is always NULL-terminated. Parsing stops if end-of-file occurs or a newline character is found, in which case str will contain that newline character.
Example code to remove the new-line:
char* nl = strrchr(temp, '\n');
if (nl) *nl = 0;
fgets leaves the newline on the end of the string, which you can plainly see if you add the following line afterwards:
printf ("[%s]\n", temp);
You'll see something like:
[file1
]
You need to remove it before use, which you can do this with something like:
size_t sz = strlen (temp);
if (sz > 0)
if (temp[sz-1] == '\n')
temp[sz-1] = '\0';
You can see this effect in action in the following complete program:
#include <stdio.h>
#include <string.h>
int main (void) {
size_t sz;
char temp[30];
printf ("\n> ");
while (fgets (temp, sizeof(temp), stdin) != NULL) {
printf ("before: [%s]\n", temp);
sz = strlen (temp);
if (sz > 0) {
if (temp[sz-1] == '\n') {
temp[sz-1] = '\0';
}
}
printf ("after : [%s]\n", temp);
printf ("\n> ");
}
return 0;
}
It basically uses your exact method to get a line using fgets (but from standard input) and then outputs the result both before and after removal of the trailing newline. A sample run follows:
pax> ./testprog
> hello
before: [hello
]
after : [hello]
> goodbye
before: [goodbye
]
after : [goodbye]
> [CTRL-D]
pax> _
You may also want to look at a few other things in that code segment:
the use of an open brace { at the end of the while loop.
the fact that you're opening the files within the loop and not doing anything with them (including closing them).
the use of "rb" open mode. Usually this is unnecessary, it's certainly unnecessary if you know it's a text file.
you should always check the return codes of functions that can fail (like fopen) before using them.
the canonical form of main in C where no arguments are needed is int main (void).
I'll state my case of which I am still uncertain: I thought my problem was with "fopen", but after trying every single solution, I ran into the extension problem, which I'm facing in Windows 10. It appears that Windows puts ".txt" automatically but, if you put ".txt" as extension, the name becomes ".txt.txt" at the end. So I left the file name with no extension, and put "file.txt" as argument of "fopen", and that was the only way it has worked for me.

Resources