I have ran into a problem while learning to program in C. I am creating a program to translate a text into "rövarspråket" (you don't need to understand what it is to understand my problem). The program works great if I only insert one word, but when I add a space it just stop producing the output. Here'sn example:
If I insert "hello world", I want the output to be "hohelollolo wowororloldod", but instead it outputs "hohelollolo♀". Here's the code:
int main(){
char inputText[100], outputText[300];
int inputLength, ipTextNum, opTextNum;
scanf("%s", &inputText);
inputLength = strlen(inputText);
for(ipTextNum = 0; ipTextNum < inputLength; ipTextNum++){
if(inputText[ipTextNum] == 'a' || inputText[ipTextNum] == 'e' || inputText[ipTextNum] == 'i' || inputText[ipTextNum] == 'o' || inputText[ipTextNum] == 'u' || inputText[ipTextNum] == 'y' || inputText[ipTextNum] == 'å' || inputText[ipTextNum] == 'ä' || inputText[ipTextNum] == 'ö'){
outputText[opTextNum] = inputText[ipTextNum];
opTextNum++;
}
else {
outputText[opTextNum] = inputText[ipTextNum];
outputText[opTextNum+1] = 'o';
outputText[opTextNum+2] = inputText[ipTextNum];
opTextNum += 3;
}
}
printf("%s", outputText);
return 0;
}
If I also add inputText[ipTextNum] == ' ' to the if statement, it outputs the same thing as before, but without the ♀. Does anyone have a clue what the problem might be? I've tried to print the input, but it seems like all the text after the whitespace is terminated there too. Is whitespace even allowed in strings and if not, how do I work around this?
issue is here:
scanf("%s", &inputText);
there are actually two issues.. You want it to read an entire line I assume, so we don't use %s, we use "all characters except \n", then read the \n. The other issue is the &inputText.
scanf("%s", inputText);
is how it should be.. but this adds the entire line.
scanf("%[^\n]\n", inputText);
Go ahead and do your parsing on that, as long as you dont overflow!
scanf really shouldn't be used for strings with spaces because it does not accept them. You should really use fgets instead. See this answer for more information:
https://stackoverflow.com/a/1248017/2990189
Here is an example (from the above answer) to show how fgets works:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Maximum name size + 1. */
#define MAX_NAME_SZ 256
int main(int argC, char *argV[]) {
/* Allocate memory and check if okay. */
char *name = malloc (MAX_NAME_SZ);
if (name == NULL) {
printf ("No memory\n");
return 1;
}
/* Ask user for name. */
printf("What is your name? ");
/* Get the name, with size limit. */
fgets (name, MAX_NAME_SZ, stdin);
/* Remove trailing newline, if there. */
if ((strlen(name)>0) && (name[strlen (name) - 1] == '\n'))
name[strlen (name) - 1] = '\0';
/* Say hello. */
printf("Hello %s. Nice to meet you.\n", name);
/* Free memory and exit. */
free (name);
return 0;
}
As well as ensuring that the input does not overflow the buffer as other posters pointed out; another major issue is that you never null-terminate your output.
The printf("%s" knows when to stop outputting characters because it encounters a null byte. But you never wrote a null byte on the end of your output, which explains why there is a garbage character after it (and then you happened to have a null byte by chance in your buffer after the garbage).
After your loop, add in outputText[opTextNum] = 0; before the printf.
Of course, you also need to make sure your loop always stops with opTextNum < sizeof outputText being true. Since input has max string length 99 , then the max output that can be written is 3*99+1, so you're safe as things stand. If you decide to change your algorithm later you'll need to think about this again though.
Superficially, your problem is the use of scanf - it stops at the first space it encounters. Using fgets is usually the answer - and that might have been that. There was another problem in that you did not terminate your string (which led to the interesting last character - quite by chance, I must add), and you did not check for overflow in either the input or output buffers. Most of these points were made in other answers.
However - the problem is quite a bit more interesting, and harder, because you are using "non ascii" characters in your input. I am guessing your robbers have Scandinavian roots. This means that you not only need to worry about whitespace - you need to be using "wide characters".
I took the liberty of rewriting your example in "wide" form - meaning that it uses
fgetws - the wide equivalent of fgets
wprintf - the wide equivalent of printf
wcschr - the wide equivalent of strchr
As well as "wide" strings like L"wide string", "wide" characters like L'å', and "wide" format specifier %ls.
A stylistic problem with your code is that really long if statement that consisted of a long series of OR-ed comparisons with individual characters. You can do one of three things:
format more clearly with carriage returns and line continuations:
if( inputText[ipTextNum] == L'a' || \
inputText[ipTextNum] == L'e' || \
inputText[ipTextNum] == L'i' || \
inputText[ipTextNum] == L'o' || \
inputText[ipTextNum] == L'u' || \
inputText[ipTextNum] == L'y' || \
inputText[ipTextNum] == L'å' || \
inputText[ipTextNum] == L'ä' || \
inputText[ipTextNum] == L'ö')
Note: you did not test for space, so space is turned into o which is not what you want according to your description.
2. Replace the whole thing with wcschr which looks for a character in a string; by searching for the character in the string of vowels (this is what I did in the code below - including test for space); or
3. You could also create a function for yourself like
int isVowel(wchar_t c) {
return wcschr("aeiouyåäö ", c)!=0;
}
To make it even more compact/readable.
Anyway - here is the complete, annotated code:
#include <stdio.h> // <<<< dont forget your header files
#include <wchar.h> // <<<< to be able to use 'wide' characters: ö etc
// define the max size of input and output string:
#define IP_MAX 100
#define OP_MAX 300
int main(void) { // <<<<< use the correct function signature
wchar_t inputText[IP_MAX], outputText[OP_MAX];
int inputLength, ipTextNum, opTextNum=0; // <<< initialize opTextNum to zero!!
// scanf("%s", &inputText); // <<<< triple NO:
// 1) scanf stops at the first space
// 2) if the input is very long you overwrite your buffer
// 3) you are passing the POINTER to the pointer to char;
// you should use inputText not &inputText
fgetws(inputText, IP_MAX, stdin); // read at most IP_MAX characters into inputText including '\0'
inputLength = wcslen(inputText); // length of wide C string
inputText[--inputLength]='\0'; // strip carriage return
// printf("The length of the string entered is %d\n", inputLength);
wprintf(L"you entered \n'%ls'\n", inputText);
for(ipTextNum = 0; ipTextNum < inputLength && opTextNum < OP_MAX-3; ipTextNum++) {
if(wcschr(L"aeiouyåäö ", inputText[ipTextNum])) { // <<< include test for space too
outputText[opTextNum] = inputText[ipTextNum];
opTextNum++;
}
else {
outputText[opTextNum] = inputText[ipTextNum];
outputText[opTextNum+1] = 'o';
outputText[opTextNum+2] = inputText[ipTextNum];
opTextNum += 3;
}
}
outputText[opTextNum]=L'\0'; // nul terminate the string
wprintf(L"The robber said:\n'%ls'\n\n", outputText);
return 0;
}
Output:
hello world
you entered
'hello world'
The robber said:
'hohelollolo wowororloldod'
Related
Input : [1,3,2,4]
I want to make arr[4] = {1, 3, 2, 4} from this input using scanf(). How can I do this in C language?
It is possible to parse input such as you describe with scanf, but each scanf call will parse up to a maximum number of fields determined by the given format. Thus, to parse an arbitrary number of fields requires an arbitrary number of scanf calls.
In comments, you wrote that
I want to find a method to ignore '[', ']', ',' and only accept integer units.
Taking that as the focus of the question, and therefore ignoring the issues of how you allocate space for the integers to be read when you do not know in advance how many there will be, and assuming that you may not use input functions other than scanf, it seems like you are looking for something along these lines:
int value;
char delim[2] = { 0 };
// Scan and confirm the opening '['
value = 0;
if (scanf("[%n", &value) == EOF) {
// handle end of file or I/O error ...
} else if (value == 0) {
// handle input not starting with a '[' ...
// Note: value == zero because we set it so, and the %n directive went unprocessed
} else {
// if value != 0 then it's because a '[' was scanned and the %n was processed
assert(value == 1);
}
// scan the list items
do {
// One integer plus trailing delimiter, either ',' or ']'
switch(scanf("%d%1[],]", &value, delim)) {
case EOF:
// handle end of file or I/O error (before an integer is read) ...
break;
case 0:
// handle input not starting with an integer ...
// The input may be malformed, but this point will also be reached for an empty list
break;
case 1:
// handle malformed input starting with an integer (which has been scanned) ...
break;
case 2:
// handle valid (to this point) input. The scanned value needs to be stored somewhere ...
break;
default:
// cannot happen
assert(0);
}
// *delim contains the trailing delimiter that was scanned
} while (*delim == ',');
// assuming normal termination of the loop:
assert(*delim == ']');
Points to note:
it is essential to pay attention to the return value of scanf. Failure to do so and to respond appropriately will cause all manner of problems when unexpected input is presented.
the above will accept slightly more general input than you describe, with whitespace (including line terminators) permitted before each integer.
The directive %1[],] attempts to scan a 1-character string whose element is either ] or ,. This is a bit arcane. Also, because the input is scanned as a string, you must be sure to provide space for a string terminator to be written, too.
it would be easier to write a character-by-character parser for your specific format that does not rely on scanf. You could also use scanf to read one character at a time to feed such a parser, but that seems to violate the spirit of the exercise.
While I think that John Bollinger answer is pretty good and complete (even without considering the wonderful %1[[,]), I would go for a more compact and tolerant version like this:
#include <stdio.h>
size_t arr_input(int *arr, size_t max_size)
{
size_t n;
for (n = 0; n < max_size; ++n) {
char c;
int res = scanf("%c%d", &c, arr + n);
if (res != 2
|| (n == 0 && c != '[')
|| (n > 0 && c != ',')
|| (n > 0 && c == ']')) {
break;
}
}
return n;
}
int main(void)
{
char *test_strings[] = { "[1,2,3,4]", "[42]", "[1,1,2,3,5,8]", "[]",
"[10,20,30,40,50,60,70,80,90,100]", "[1,2,3]4" };
size_t test_strings_n = sizeof test_strings / sizeof *test_strings;
char filename[L_tmpnam];
tmpnam(filename);
for (size_t i = 0; i < test_strings_n; ++i) {
freopen(filename, "w+", stdin);
fputs(test_strings[i], stdin);
rewind(stdin);
int arr[9];
size_t num_elem = arr_input(arr, 9);
printf("%zu: %s -> ", i, test_strings[i]);
for (size_t j = 0; j < num_elem; ++j) {
printf("%d ", arr[j]);
}
printf("\n");
fclose(stdin);
}
remove(filename);
return 0;
}
The idea is that you allocate space for the maximum number of integers you accept, then ask the arr_input() function to fill it up to max_size elements.
The check after scanf() tries to cope with incorrect input, but is not very complete. If you trust your input to be correct (don't) you can even make it shorter, by dropping the three || cases.
The most complex thing was to write the test driver with tmp files, strings, reopening and such. Here I'd have loved to have std::istream to just drop a std::stringstream. The fact that the FILE interface doesn't support strings really bugs me.
int arr[4];
for(int i=0;i<4;i++) scanf("%d",&arr[i]);
Are you asking for this? I was little bit confused with your question, if this doesn't solve your query, then don't hesitate to ask again...
use scanf to read a string input from user then parse that input into an integer array
To parse you can use string function "find" to locate the "," and "[]" and then use "atoi" to convert string into integer to fill the destination input array.
Edit: find is a C++ function.
the C function is strchr
THIS IS A DIFFERENT QUESTION ENTIRELY. THE OTHER ONE IS URL SPECIFIC MINE IS NOT. THAT WAS JUST AN EXAMPLE.
So here's my code:
main()
{
char input[150]; //Create a variable of type char named input to store user input
printf(" Enter a standard line: "); //Ask the user for some standard input
if (fgets(input, 150, stdin) == NULL) //Initiate a series of checks to make sure there is valid input
{
puts(" End-of-File or Input Error Detected. "); //If the end of the file is reached or there is a problem with input output a message
}
else if (input[0] == '\n') //If the user neglected to enter anything output a message
{
puts(" Oops! Looks like you forgot to enter something! ");
}
else
{
printf(" Here's what you entered: %s ", input); //If there is valid user input echo it back to the user
int i = 0;
while ( input[i] != '\n' )
{
if (input[i] = '/')
putchar("%2F");
i++
}
}
}
I have to replace and adjust the input line accordingly by replacing certain characters with their ASCII code.
For example:
1. user inputs: google.COM/search?client
2. program changes and prints back to the user as: GOOGLE.com%2FSEARCH%3FCLIENT
But the system gives me this long error message when I try to compile my code.
/home/cs/carroll/cssc0154/One/p1.c: In function 'main':
/home/cs/carroll/cssc0154/One/p1.c:41:5: warning: passing argument 1 of 'putchar' makes integer from pointer without a cast [enabled by default]
putchar("%2F");
^
In file included from /home/cs/carroll/cssc0154/One/p1.c:15:0:
/usr/include/stdio.h:580:12: note: expected 'int' but argument is of type 'char *'
extern int putchar (int __c);
Where am I going wrong?
You're trying to pass 3 characters '%' '2' 'f' to a function putchar() that expects one character.
Consider using
printf("%%2f")
instead of
putchar("%2f"), you'll get the same output in stdout.
Note: you have to escape the percent character in printf with (%).
Since I don't have the points to comment on the above...
Background:
C doesn't really have 'strings' - it has pointers to characters which it handles very much like arrays.
standard library functions expect the last value in the array to be '\0', and they will keep processing characters until one of these 'null characters' is read.
This line:
putchar("%2F");
Passes a pointer (char *) to 'putchar()' instead of an integer.
The warning is simply pointing out the type mismatch, because it's unlikely you meant to do that. (it is correct, this is a serious mistake which can crash or cause vulnerabilities depending on the way the called function operates)
Most machines/compilers nowadays are LP64. That means 'putchar()' expects a 32bit integer, and you are providing a 64bit pointer.
The reason this code:
int i = 0;
while ( input[i] != '\n' )
{
if (input[i] == '/')
puts("%2F");
i++
}
prints out "%2F" (I assume given the input "google.COM/search?client") is because it finds '/' once, and prints the string you provided "%2F". No other character executes a print statement.
If you added:
int i = 0;
while ( input[i] != '\n' )
{
if (input[i] == '/')
puts("%2F");
else
putc(intput[i]);
i++
}
you would get
google.COM%2F
search?client
because puts() prints a newline ('\n') after printing its input.
To avoid that you'll need to choose a different output function, and while many options are available, printf() is likely the best and certainly the most recognizable.
printf() prints 'formatted' text which uses '%' to indicate formatting options, so printing a single '%' requires you to 'escape' it which looks like "%%"
Here is a working example of what you're trying to do.
I replaced, what must be a large block of
if (input[i] == 'CHAR') printf("%%XX");
else if (input[i] == ...
by putting all the special characters in a string.
I simply check whether that string contains each character I'm about to print.
If the character is found, I print a '%' followed by the character's hex value. The "%02X" tells printf() to write the hex value with at least 2 characters and to put 0 in front if it needs to pad the value. Basically it will never write '9' but instead write '09'.
You could also use the isalpha() ... isalphanum() ... ispunct() functions to identify which types of characters you should encode.
#include <stdio.h>
#include <string.h>
int main(int argc, char ** argv)
{
char *decodeStr = "/:\\+";
int decodeLen = strlen(decodeStr);
while(--argc >= 0 && ++argv && *argv){
char *str = *argv;
for(int i = 0; str[i] != '\0'; i++){
if (NULL != memchr(decodeStr, str[i], decodeLen))
printf("%%%02X", str[i]);
else
putchar(str[i]);
}
putchar('\n');
}
return 0;
}
example run:
./test http://google.com/atest+with+things
http%3A%2F%2Fgoogle.com%2Fatest%2Bwith%2Bthings
I want to check to make sure that a given string contained in an array called secretWord has no symbols in it (e.g. $ % & #). If it does have a symbol in it, I make the user re-enter the string. It takes advantage of recursion to keep asking until they enter a string that does not contain a symbol.
The only symbol I do accept is the NULL symbol (the symbol represented by the ASCII value of zero). This is because I fill all the empty space in the array with NULL symbols.
My function is as follows:
void checkForSymbols(char *array, int arraysize){ //Checks for symbols in the array and if there are any it recursively calls this function until it gets input without them.
for (int i = 0; i < arraysize; i++){
if (!isdigit(array[i]) && !isalpha(array[i]) && array[i] != (char) 0){
flushArray(array, arraysize);
printf("No symbols are allowed in the word. Please try again: ");
fgets(secretWord, sizeof(secretWord) - 1, stdin);
checkForSymbols(secretWord, sizeof(secretWord));
}//end if (!isdigit(array[i]) && !isalpha(array[i]) && array[i] != 0)
else
continue;
}//end for(i = 0; i < sizeof(string[]); i++){
}//end checkForSymbols
The problem: When I enter any input (see example below), the if statement runs (it prints No symbols are allowed in the word. Please try again: and asks for new input).
I assume the problem obviously stems from the statement if (!isdigit(array[i]) && !isalpha(array[i]) && array[i] != (char) 0). But I have tried changing the (char) 0 part to '\0' and 0 as well and neither change had any effect.
How do I compare if what is in the index is a symbol, then? Why are strings without symbols setting this if statement off?
And if any of you are wondering what the "flushArray" method I used was, here it is:
void flushArray(char *array, int arraysize){ //Fills in the entire passed array with NULL characters
for (int i = 0; i < arraysize; i++){
array[i] = 0;
}
}//end flushArray
This function is called on the third line of my main() method, right after a print statement on the first line that asks users to input a word, and an fgets() statement on the second line that gets the input that this checkForSymbols function is used on.
As per request, an example would be if I input "Hello" as the secretWord string. The program then runs the function on it, and the if statement is for some reason triggered, causing it to
Replace all values stored in the secretWord array with the ASCII value of 0. (AKA NULL)
Prints No symbols are allowed in the word. Please try again: to the console.
Waits for new input that it will store in the secretWord array.
Calls the checkForSymbols() method on these new values stored in secretWord.
And no matter what you input as new secretWord, the checkForSymbols() method's if statement fires and it repeats steps 1 - 4 all over again.
Thank you for being patient and understanding with your help!
You can do something like this to find symbols in your code, put the code at proper location
#include <stdio.h>
#include <string.h>
int main () {
char invalids[] = "#.<#>";
char * temp;
temp=strchr(invalids,'s');//is s an invalid character?
if (temp!=NULL) {
printf ("Invalid character");
} else {
printf("Valid character");
}
return 0;
}
This will check if s is valid entry or not similarly for you can create an array and do something like this if array is not null terminated.
#include <string.h>
char false[] = { '#', '#', '&', '$', '<' }; // note last element isn't '\0'
if (memchr(false, 'a', sizeof(false)){
// do stuff
}
memchr is used if your array is not null terminated.
As suggested by #David C. Rankin you can also use strpbrk like
#include <stdio.h>
#include <string.h>
int main () {
const char str1[] = ",*##_$&+.!";
const char str2[] = "##"; //input string
char *ret;
ret = strpbrk(str1, str2);
if(ret) {
printf("First matching character: %c\n", *ret);
} else {
printf("Continue");
}
return(0);
}
The only symbol I do accept is the NULL symbol (the symbol represented by the ASCII value of zero). This is because I fill all the empty space in the array with NULL symbols.
NULL is a pointer; if you want a character value 0, you should use 0 or '\0'. I assume you're using memset or strncpy to ensure the trailing bytes are zero? Nope... What a shame, your MCVE could be so much shorter (and complete). :(
void checkForSymbols(char *array, int arraysize){
/* ... */
if (!isdigit(array[i]) && !isalpha(array[i]) /* ... */
As per section 7.4p1 of the C standard, ...
In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.
Not all char values are representable as an unsigned char or equal to EOF, and so it's possible (and highly likely given the nature of this question) that the code above invokes undefined behaviour.
As you haven't completed your question (by providing an MCVE, and describing what errors are occuring) I'm assuming that the question you're trying to ask might be a duplicate of this question, this question, this question, this question and probably a whole lot of others... If so, did you try Googling the error message? That's probably the first thing you should've done. Should that fail in the future, ask a question about the error message!
As per request, an example would be if I input "Hello" as the secretWord string.
I assume secretWord is declared as char secretWord[] = "Hello"; in your example, and not char *secretWord = "Hello";. The two types are distinct, and your book should clarify that. If not, which book are you reading? I can probably recommend a better book, if you'd like.
Any attempt to modify a string literal (i.e. char *array = "Hello"; flushArray(array, ...)) is undefined behaviour, as explained by answers to this question (among many others, I'm sure).
It seems a solution to this problem might be available by using something like this...
In response to your comment, you are probably making it a bit tougher on yourself than it needs to be. You have two issues to deal with (one you are not seeing). The first being to check the input to validate only a-zA-Z0-9 are entered. (you know that). The second being you need to identify and remove the trailing '\n' read and included in your input by fgets. (that one may be tripping you up)
You don't show how the initial array is filled, but given your use of fgets on secretWord[1], I suspect you are also using fgets for array. Which is exactly what you should be using. However, you need to remove the '\n' included at the end of the buffer filled by fgets before you call checkforsymbols. Otherwise you have character 0xa (the '\n') at the end, which, of course, is not a-zA-Z0-9 and will cause your check to fail.
To remove the trailing '\n', all you need to do is check the last character in your buffer. If it is a '\n', then simply overwrite it with the nul-terminating character (either 0 or the equivalent character representation '\0' -- your choice). You simply need the length of the string (which you get with strlen from string.h) and then check if (string[len - 1] == '\n'). For example:
size_t len = strlen (str); /* get length of str */
if (str[len - 1] == '\n') /* check for trailing '\n' */
str[--len] = 0; /* overwrite with nul-byte */
A third issue, important, but not directly related to the comparison, is to always choose a type for your function that will return an indication of Success/Failure as needed. In your case the choice of void gives you nothing to check to determine whether there were any symbols found or not. You can choose any type you like int, char, char *, etc.. All will allow the return of a value to gauge success or failure. For testing strings, the normal choice is char *, returning a valid pointer on success or NULL on failure.
A fourth issue when taking input is you always need to handle the case where the user chooses to cancel input by generating a manual EOF with either ctrl+d on Linux or ctrl+z on windoze. The return of NULL by fgets gives you that ability. But with it (and every other input function), you have to check the return and make use of the return information in order to validate the user input. Simply check whether fgets returns NULL on your request for input, e.g.
if (!fgets (str, MAXS, stdin)) { /* read/validate input */
fprintf (stderr, "EOF received -> user canceled input.\n");
return 1; /* change as needed */
}
For your specific case where you only want a-zA-Z0-9, all you need to do is iterate down the string the user entered, checking each character to make sure it is a-zA-Z0-9 and return failure if anything else is encountered. This is made easy given that every string in C is nul-terminated. So you simply assign a pointer to the start of your string (e.g. char *p = str;) and then use either a for or while loop to check each character, e.g.
for (; *p != 0; p++) { do stuff }
that can be written in shorthand:
for (; *p; p++) { do stuff }
or use while:
while (*p) { do stuff; p++; }
Putting all of those pieces together, you could write your function to take a string as its only parameter and return NULL if a symbol is encountered, or return a pointer to your original string on success, e.g.
char *checkforsymbols (char *s)
{
if (!s || !*s) return NULL; /* validate string and not empty */
char *p = s; /* pointer to iterate over string */
for (; *p; p++) /* for each char in s */
if ((*p < 'a' || *p > 'z') && /* char is not a-z */
(*p < 'A' || *p > 'Z') && /* char is not A-Z */
(*p < '0' || *p > '9')) { /* char is not 0-9 */
fprintf (stderr, "error: '%c' not allowed in input.\n", *p);
return NULL; /* indicate failure */
}
return s; /* indicate success */
}
A short complete test routine could be:
#include <stdio.h>
#include <string.h>
#define MAXS 256
char *checkforsymbols (char *s);
int main (void) {
char str[MAXS] = "";
size_t len = 0;
for (;;) { /* loop until str w/o symbols */
printf (" enter string: "); /* prompt for user input */
if (!fgets (str, MAXS, stdin)) { /* read/validate input */
fprintf (stderr, "EOF received -> user canceled input.\n");
return 1;
}
len = strlen (str); /* get length of str */
if (str[len - 1] == '\n') /* check for trailing '\n' */
str[--len] = 0; /* overwrite with nul-byte */
if (checkforsymbols (str)) /* check for symbols */
break;
}
printf (" valid str: '%s'\n", str);
return 0;
}
char *checkforsymbols (char *s)
{
if (!s || !*s) return NULL; /* validate string and not empty */
char *p = s; /* pointer to iterate over string */
for (; *p; p++) /* for each char in s */
if ((*p < 'a' || *p > 'z') && /* char is not a-z */
(*p < 'A' || *p > 'Z') && /* char is not A-Z */
(*p < '0' || *p > '9')) { /* char is not 0-9 */
fprintf (stderr, "error: '%c' not allowed in input.\n", *p);
return NULL; /* indicate failure */
}
return s; /* indicate success */
}
Example Use/Output
$ ./bin/str_chksym
enter string: mydoghas$20worthoffleas
error: '$' not allowed in input.
enter string: Baddog!
error: '!' not allowed in input.
enter string: Okheisagood10yearolddog
valid str: 'Okheisagood10yearolddog'
or if the user cancels user input:
$ ./bin/str_chksym
enter string: EOF received -> user canceled input.
footnote 1.
C generally prefers the use of all lower-case variable names, while reserving all upper-case for macros and defines. Leave MixedCase or camelCase variable names for C++ and java. However, since this is a matter of style, this is completely up to you.
This seems like it should be a simple thing but after hours of searching I've found nothing...
I've got a function that reads an input string from stdin and sanitizes it. The problem is that when I hit enter without typing anything in, it apparently just reads in some junk from the input buffer.
In the following examples, the prompt is "input?" and everything that occurs after it on the same line is what I type. The line following the prompt echoes what the function has read.
First, here is what happens when I type something in both times. In this case, the function works exactly as intended.
input? abcd
abcd
input? efgh
efgh
Second, here is what happens when I type something in the first time, but just hit enter the second time:
input? abcd
abcd
input?
cd
And here is what happens when I just hit enter both times:
input?
y
input?
y
It happens to return either 'y' or '#' every time when I run it anew. 'y' is particularly dangerous for obvious reasons.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#define STRLEN 128
int main() {
char str[STRLEN];
promptString("input?", str);
printf("%s\n", str);
promptString("input?", str);
printf("%s\n", str);
return EXIT_SUCCESS;
}
void promptString(const char* _prompt, char* _writeTo) {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
cleanString(_writeTo);
return;
}
void cleanString(char* _str) {
char temp[STRLEN];
int i = 0;
int j = 0;
while (_str[i] < 32 || _str[i] > 126)
i++;
while (_str[i] > 31 && _str[i] < 127) {
temp[j] = _str[i];
i++;
j++;
}
i = 0;
while (i < j) {
_str[i] = temp[i];
i++;
}
_str[i] = '\0';
return;
}
I've tried various methods (even the unsafe ones) of flushing the input buffer (fseek, rewind, fflush). None of it has fixed this.
How can I detect an empty input so that I can re-prompt, instead of this annoying and potentially dangerous behavior?
This part of cleanString
while (_str[i] < 32 || _str[i] > 126)
i++;
jumps over \0 when the string is empty.
You should add _str[i] != '\0' into the loop's condition.
To detect an empty string, simply check it's length just after the input:
do {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
} while (strlen(_writeTo) < 2);
(comparing with two because of '\n' which fgets puts into the end of buffer)
Why do you have a bunch of variable names with leading underscores? That's nasty.
Anyway, the first thing you must do is check the return value of fgets. If it returns NULL, you didn't get any input. (You can then test feof or ferror to find out why you didn't get input.)
Moving on to cleanString, you have a while loop that consumes a sequence of non-printable characters (and you could use isprint for that instead of magic numbers), followed by a while loop that consumes a sequence of printable characters. If the input string doesn't consist of a sequence of non-printables followed by a sequence of printables, you will either consume too much or not enough. Why not use a single loop?
while(str[i]) {
if(isprint(str[i]))
temp[j++] = str[i];
++i;
}
This is guaranteed to consume the whole string until the \0 terminator, and it can't keep going past the terminator, and it copies the "good" characters to temp. I assume that's what you wanted.
You don't even really need to use a temp buffer, you could just copy from str[i] to str[j], since j can never get ahead of i you'll never be overwriting anything that you haven't already processed.
This question already has answers here:
How to check if a string starts with another string in C?
(10 answers)
Closed 7 years ago.
In C how do I strcmp just the beginning 2 characters? Then concatenate with another string? Something like this:
char s[10];
scanf("%s",s);
/* if i input "cs332" or "cs234", anything start with cs */
if (strcmp("cs",???)==0)
strcat(s,"by professor");
You are looking for the strncmp function which is functionally identical to strcmp but limits the number of characters checked. So you would use it with a length of two and the comparison string of "cs". But, you have a few other problems here.
First, your buffer is not big enough. There is no string that will fit into a ten-character buffer when you append the text "by professor" to it.
Secondly, robust code will never use scanf with an unbounded-string format specifier: that's asking for a buffer overflow problem. The scanf family is meant for formatted input and there is little more unformatted than user input :-)
If you want a robust input solution, see one of my previous answers.
Thirdly, you should always assume that concatenating a string may overflow your buffer, and introduce code to prevent this. You need to add up:
the current length of the string, input by the user.
the length of the appending string ("by professor").
one more for the null terminator.
and ensure the buffer is big enough.
The method I would use would be to have a (for example) 200-byte buffer, use getLine() from the linked answer (reproduced below to make this answer self-contained) with a sufficiently smaller size (say 100), then you can be assured that appending "by professor" will not overflow the buffer.
Function:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
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.
if (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;
}
Test code:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
why not directly comparing characters rather than calling strcmp?
E.g.
if(s[0]=='c' && s[1]=='s'){
...
}
if (strncmp("cs",???, 2)==0) strcat(s,"by professor");
Use strncmp
Several ways to do this.
String comparison:
if ( s[0] == 'c' && s[1] == 's' )
Is the naive way, as you can't expand this easily to slightly longer codes (say 3/4 characters in length).
I guess you've gathered you should be using strncmp() right?
String Concaternation
Don't use strcat. Really. If you concatenate two strings whose length is greater than the size of s (the destination) you're in trouble. Consider using snprint() instead, like this:
char str[80];
snprintf(str, 80, "%s by professor", s);
Or, you could use strncat() as Heath points out:
char s[80];
strncat(s, " by professor", 80);
You can use strncmp.
Edit:
strcat(s,"by professor");
// s is an array of 10 characters. You need to make sure s is big enough
// to hold the string that needs to be concatenated + to have a terminating
// character '\0'.
Yes, as said, strcmp is the preferred method. Here's just a different way to do the same.
#define CS 29539
char s[80];
scanf("%60s", s);
if( *(short *)s == CS )
if( strlcat(s, " by professor", sizeof(s)) >= sizeof(s) )
fprintf(stderr, "WARNING: truncation detected: %s", s);