As part of a larger code repository, I replicated the gets() function by creating my own. The function merely accepts characters into a character array until it encounters a newline character or detects that the string length has been breached. The code is as follows:
void word_enter(char *word_search)
{
char r;
char *m=word_search;
printf("Enter the word to search for: ");
while((r==getchar())!=EOF)
{
if(r=='\n' || m==&word_search[WORD_LEN-1])
{
*m=0;
break;
}
else
{
*(m++)=r;
}
}
}
Apropos WORD_LEN, its value has been defined as 25 by the #define directive.
The function, however, doesn't work properly, and is rather jittery. Inputting a newline does not make the function cease, and the cursor merely shifts to the next line. What explains this? I've been bawling my hearts out figuring out as to what the error is, but to no avail. Be advised that while I'm awfully cognisant of the fact the string.h library has the same function under its belt, I intend to create mine from scratch.
You are comparing r with the return of getchar():
while((r==getchar())!=EOF)
What you should do instead is to assign the return value of getchar() to r:
while((r=getchar())!=EOF)
Related
I have just started learning C after coding for some while in Java and Python.
I was wondering how I could "validate" a string input (if it stands in a certain criteria) and I stumbled upon the sscanf() function.
I had the impression that it acts kind of similarly to regular expressions, however I didn't quite manage to tell how I can create rather complex queries with it.
For example, lets say I have the following string:
char str[]={"Santa-monica 123"}
I want to use sscanf() to check if the string has only letters, numbers and dashes in it.
Could someone please elaborate?
The fact that sscanf allows something that looks a bit like a character class by no means implies that it is anything at all like a regular expression library. In fact, Posix doesn't even require the scanf functions to accept character ranges inside character classes, although I suspect that it will work fine on any implementation you will run into.
But the scanning problem you have does not require regular expressions, either. All you need is a repeated character class match, and sscanf can certainly do that:
#include <stdbool.h>
bool check_string(const char* s) {
int n = 0;
sscanf(s, "%*[-a-zA-Z0-9]%n", &n);
return s[n] == 0;
}
The idea behind that scanf format is that the first conversion will match and discard the longest initial sequence consisting of valid characters. (It might fail if the first character is invalid. Thanks to #chux for pointing that out.) If it succeeds, it will then set n to the current scan point, which is the offset of the next character. If the next character is a NUL, then all the characters were good. (This version returns OK for the empty string, since it contains no illegal characters. If you want the empty string to fail, change the return condition to return n && s[n] == 0;)
You could also do this with the standard regex library (or any more sophisticated library, if you prefer, but the Posix library is usually available without additional work). This requires a little bit more code in order to compile the regular expression. For efficiency, the following attempts to compile the regex only once, but for simplicity I left out the synchronization to avoid data races during initialization, so don't use this in a multithreaded application.
#include <regex.h>
#include <stdbool.h>
bool check_string(const char* s) {
static regex_t* re_ptr = NULL;
static regex_t re;
if (!re_ptr) regcomp((re_ptr = &re), "^[[:alnum:]-]*$", REG_EXTENDED);
return regexec(re_ptr, s, 0, NULL, 0) == 0;
}
I want to use sscanf() to check if the string has only letters, numbers and dashes in it.
Variation of #rici good answer.
Create a scanset for letters, numbers and dashes.
//v The * indicates to scan, but not save the result.
// v Dash (or minus sign), best to list first.
"%*[-0-9A-Za-z]"
// ^^^^^^ Letters a-z, both cases
// ^^^ Digits
Use "%n" to detect how far the scan went.
Now we can use determine if
Scanning stop due to a null character (the whole string is valid)
Scanning stop due to an invalid character
int n = 0;
sscanf(str, "%*[-0-9A-Za-z]%n", &n);
bool success = (str[n] == '\0');
sscanf does not have this functionality, the argument you are referring to is a format specifier and not used for validation. see here: https://www.tutorialspoint.com/c_standard_library/c_function_sscanf.htm
as also mentioned sscanf is for a different job. for more in formation see this link. You can loop over string using isalpha and isdigit to check if chars in string are digits and alphabetic characters or no.
char str[]={"Santa-monica 123"}
for (int i = 0; str[i] != '\0'; i++)
{
if ((!isalpha(str[i])) && (!isdigit(str[i])) && (str[i] != '-'))
printf("wrong character %c", str[i]);//this will be printed for spaces too
}
I want to ... check if the string has only letters, numbers and dashes in it.
In C that's traditionally done with isalnum(3) and friends.
bool valid( const char str[] ) {
for( const char *p = str; p < str + strlen(str); p++ ) {
if( ! (isalnum(*p) || *p == '-') )
return false;
}
return true;
}
You can also use your friendly neighborhood regex(3), but you'll find that requires a surprising amount of code for a simple scan.
After retrieving value on sscanf(), you may use regular expression to validate the value.
Please see Regular Expression ic C
An exercise asked to write a function that:
reads a sequence of alphabetic characters (without memorizing the sequence) that ends only when the users enters '\n'.
returns 1 if the number of capitalized letters went beyond the lower ones of at most an integer m, entered by the user, or 0 else.
I tried with the following code:
#include<stdio.h>
int read(int p,char c)
{
int M=0,m=0,d;
char A,Z,a,z;
while(c != '\n')
{
if(A<=c<=Z)
{
M++;
}
else if(a<=c<=z)
{
m++;
}
scanf("%c",&c);
}
if(M-m>0)
d=(m-M);
else
d=0;
if(d==0)
return 0;
else if (d<=p)
return 1;
}
int main()
{
int a,h;
char k;
scanf("%d", &h);
scanf("%c", &k);
a=read(h,k);
printf("%d\n",a);
return 0;
}
At this point, trying to execute the program with the gcc command, i noticed that the program was taking just the integer, let's say 2, and gave back 0 as if it entered in the function without taking the second scan on the character.
Besides the formal misconception and errors about the program and c function that i'm glad you rectify,
I was trying to understand, because as they say i'm trying to be self-taught, how scanf function and function work in general, when and to who priority is given.
For example in function read it's not clear to me when the value i'm returning to the function are taken putting a higher if as i did.
This isn't going to do what you probably expect
if(A<=c<=Z)
... for all sorts of reasons. Firstly, the values of A and Z are uninitialized. Second, the logic is written to be read by a mathematician, not a C compiler.
You almost certainly wanted this:
if('A'<=c && c<='Z')
... and remove the four variables char A,Z,a,z;
Note that use of character constants such as 'A' and 'Z' assumes a runtime environment using ASCII character sets. If you're interested in a more portable solution, you can look up isupper() and islower()
I am writing a text editor in ncurses. The program is initialized in raw mode. So I need to manually do many things like deletion, avoiding printing non printable characters, etc.
For deletion:
void console(ch)
{
if(ch == 8) //8 = backspace according to asciitables.com
{
printw("\b");
printw(" ");
}
else
{
addch(ch);
}
}
For avoiding non-printable characters:
void console(ch)
{
bool safe = TRUE;
int avoid[] = { 1,2,3,4,5,6,7,8};
for(int i=0;i<4;i++)
{
while(ch==avoid[i])
{
safe = false;
}
}
if(safe)
{
printw("%c",ch); //Prints the key's characters on the screen.
}
else
{
break;
}
}
In the deletion, I wanted to deleted the previously printed character in the terminal and insert a blank space and move the cursor back to the place of the previous character. But that doesn't work.
In the avoid non-printable character, I wanted to avoid the non printable characters to get printed and only print the printable character. But that also doesn't seems to work.
It would be very helpful if someone points me where I am wrong and correct me. It would be also helpful if anybody can tell me whether there are any specific functions for this in the ncurses library. I am pretty much new to ncurses.
The easiest way in curses to detect "nonprintable" characters is to examine the result from unctrl. If the character is printable, the result is a single-character. Otherwise it is two or more characters:
char *check = unctrl(ch);
int safe = (check != 0 && strlen(check) == 1);
(The manual page goes into some detail).
By the way, addch is more appropriate than printw for printing characters (but keep in mind that its parameter is a chtype, which fits in an int, not char). Again, the manual page would be useful reading to prepare your program.
I'm stuck in a problem given in a book. The problem is -
The prototypical Internet newbie is a fellow name B1FF,who has a unique way of wriring messages.Here’s a typical B1FF communique.:
H3Y DUD3, C 15 R1LLY C00L
Write a “B1FF filter” that reads a message entered by the user and translates it into B1FF-speak:
Enter message: Hey dude, C is rilly cool
In B1FF-speak : H3Y DUD3, C 15 R1LLY C00L
Your program should convert the message to upper-case letters,substitute digits for certain letters (A=4,B=8,E=3,I=1,O=0,S=5).
My Program-
#include<stdio.h>
int main()
{
char arr[50]={0},ch[50]={0};
int i=0;
printf("\nenter the sentence : ");
while(arr[i-1]!='\n')
{
scanf("%c",&arr[i]);
i++;
}
i=0;
while(arr[i]!='\n')
{
if(arr[i]=='e')
ch[i]='3';
if(arr[i]==('A'||'a') )
ch[i]='4';
if(arr[i]==('B'||'b'))
ch[i]='8';
if(arr[i]==('I'||'i'))
ch[i]='1';
if(arr[i]==('o'||'O'))
ch[i]='0';
if(arr[i]==('S'||'s'))
ch[i]='5';
else ch[i]=arr[i]-32;
i++;
}
ch[i]='\n';
i=0;
printf("\nIn B1FF-SPEAK : ");
while(ch[i]!='\n')
{
printf("%c",ch[i]);
i++;
}
printf("\n");
return 0;
}
OUTPUT OF THE PROGRAM-
I don't understand why the program is not converting the alphabets and why scanf() is not accepting space as a character ?
First and foremost, you cannot chain the logical OR operator like
if(arr[i]==('A'||'a') )
and get what you're expecting, because this resolves to an always TRUE condition. [('A'||'a') evaluates to TRUE]
You have to use
if ( (arr[i]=='A') || (arr[i] =='a'))
That said,
ch[i]=='8';, ch[i]==1; are basically empty statements. You're comparing and discarding the comparison result. If you want assignment, you need to use =.
the else ch[i]=arr[i]-32; only binds with the previous if statement, no the whole if chains. You can either make use of switch case or if-else if-else constructs to take care of that part.
you did not handle whitespaces separately.
To elaborate, due to the second point above, you code basically reduces to
while(arr[i]!='\n')
{
if(arr[i]==('S'||'s')) //always false
ch[i]='5';
else ch[i]=arr[i]-32; //always TRUE
i++;
}
which is just a poor attempt to convert lowercase to UPPERCASE. In case a non-alpha input is there in the string, the code will blow up.
That said, regarding the
why scanf() is not accepting space as a character?
part, scanf() perfectly accepts a space as an input with %c, it's you just convert it no NUL (null) by blindly subtracting 32 from a space which has an ASCII value of decimal 32. A NUL is non printable, and does not appear in the output.
Some recommendations:
Don't use lots of little calls to scanf("%c"...). Use fgets() (manpage). fgets reads in a whole line from input, stores it in your buffer (including the newline), and sticks a '\0' byte at the end of it so you know how long the string is.
use toupper() (manpage). It takes an arbitrary character as input. If the input is a lower case letter, then the output is the upper-case version of that letter. Otherwise, the output is the same as the input.
Update the array in-place. You don't need two arrays. Read the array with fgets(), loop over it with your translation logic, updating each character as you go, then write the array out with fputs().
Learn about switch statements. A switch statement will make your code more compact and easier to read. You need one case for every letter->number transform, and a default case for everything else.
c = toUpper(c);
switch (c) {
case 'A': c = '4'; break;
case 'B': c = '8'; break;
....
default: break;
}
My problem is: Input the string then replace the word that we want to change
For example: input: i love coke
word: coke
replace: pepsi
result: i love pepsi
But when i run this code it crashed. Can you help show me the mistake?
#include <stdio.h>
#include<string.h>
char replace(char s1[100],char s2[100],char s3[100])
{
int k,i,j;
for(i=0;i<strlen(s1);i++)
for(j=0;j<strlen(s2);j++)
for(k=0;k<strlen(s3);k++)
{
if(s1[i]==s2[j])
{
s1[i]=s3[k];
}
}
return s3;
}
int main()
{
char s1[100],s2[100],s3[100];
printf("input string: ");gets(s1);
printf("Find string: ");gets(s2);
printf("Replace: ");gets(s3);
printf("Result: %s",replace(s1,s2,s3));
return 0;
}
I suggest you use a 4th buffer to store the generated result. You won't be able to replace locally if the word to be replaced and the new word aren't the same length.
Also, you are comparing characters individually. Just because you found a c doesn't automatically mean you found coke and that you should replace it. You must check the entire word is there before replacing anything. Use strstr() to locate substrings inside a string.
In addition, your function is returning a char, it should return a string (char *).
Furthermore, there are plenty of examples online on how to write a function to replace words on a string, so lets not be reduntant. Google it.
Strings in C are null terminated e.g. "i love coke\0". The string length does not include the null terminator. Because of this you are overwriting the null terminator after the 'e' with the 'i' in "pepsi".
A quick hack to check if null terminating the string would help, is to memset s1, s2, and s3 to 0.
Your approach doesn't quite work. What you need to do is search the input string for the word you wish to replace. So, before you even start switching things around, you need to search for the whole word you wish to replace.
Once you find that word, you need to then put in your new word in it's place, and then start searching for the word again untill you finish your input string.
So, for pseudo code:
for i in input //for every letter
if input[i] != lookfor[0]
results[i] put input[i] into new "results" array
else // We might have found it.
for j in lookfor // Go through coke, one at a time
if input[i+j] != lookfor[j] "c,o,k,e"
break; //You didn;t find coke, just "c" or "co" or "cok"
// If you got all the way through, you found coke.
//So now you have to switch that out for the new that in the result
results[i] = "pepsi" //Just be careful here, because this has a different index than i, because pespi's length != coke's length
Did that make sense?
First of all, your replace function is returning to char instead of char*. You can also define your function's return type as void and can make it return to char* buffer (in/out) parameter after in-function string operations. Moreover, you can use strtok(), strcmp() and strstr() predefined string.h functions to accomplish any kind of string operations.
Check this out to get information about standart string operation functions: String Operations