"Vigenere" : String lowercasing program is appending character for some inputs - c

I'm finding that my very basic code to lowercase a string is sometimes outputting an extra character. If I run vigenere with some inputs, it works correctly:
~/workspace/pset2/vigenere/ $ ./vigenere tweedDLed
tweeddled
But for other inputs, it inserts an extra character at the end:
~/workspace/pset2/vigenere/ $ ./vigenere tweedDLedf
tweeddledfB
or...
~/workspace/pset2/vigenere/ $ ./vigenere bkls33bf
bkls33bfW
What is going on here? I am not finding anything with the debugger as the character array is not displayed. This is my code:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <cs50.h>
int main(int argc, string argv[]){
if (argc!=2){
return 1;
}
else{
int n = strlen(argv[1]);
char cipherKey[n];
for (int i=0;i<n;i++){
cipherKey[i]=tolower(argv[1][i]);
}
printf("%s\n",cipherKey);
}
}

You need to allocate space for the string termination character, and you need to terminate your string. Otherwise, you leave your string unterminated, printf may read out of the string's bounds yielding undefined behaviour (e.g. in form of "weired" output). You could correct this as follows:
int n = strlen(argv[1]);
char cipherKey[n+1];
for (int i=0;i<n;i++){
cipherKey[i]=tolower((unsigned char)argv[1][i]);
}
cipherKey[n]='\0';

Related

How does one correctly format command line arguments?

I am trying to construct a simple programme to acquaint myself with command line functionality, I seem to have formatted something incorrectly, but I find it very difficult to understand precisely what the resulting error message means. My intenion is to create a programme which checks that all the characters in the command line are digits. Here is my code:
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
for (int i = 0; i < strlen(argv); i++)
{
if (!isalnum(argv[i]))
{
printf("Please provide letters or numbers only");
}
else
{
printf("Success!");
}
}
}
The error message I receive when I attempt to compile is: c:9:32: error: incompatible pointer types passing 'char **' to parameter of type 'const char *'; dereference with * [-Werror,-Wincompatible-pointer-types].
argv is an array (pointer) of pointers to strings.
isalnum is checking a character, not a whole string, so you will need two loops: to check each strings and to check each characters in the strings.
This code won't get the warning:
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
for (int c = 1; c < argc; c++) // loop for checking each strings
{
for (int i = 0; i < strlen(argv[c]); i++) // loop for checking each characters in the strings
{
if (!isalnum(argv[c][i]))
{
printf("Please provide letters or numbers only");
}
else
{
printf("Success!");
}
}
}
}
But you may want this code with implovements:
Print the message only once instead of for each characters.
Use size_t for looping until the length of the string.
Call strlen() once before the loop instead of calling in each iteration.
Use puts instead of printf to print newline character at the end of output.
Add return 0; to clarify that the code returns 0.
Remove unnecessary #includes, including non-standard one.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
int all_ok = 1;
for (int c = 1; c < argc; c++)
{
size_t len = strlen(argv[c]);
for (size_t i = 0; i < len; i++)
{
all_ok = all_ok && isalnum(argv[c][i]);
}
}
if (!all_ok)
{
puts("Please provide letters or numbers only");
}
else
{
puts("Success!");
}
return 0;
}

How I can cause a program in C to print only one statement once it encounters a specific condition?

I have been trying to write a C program where the user inputs a command-line argument and the program checks if it is all number digits or not. If that is true, the program simply reprints what the user put in. If that is false and there is at least one non-number mixed in, the program just prints the word "bad" once.
What I have come up so far consists of the program checking each character of the input individually. However, what ends up happening once I put in a mixture of numbers and letters in is that the program would still print the numbers, and then once it gets to printing a letter, it prints the word "bad".
Here's an example:
I put ./program 100x into the terminal
=> Expected result: bad
=> Actual result: 100bad
Here's the code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, string argv[])
{
for(int i = 0; i < strlen(argv[1]); i++) {
if (isdigit(argv[1][i]) != 0) {
printf("%c", argv[1][i]);
return 0;
} else {
printf("bad");
return 1;
}
}
}
First, I don't think your program is doing what your describe because it returns at the first digit encountered. Then, as soon as you see a digit you print it, you want to first check that your input is right and then print it. Taking your implementation, you could modify it like that:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, string argv[])
{
// checking input
for(int i = 0; i < strlen(argv[1]); i++) {
if (!(isdigit(argv[1][i]) != 0)) {
printf("bad\n");
return 1;
}
}
// here you know your input is good
// prints input
printf("%s\n", argv[1]);
return 0;
}
Try this one :
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
//for(int i = 0; i < strlen(argv[1]); i++)
{
//if (isdigit(argv[1][i]) != 0)
if(strspn(argv[1], "0123456789" )== strlen(argv[1]))
{
printf("%s", argv[1]);
return 0;
}
else
{
printf("bad");
return 1;
}
}
}
Your logical error is that your program checks one character and immediately prints a digit or a bad message, and so continues with each character. However, you need to try writing an algorithm that first checks the whole string for characters other than numbers. If the algorithm finds at least one character (the result is false), it can close loop by using "break" keyword and print "bad" after. OR if all characters are numbers, you can return the value true and only then print them on the screen.

How to fix Segmentation fault in a command line argument

I am trying to create a cipher coder in C, so far I have the following code. The if statement doesn't work properly and pops up segmentation fault whenever something is inputted whether it be a digit or just a letter. The else statement which is supposed to print out how to use the cipher text only prints it out when there is a space.
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int main(int argc, string argv[]) //im certain the problem is on this line but I dont know where exactly nor do I know how to fix it
{
if (isdigit(argv[1]))
{
string s = get_string("Cipher Text: ");
}
else
{
printf("Usage: ./caesar key\n");
}
}
I'm certain the error has to deal with the string in the command line argument but I'm not to sure on what steps to take to fix it.
This is a very small program to show how you can access arguments of a C program.
It only uses standard C features (not CS50 or special string type).
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
printf("argc=%d\n", argc);
for (i=0; i < argc; i++)
printf("argv[%d]=%s\n", i, argv[i]);
return 0;
}

String bug in my_shell program

I'm trying to create a simple shell program which execute the program specified in input. There are two main function: scanner() (use strtok to split the input in token) and execute() (fork the process and execute the program).
Unfortunately it doesn't work... I've tried to print string[0] at the end of scanner() and at the beginning of execute(). The first time the output is correct but the second time string[] seems to be modified in a sequence of random numbers so execvp() doesn't work...
I really can't figure out why the values of string[] changes, probably is a very stupid error but I can't see it. I really need your help! Thanks in advice.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define DIM 256
int scanner(char*[]);
int execute(char*[]);
int main()
{
char* string[DIM];
scanner(string);
execute(string);
}
/* scan: read the input in token*/
int scanner(char* string[])
{
char input[1024];
char delimit[]=" \t\r\n\v\f";
int i = 0;
if(fgets(input, sizeof input, stdin)) {
string[i] = strtok(input, delimit);
while(string[i]!=NULL){
i++;
string[i]=strtok(NULL,delimit);
}
return 0;
}
return 1;
}
/* execute: execute the command*/
int execute(char* string[])
{
int pid;
printf("%s\n", string[0]);
switch(pid = fork()){
case -1:
return 1;
case 0:
execvp(string[0], string);
return 1;
default:
wait((int*)0);
return 0;
}
}
The string variable input in scanner is a local variable, with storage class "auto". That means that when that function returns, that variable disappears, and the memory it occupied can be re-used for other things. That is unfortunate, since strtok returns pointers into that string variable.

In C: I want to take a mix of characters and convert them to lower case

Why does my code not accept the argv[] string? What do I need to do to fix it? I want to be able to type in both lower and upper case letters and end up with only lowercase letters in the array. Thanks for any help.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char argv[])
{
char word[30]= atoi(argv[1]); // here is the input
for (int i = 0; word[i]; i++)
word[i] = tolower(word[i]);
printf("Here is the new word: %s\n", word);
return 0;
}
int main(int argc, char argv[])
should be:
int main(int argc, char *argv[])
Besides, strtol is a better option than atoi as strtol can handle failures better.
You have several problems with your code:
As KingsIndian already mentioned, you are missing a * in front of the argv paramter of the main function. This 'Main Function' wiki page contains some more details on this.
atoi is used to convert a string number to an integer number. This is not what you want I suppose. argv[x] is already a string (char *), so you can use it directly.
If you use it directly, you cannot modify it contents (not allowed, I believe). Therefore you need to make a copy. Use strlen() to find out the length of argv[1], malloc() to create an buffer and strcpy() to copy it:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *word = malloc(strlen(argv[1]) + 1);
strcpy(word, argv[1]);
int i;
for (i = 0; word[i]; i++)
word[i] = tolower(word[i]);
printf("Here is the new word: %s\n", word);
return 0;
}
Additional notes:
It would be better (more robust) if you check the amount of given command line parameters using argc!
In theory malloc() can return 0, indicating that claiming the memory did fail. So you should check for this.
If you only want to print the lower case world, you do not require to first convert it and then print it. Instead, you could directly print each converted character.

Resources