Why is my if/else statement not ever going to else? - c

I'm trying to write a program that takes in an argument(file name) from the command line. If it's not one of the four files that I expect then I want to print out that the correct file isn't found then exit safely. Right now when I run my code no matter what the argument is it will always run the printf("\n\n %s \n\n", argv[1]); line. Why is this happening?
int main(int argc, char **argv)
{
if(argv[1] == "orlando.csv" || "orlando5.csv" || "florida.csv" ||
"twolines.csv"){
printf("\n\n %s \n\n", argv[1]);
} else {
printf("etl ERROR: File %s not found", argv[1]);
return -1;
}
}

if(argv[1] == "orlando.csv" || "orlando5.csv" || "florida.csv" || "twolines.csv")
This doesn't do what you think it does.
First it tests whether the address of the string argv[1] is equal to the address of the string "orlando.csv". Since one is a command-line parameter and the other is a string literal, this test will always be false.
(Two strings with the same address must also have the same contents, but two strings with different addresses might still have the same contents.)
Then it tests whether the address of the string "orlando5.csv" is not "null". String literals never have "null" addresses, so this test will always be true.
Once one entry in a chain of || tests has been found to be true, all further tests are skipped (this is called "short-circuit evaluation").
So the if-expression is always found to be true, and the if-branch is always executed, and the else-branch is never executed.
The if-expression you should have written is
if (!strcmp(argv[1], "orlando.csv") ||
!strcmp(argv[1], "orlando5.csv") ||
!strcmp(argv[1], "florida.csv") ||
!strcmp(argv[1], "twolines.csv"))
Yes, you have to repeat the "!strcmp(argv[1]," part every single time. Yes, you have to call a library function to compare strings in C. (You will need to add #include <string.h> to the top of your source file.)

Change:
if(argv[1] == "orlando.csv" || "orlando5.csv" || "florida.csv" || "twolines.csv")
To:
if( (strcmp(argv[1],"orlando.csv")==0) ||
(strcmp(argv[1],"orlando5.csv")==0) ||
(strcmp(argv[1],"florida.csv")==0) ||
(strcmp(argv[1],"twolines.csv")==0) )

Related

Change input stream mid-program

I have the following program which prints text from a file:
#include<stdio.h>
int main(void) {
int ch;
while ((ch=getchar()) != EOF) {
putchar(ch);
}
printf("Enter a character now...\n");
// possible to prompt input now from a user?
ch = getchar();
printf("The character you entered was: %c\n", (char) ch);
}
And running it:
$ ./io2 < file.txt
This is a text file
Do you like it?
Enter a character now...
The character you entered was: �
After this, how would I get a user to enter in a character. For example, if I were to do getchar() (when not redirecting the file to stdin)?
Now it seems it just will keep printing the EOF character if I keep doing getchar() at the end.
My previous suggestion won't work because the cat command will just join both the file and stdin as one and supply that to your program and you will eventually reach the same conclusion.
If your program needs the file, it should just read from it directly, then get the rest of its input from standard input...
#include<stdio.h>
#include<stdlib.h>
int main(void) {
int ch;
FILE* file = fopen("file.txt", "r");
if (file == NULL) {
perror("fopen");
return EXIT_FAILURE;
}
while ((ch=fgetc(file)) != EOF) {
putchar(ch);
}
fclose(file);
... // now just read from stdin
return EXIT_SUCCESS;
}
You can call clearerr(stdin) to clear the end-of-file (and error) conditions:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int c;
/* Consume standard input */
while ((c = getchar()) != EOF) {
putchar(c);
}
/* Clear the error condition */
clearerr(stdin);
printf("Please provide more input.\n");
fflush(stdout);
/* Consume more standard input */
while ((c = getchar()) != EOF) {
putchar(c);
}
return EXIT_SUCCESS;
}
However, this is the wrong approach. If you run echo Hello | ./io2, the program will not wait for additional input, because standard input is provided by echo, and is no longer connected to the terminal.
The proper approach is to use command-line parameters to specify the file name, and read it using a separate FILE handle:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
FILE *in;
int c;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
fprintf(stderr, " %s FILENAME\n", arg0);
fprintf(stderr, "\n");
fprintf(stderr, "This program reads and outputs FILENAME, then\n");
fprintf(stderr, "prompts and reads a line from standard input.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
/* Open specified file. */
in = fopen(argv[1], "r");
if (!in) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
/* Read and output file, character by character ("slow") */
while ((c = getc(in)) != EOF) {
putchar(c);
}
/* Check if the EOF indicated an error. */
if (ferror(in)) {
fprintf(stderr, "%s: Read error.\n", argv[1]);
return EXIT_FAILURE;
}
/* Close the input file. Be nice, and check for errors. */
if (fclose(in)) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
/* Prompt for new input. */
printf("Please input something.\n");
fflush(stdout);
while (1) {
c = getchar();
if (c == EOF || c == '\n' || c == '\r')
break;
putchar(c);
}
printf("All done.\n");
return EXIT_SUCCESS;
}
There are several things worthy of note here:
argv[0] is the command itself (./io2 in your example). The first command-line argument is argv[1]. Because argc is the number of entries in argv, argc == 1 means there are no arguments; argc == 2 means there is one argument, and so on.
If argc == 2, then argv[0] and argv[1] are valid. In POSIXy systems like Linux and BSDs and Mac, and standard C libraries conforming to C11, argv[argc] == NULL, and is safe to access.
The if line relies on C logic rules. In particular, if you have expr1 || expr2, and expr1 is true, expr2 is never evaluated.
This means that if argc != 2, the strcmp() checks are not evaluated at all.
The !strcmp(argv[1], "-h") is true if and only if argv[1] matches -h.
Thus, the if line reads, "if argc says we don't have exactly two elements in argv array, or we have and argv[1] matches -h, or we have and argv[2] matches --help, then".
It is often possible to execute a program without any arguments in POSIXy systems, for example via execl("./io2", NULL, NULL) or by some other nonstandard trick. This means that it is technically possible for argc to be zero. In that case, we don't know how this program was executed.
The value of arg0 is a ternary expression which essentially reads, "if argc says we we should have at least one element in argv array, and argv array exists, and the first element in argv array exists, and the first character in that first element is not end of string mark, then arg0 is the first element in argv array; otherwise, arg0 is (this)."
This is only needed because I like printing the usage when run with -h or --help as the first parameter. (Almost all command-line programs in POSIXy systems do this.) The usage shows how to run this program, and for this, I want to use the same command that was used to run this program; hence arg0.
When getc()/getchar()/fgetc() returns EOF, it means that there is no more input in the stream. This can happen because of end of stream, or because a read error occurred.
I like to carefully check for errors. Some consider it "useless", but to me, error checking is important. As an user, I want to know – no, I need to know if my storage media is producing errors. Therefore, the ferror(in) check is important to me. It is true (nonzero) only if there was a read/write error (I/O error) when accessing the stream in.
Similarly, it is possible for fclose(in) to report a delayed error. I do not believe it is possible to happen for a read-only stream, but it definitely is possible for streams we write to, because the C standard library buffers stream data, and the final underlying write operation can occur when we are closing the stream handle. Even the man 3 fclose man page explicitly says this is possible.
Some programmers say that it is not useful to check for fclose() errors, because they are so rare. To me, as an user, it is. I want the programs I use to report the errors they detect, instead of assuming "eh, it's so rare I won't bother checking or reporting those"*.
By default, standard output (stdout) is line-buffered, so technically the fflush(stdout) is not needed. (The fact that the previous printf() ends with a newline, \n, means that that printf() should cause the standard output to be flushed.)
Flushing a stream means ensuring the C library actually writes its internal buffer to the output file or device. Here, we definitely want to be sure the user sees the prompt, before we start waiting for input. Thus, while the fflush(stdout) is technically not needed, here it also provides us human programmers a reminder that at this point, we do need the stdout stream to be flushed to the actual output device (terminal).
It is often useful to redirect program output to a file, or via a pipe as input to another program. Because of this, I like to use the standard error (stderr) for error messages and the usage information.
If the user runs the program incorrectly, or an error occurs, with the output redirected to a file or piped to another program, they will typically still see the standard error output. (It is possible to redirect standard error too, though.)

True and false in C in if statement with getopt()

I have a hard time in understatanding how true and false works with "if statement" when I am using argv & getopt.
This is the simple code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int opt;
while ((opt = getopt (argc, argv, "i:l:")) != -1)
switch (opt) {
case 'i':
printf("This is option i");
break;
case 'l':
printf("This is option l");
break;
default:
fprintf(stderr,"Usage: %s here goes usage\n",argv[0]);
}
if (argc == 1) {
printf("Without options");
}
if ((argc == 2) && (argv[1] != "-l") || (argv[1] != "-i")) {
printf("Without option -l or -i but with other argument \n");
printf("argv[1] is %s\n", argv[1]);
}
Usage:
./a.out foo
Output:
Without option -l or -i but with other argument
argv[1] is foo
It's good so far. Now let me check if it works when argv[1] is "-l":
Usage:
./a.out -l
Output:
./a.out: option requires an argument -- 'l'
Usage: ./a.out here goes usage
Without option -l or -i but with other argument
argv[1] is -l
Getopt works fine, but second information occurs even if argv[1] is -l and I set in "if" statement that (argv[1] != "-l"). Why it works like that? I have no clue.
Thanks for any answer.
B.
Do not use:
argv[1] != "-l"
to compare strings in C(a), this compares the addresses of two arrays of char(b) rather than the contents of those two strings. Instaed, you should be using:
strcmp(argv[1], "-l") == 0
(a) This is one of the things fixed in C++ strings, the equality operators there compare the content of the strings.
(b) This may sometimes seem to work with something like "xyzzy" == "xyzzy" but that's only because the compiler is allowed (but not required) to fold identical string constants together (so they take less room). It generally is not possible when one of the strings is not a constant (such as when it's passed on the command line).
You re comparing two char *s using != this compares the addresses.
(argv[1] != "-l")
The way to compare strings is using strcmp. Mind hat the strcmp function is "trinary", and returns either a negative or positive value if the strings are different (to provide order) or 0 if equal.
(strcmp(argv[1], "-l") == 0)
AND (&&) has precedence over OR (||), so I think your code inside the if statement is interpreted like this: if (((argc == 2) && (argv[1] != "-l")) || (argv[1] != "-i")) So the logic inside of it is always true, because even if your option IS -l and it's one and only (number of option != 2) it's also true that the option you typed is NOT -i.

Why is my while loop activated, but nothing else happens?

So right now I'm trying to mess around with stdin and an input file by doing the following:
./a.exe < input.txt
The input.txt file just has a lot of random test cases so I can see if I'm doing this right or not. I'm trying to remove some punctuation marks from my input file through the use of stdin. You will notice that I have a couple of printf's laying in there saying "here". I put that there to see if the program reaches it or not.
So whenever I run it, I type "./a.exe < input.txt", and I get "bash: input.txt: No such file or directory". So I'm like... okay, I'll just type in a couple of random inputs to see if it works. I typed in random words, and it turns out the program doesn't even hit any of the words I typed after ./a.exe.
Can anyone explain why I'm having these errors?
Here's my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
char textin[80], wordout[80];
int i, j;
int count = 0;
printf("%d", argc);
while (count != argc)
{
scanf("%s", textin);
if (strcmp(textin, ".") == 0)
printf("here");
printf("here2");
strcpy(wordout, textin);
for (i = 0 ; i < strlen(wordout) ; i++)
{
if (wordout[i] == '.' || wordout[i] == ',' ||
wordout[i] == '"' || wordout[i] == ';' ||
wordout[i] == '!' || wordout[i] == '?' ||
wordout[i] == '(' || wordout[i] == ')' ||
wordout[i] == ':')
{
for (j = i ; j < strlen(wordout) - 1 ; j++)
wordout[j] = wordout[j + 1];
wordout[j] = '\0';
}
printf("here3");
}
printf("%s\n", wordout);
count++;
}
return 0;
}
That first issue has nothing to do with your program, the error you're getting is a bash error, trying to open input.txt to connect to the standard input of your a.exe program. This happens before your code even begins to run.
You need to figure out why bash can't find your file, such as:
are you in the correct directory?
does the input.txt file actually exist?
have you created it in such a way that there are special characters in the file name (such as inpux<backspace>t.txt)?
As to the other issue, you appear to be using a mish-mash of two different methods of getting information from the user.
These two methods are arguments passed to the program (argc/v) and standard input (scanf()).
It may be that you're running your code with something like:
./a.exe arg1 arg2 arg3
and expecting it to do something with them. Unfortunately, the scanf() inside your loop will simply wait for you to type something on the terminal so it may seem that it has hung.
I think you need to figure out how you want to present the input to the program. If it's via arguments, you would use argv[N] where 1 <= N < argc. If it's via standard input, you probably don't need to concern yourself with argc/v at all.

Compare string literal command line parameters in C

I need my program to run this way ./src c 2345 or ./src s 345, whereby the first character hs to be either c or s and second an integer. The program should throw an usage error if there's any less parameters and also any charcter other than c or s. Here's my code
int main(int argc, char **argv) {
int num_of_connections = 0, client_sockfd;
int max_sockfd, master_socket;
fd_set socket_collection, read_collection;
// Check validity of the user input
if(argc < 3) {
if((strcmp(argv[2], "s") != 0) || (strcmp(argv[2], "c") != 0)){
fprintf(stderr, "Usage: ./src <s/c> <port>\n");
exit(EXIT_FAILURE);
}
}
When I enter one argument I get a segmentation fault. Also it doesnt identify the C or S parameter. Any help will be appreciated.
Notice that main has a very specific convention: the argv array has argc+1 members, with the last being NULL and the others being non-null distinct pointers to zero-terminated strings.
So if argc is 1 (e.g. if your run ./src alone) or 2, argv[2] is NULL and you cannot pass it to  strcmp
You can call strcmp(argv[2],"s") only when argc>=3
BTW, I would suggest to use getopt(3) or preferably (on Linux only) getopt_long and to accept the --help and --version arguments, per GNU conventions.
Also, compile with all warnings and debug info (gcc -Wall -g) and use the gdb debugger. It would be faster for you to use gdb than to ask here and wait for replies.
if(argc < 3) { does not make sense if you want exactly two parameters. In the inner if block you are confusion || (logical or) with && (logical and).
In your invocation example ./src s 345 the character is the first argument, so probably argv[2] should read argv[1].
if ((argc != 3) || ((strcmp(argv[1], "s") != 0) &&
(strcmp(argv[1], "c") != 0))) {
fprintf(…);
return EXIT_FAILURE;
}
Note: all parentheses in this if (…) condition are optional, because of C's operator precedence. I put them for readability.

Printing out an error message in terminal

I am working on a program that determines the mode of a set of values for example (3 4 2 3 3) should print out "3". The catch is the program must receive the option of the mathematical function to execute and its arguments as parameters in the main function so no user input. Everything must be inserted in the command line and check by using pointers. My program works except for example say the user enters (mode) but doesn't enter in any values after. This should then print a message that just says "ERROR" and the program ends. However it does not instead it prints
Johns-MacBook-Pro-2:AdvanceCalc jvdamore$ ./a.out mode
Segmentation fault: 11
when it should print
Johns-MacBook-Pro-2:AdvanceCalc jvdamore$ ./a.out mode ERROR
below is my code. So my question is does my if statement with strcmp(p[2], "") == 0 work in order to produce the desired error message? or am I doing something wrong?
int main(int n, char **p)
{
int i, x, A[100];
if (strcmp(p[1], "mode")==0){
if (strcmp(p[2], "") == 0){
printf("ERROR");
return -1;
}
for(i=2;i<n;i++){
if (sscanf(p[i], "%d", &x) != 1) {
printf("ERROR");
return -1;
}
if (x<1 || x>30){
printf("ERROR");
return-2;
}
A[i-2]= x;
}
find_mode(A, n-2);
}
Rather than comparing a string to "" with strcmp, you need to see if it is NULL. strcmp( NULL, "" ) does not work very well, and you should instead do:
if( p[2] == NULL )
(well, really, you should rename the varaible argv, and there are several other issues, but this is the main problem. Make sure you have checked that p[1] is not NULL before you reference p[2])

Resources