What do those functions check?
From what I understand, they are supposed to check if a word contains a non-alphanumeric character.
I don't understand how it does this.
My understanding:
The first check is for the length - this is OK.
The second check is if the character is a letter:
IsLetter(symbol[0])) evaluates to FALSE. This is logically negated.
The third function is the same as the above.
What I didn't understand, is the fourth one:
IsLetterOrDigit(*symbol)).
How does it check if the word has non-alphanumeric characters?
The code:
int IsSymbolValid(char* symbol)
{
int len = strlen(symbol);
if ((len == 0) || (len > MAX_SYMBOL_SIZE))
{
strcpy(LastParsingError, "Invalid symbol length");
return 0;
}
if (!IsLetter(symbol[0]))
{
strcpy(LastParsingError, "Symbol name has to start with letter");
return 0;
}
while (*symbol != 0)
{
if (IsLetterOrDigit(*symbol))
{
strcpy(LastParsingError, "Symbol name can contain only letters and digits");
return 0;
}
++symbol;
}
return 1;
}
int IsLetter(char ch)
{
return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')));
}
int IsDigit(char ch)
{
return ((ch >= '0') && (ch <= '9'));
}
int IsLetterOrDigit(char ch)
{
return (IsLetter(ch) && IsDigit(ch));
}
Your confusion comes from the fact that this function is indeed wrong:
int IsLetterOrDigit(char ch)
{
return (IsLetter(ch) && IsDigit(ch));
}
&& shows logical and, while a character cannot be both a digit and a letter. It should have used || (logical or):
int IsLetterOrDigit(char ch)
{
return (IsLetter(ch) || IsDigit(ch));
}
While we are at it, it seems like there is also a bug where it is being used:
if (IsLetterOrDigit(*symbol))
{
strcpy(LastParsingError, "Symbol name can contain only letters and digits");
return 0;
}
You'd want the error if the characters is not letter or digit. Therefore (notice the !):
if (!IsLetterOrDigit(*symbol))
{
strcpy(LastParsingError, "Symbol name can contain only letters and digits");
return 0;
}
Related
I have solved the exercises 3.3 from the K&R book. The solution I have implemented seems to work, but is a bit verbose and there could be smarter way to write this code. I wanted to ask if there could be problems with the solution I implemented and if there were easier way to write it:
Write a function expand(s1,s2) that expands shorthand notations like
a-z in the string s1 into the equivalent complete list abc...xyz in
s2. Allow for letters of either case and digits, and be prepared to
handle cases like a-b-c and a-z0-9 and -a-z. Arrange that a leading or
trailing - is taken literally
My code is this one:
#include <stdio.h>
void expand(char s1[],char s2[]){
int j=0,i=0;
while(s1[j] != '\0'){
if (s1[j]>= 'a' && s1[j] <= 'z' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= 'a' && s1[j+2] <= 'z' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (s1[j]>= 'A' && s1[j] <= 'Z' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= 'A' && s1[j+2] <= 'Z' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (s1[j]>= '0' && s1[j] <= '9' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= '0' && s1[j+2] <= '9' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (j!= 0 && s1[j] == '-' && (s1[j-1] < s1[j+1])){
int z = s1[j+1]-(1+s1[j-1]);
int c;
for (c=0;c<=z;c++){
s2[i]= c+(s1[j-1]+1);
i++;
}
j=j+2;
}
else if ( s1[j]>= 32 && s1[j] <= 127 && (s1[j+1] != '-' || s1[j+1]>= 32 && s1[j+1] <= 127 )){
s2[i] = s1[j];
j++;
i++;
}
}
s2[i]='\n';
i++;
s2[i]='\0';
}
int main() {
int c;
char s2[100];
expand("-a-c,a-c-g,A-Z0-9--", s2);
printf("%s",s2);
}
The code works in this way:
First it check if there is a triplet of the kind "x-y" where x<y. Then if gives to the array the values from x to y included and jump to the next character after the triplet "x-y". The same is done for upper case letters and for numbers in further if conditions.
the condition else if (j!= 0 && s1[j] == '-' && (s1[j-1] < s1[j+1])) is used to check for cases like "a-c-d1". The code I have implemented in this example will work like this:
Since we start with the 0-th character in "a-c-d" and the pattern "x-y" is present, "abc" will be assigned to the array. then we will directly jump to the second - in "a-c-f". Since this second - is preceded by a letter "c" and followed by a letter "f", and "c"<"f", then the characters between "c" and "f" will be assigned to the array, excluding the initial "c". Then the index for the string will jump of two and reach 1.
Some other way :
you only to know the last char before - and if it is the same type as current one (lower or upper case letter or digit)
when you get a - and previous char is a letter or digit you know you may have to make expansion
if you have a letter or digit after - and it is corresponding to letter/digit before - you know you can expand from char before / to current one.
you do need to look forward but only save previous char and char before -
you do same kind of processing for each different char type (letter/digit)
You can find an example after :
#include <stdio.h>
// handle different char type
typedef enum E_chartype {
LowerCaseLetter,
UpperCaseLetter,
Digit09,
OtherChar
} E_chartype;
// save if we may have a posdible expansion
typedef enum E_states {
NothingStarted,
StartedExpansion
} E_states;
// find type of a char
E_chartype getCharType(char c) {
if ((c >= 'a') && (c <= 'z'))
return LowerCaseLetter;
if (( c >= 'A') && (c <= 'Z'))
return UpperCaseLetter;
if ((c >= '0') && (c <= '9'))
return Digit09;
return OtherChar;
}
void expandopt(char *inS, char *outS) {
// init output string to null string
outS[0] = 0;
char *endS = outS;
E_states automat = NothingStarted;
char savedChar = 0;
int currentIndex;
E_chartype prevCType=OtherChar,savedCType=OtherChar;
char savedC = 0,prevC=0;
// loop on input string
for (currentIndex = 0; inS[currentIndex] != 0;currentIndex++) {
// save current char in variable c for shorter writting
char c = inS[currentIndex];
printf("%c : ",c);
// save type of current char
E_chartype currentCType = getCharType(c);
switch (automat) {
// genersl case notjing yet started
case NothingStarted:
// possibkee expansion if previous chsr is letter or digit and current char is -
if ((prevCType != OtherChar) && (c == '-')) {
printf("start rep\n");
automat = StartedExpansion;
// save the previous char and its type as it eill br the reference fircexpansion
savedCType = prevCType;
savedC = prevC;
} else {
// reset and cooy current char to iutput
automat = NothingStarted;
printf("nothing\n");
*endS++ = c;
}
break;
case StartedExpansion:
// we make ecpansion only if still same char type and letter/digit is strictly after saved one
if ((currentCType == savedCType) && (c > savedC)){
printf("expansion ");
for (char newC
= savedC+1;newC <= c;newC++) {
*endS++ = newC;
}
// save char in case thrre id a - after, which mean nee expansion
savedC = c;
} else {
// save current chsrcsnd its type
savedCType = currentCType;
savedC = c;
// copy previous char (= -) whch was not vopief in case of expansion
*endS++ = prevC;
*endS++ = c;
}
automat = NothingStarted;
break;
}
// save current chsr and type
prevCType = currentCType;
prevC = c;
}
// add 0 at end of string
*endS = 0;
}
int main() {
expandopt("-a-c,a-c-g,A-Z0-9–",s2);
printf("%s\n",s2);
}
Sorry for the code formatting, I did not find good code editor on phone.
i should realize two very similar functions but i am having problems.
I have to read the string "username", this string can only contain letters (upper and lower case) and spaces.
I have to read the string "key", this string can only contain letters (upper and lower case) and numbers.
If the guidelines are not followed, the user must be able to retrieve the input.
Unfortunately, I cannot use special libraries (only stdio and stdlib).
I realized this:
void checkString(char *i){
int cont;
do {
scanf("%s", i);
if (checkStrLen(6, 6, i) != 0) { //function that controls the size of the string (min,max,string)
for(cont=0; cont<6;){
if((i[cont]>='0' && i[cont]<='9')||
(i[cont]>='A' && i[cont]<='Z')||
(i[cont]>='a' && i[cont]<='z')){
cont++;
}else{
printf("Not valid character");
printf("Try again");
}
}
}else{
printf("\nToo large string");
printf("\nTry again");
}
}while(1);
}
I was thinking of doing something similar.
For the first problem I would replace (i[cont]>='0' && i[cont]<='9') with (i[cont]==' ').
the problem is that I don't understand how to get out of the for if I find a forbidden character during the loop.
I was thinking of using a break, but that would get me out of the whole function.
any advice?
PS how does the function look like? can it be okay or is it completely wrong?
I think the do while loop is not necessary here. do the scanf and get user input first then call checkString. Inside checkString keep your if else statement.
char checkString(char *i){
int cont;
if (checkStrLen(6, 6, i) != 0) { //function that controls the size of the string (min,max,string)
for(cont=0; cont<6;){
if((i[cont]>='0' && i[cont]<='9')||
(i[cont]>='A' && i[cont]<='Z')||
(i[cont]>='a' && i[cont]<='z')){
cont++;
}else{
printf("Not valid character");
printf("Try again");
return i;
}
}
}
else{
printf("\nToo large string");
printf("\nTry again");
}
}
#include <stdio.h>
#define MAXSIZE 100
#define SIZELIM 6
#define true 1
#define false 0
// Returns length of string
// If possible, use strlen() from <string.h> instead
int strlen(char *str) {
char i;
for (i = 0; str[i] != 0 && str[i] != '\n'; i++);
return i;
}
// Returns 1 if strings are equal
// If possible, use strcmp() from <string.h> instead
int streq(const char *x, const char *y) {
char chrx = 1, chry = 1, i;
for (i = 0;
chrx != 0 && chry != 0 && chrx == chry;
chrx = x[i], chry = y[i], i++);
return chrx == chry;
}
// Returns 1 if chr is number or letter
// If possible, use isalnum() from <ctype.h> instead
int isalnum(const char chr) {
return (chr >= '0' && chr <= '9' ||
chr >= 'A' && chr <= 'Z' ||
chr >= 'a' && chr <= 'z');
}
// Checks if string contains numbers and letters only
int isvalid(const char *str) {
int valid = true;
for (int i = 0; str[i] != 0 && str[i] != '\n'; i++) {
if (!isalnum(str[i])) {
valid = false;
break;
}
}
return valid;
}
// Main
int main(void) {
char str[MAXSIZE];
for (;;) {
printf("> ");
fgets(str, MAXSIZE, stdin);
if (streq(str, "quit\n"))
break;
if (strlen(str) > SIZELIM || !isvalid(str)) {
if (strlen(str) > SIZELIM)
puts("String too large");
else if (!isvalid(str))
puts("Not a valid string");
puts("Try again"); }
}
return 0;
}
You can code those functions that you cannot import:
int letters_and_spaces(char c)
{
return c == ' ' || C >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
}
int letters_and_numbers(char c)
{
return c >= '0' && c <= '9' || C >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
}
And to use scanf to read spaces you can't use %s. You could change to:
scanf("%100[^\n]*c", i);
BE CAREFUL: I've put 100, supposing i has enough space for that. It will read up to 100 characters (or as many as the number you put there) or until find the \n.
I am trying to detect whether a string contains only the characters '0' and '1'. This is what I have so far:
while (indexCheck < 32) {
if ((input[indexCheck] != '0') && (input[indexCheck] != '1')) {
printf("not binary ");
indexCheck++;
} else if ((input[indexCheck] = '0') && (input[indexCheck] = '1')) {
indexCheck++;
printf("is binary ");
}
}
I know why it returns "is binary" or "not binary" for every single character in the array, but I don't know how to fix this. I want it to return "is binary" once if the string is only made of '1' and '0', and the opposite if this is false. I'm new to C so all help is appreciated.
Instead of looping manually through the string, you can see if it only contains certain characters by checking to see if strspn() returns the length of the string (By seeing if the index of the value it returns is the 0 terminator at the end of the string):
_Bool is_binary(const char *s) {
if (!s || !*s) {
return 0;
}
return s[strspn(s, "01")] == '\0';
}
I would make a function for this:
int isBinary(const char *input)
{
for (int i = 0; input[i]; ++i)
if (input[i] != '0' && input[i] != '1')
return 0;
return 1;
}
Then you can call the function:
if (isBinary("0001110110101"))
printf("is binary\n");
else
printf("is not binary\n");
https://ideone.com/tKBCbf
You can stop looping through the string the moment you find a character which is neither '0' nor '1'. After the loop is terminated, you check whether or not you've reached the end of the string, i.e. the current character is a null character '\0'
while (*s == '0' || *s == '1') ++s;
if (*s)
puts("not binary");
else
puts("binary");
You can do:
while (indexCheck < 32)
{
if ((input[indexCheck] != '0') && (input[indexCheck] != '1'))
{
break;
}
else
{
indexCheck++;
}
}
if (indexCheck == 32)
printf("is binary ");
else
printf("is not binary ");
Only when it has processed all elements and did not encounter a non 1-or-0 ends the loop with indexCheck == 32 so you can use that to determine what to print.
Note also that your else condition is not needed.
there is a block of code for you with comments.
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 32 // Better to use #define for reusability
// Function prototype
int isBinary(char * testInput);
// Main program
int main(void)
{
// Test inputs
char testInputBinary[33] = "01010101010101010101010101010101";
char testInputNotBinary[33] = "010101010101010101010101010101ab";
// Test & collect results
if (isBinary(testInputBinary))
{
printf("Binary ! \n");
}
else
{
printf("Not binary ! \n");
}
if (isBinary(testInputNotBinary))
{
printf("Binary ! \n");
}
else
{
printf("Not binary ! \n");
}
return EXIT_SUCCESS;
}
int isBinary(char * testInput)
{
int loopIdx = 0; // Loop index
int returnVal = 0; // 0: False, 1: True
// iterate over string
for (loopIdx = 0; loopIdx < STRING_SIZE; loopIdx++)
{
if(testInput[loopIdx] != '0' && testInput[loopIdx] != '1')
{
break;
}
}
// If the loop is not broken, it means all characters are in binary form
if (loopIdx == STRING_SIZE)
{
returnVal = 1;
} // No need to writing else clause since returnVal = 0 at the beginning
return returnVal;
}
int isBinary = 1;
while (input[indexCheck] != '\0')
{
if (input[indexCheck] != '1' && input[indexCheck] != '0')
{
isBinary = 0;
break;
}
++indexCheck;
}
if (isBinary)
{
printf("binary");
}
else
{
printf("not binary");
}
Check each element in string input. If input[index] is not 0 or 1 the flag isBinary becomes 0 and breaks while. And you do not need length of string.
I am trying out a program which translates user input based on the below rules:
If the character is a letter, it must be printed in upper case.
If the character is a number (0-9), an asterisk (*) must be printed instead.
If the character is a double quote ("), a single quote (') must be printed instead.
If the character is the backslash "\" then it is skipped (not printed), and the next character is printed without any modifications.
Otherwise, the character is printed as-is.
MyCode
#include <stdio.h>
void lowertoupper(char lower)
{
char upper;
if(lower >= 'a' && lower <= 'z'){
upper = ('A' + lower - 'a');
}
else{
upper = lower;
}
printf("%c",upper);
}
int main(void) {
char chara;
printf("please enter");
while(scanf(" %c", &chara)!= EOF) {
if ((chara>='a' && chara<='z')||(chara>='A' && chara<='Z'))
{
lowertoupper(chara);
}
else if (chara>=0 && chara<=9)
{
printf("*");
}
else if (chara=='"')
{
printf("'");
}
else if (chara=="\\")
{
}
else {
printf("%c",chara);
}
}
}
My questions
This doesn't as expected. What conditions am I missing here.
How can I satisfy the condition 4 without using any built in function.
int noChange = 0; /* Added */
while(scanf(" %c", &chara) == 1) { /* Modified */
if(noChange == 1) { /* Added */
printf("%c",chara); /* Added */
noChange = 0; /* Added */
} /* Added */
else if ((chara>='a' && chara<='z')||(chara>='A' && chara<='Z')) /* Modified */
{
lowertoupper(chara);
}
else if (chara >= '0' && chara <= '9') /* Modified */
{
printf("*");
}
else if (chara=='"')
{
printf("'");
}
else if (chara=='\\') /* Modified */
{
noChange = 1; /* Added */
}
else {
printf("%c",chara);
}
}
Working demo here
This also works, comparing chara stores the ascii value, so ascii value of '0' is not 0.
#include <stdio.h>
int main() {
char chara = '\0';
while(scanf("%c", &chara) == 1) {
if ( chara>='a' && chara<='z' ) printf("%c",'A' + (chara - 'a'));
else if (chara >= '0' && chara <= '9') printf("%c",'*');
else if (chara=='"') printf("%c",'\'');
else if (chara=='\\') ;
else printf("%c",chara);
}
}
Your test for digits is problem. The digit characters are '0' through '1', whose actual value depends on the local charset but are never 0 through 9.
Is there an easy way to call a C script to see if the user inputs a letter from the English alphabet? I'm thinking something like this:
if (variable == a - z) {printf("You entered a letter! You must enter a number!");} else (//do something}
I want to check to make sure the user does not enter a letter, but enters a number instead. Wondering if there is an easy way to pull every letter without manually typing in each letter of the alphabet :)
It's best to test for decimal numeric digits themselves instead of letters. isdigit.
#include <ctype.h>
if(isdigit(variable))
{
//valid input
}
else
{
//invalid input
}
#include <ctype.h>
if (isalpha(variable)) { ... }
isalpha() will test one character at a time. If the user input a number like 23A4, then you want to test every letter. You can use this:
bool isNumber(char *input) {
for (i = 0; input[i] != '\0'; i++)
if (isalpha(input[i]))
return false;
return true;
}
// accept and check
scanf("%s", input); // where input is a pointer to a char with memory allocated
if (isNumber(input)) {
number = atoi(input);
// rest of the code
}
I agree that atoi() is not thread safe and a deprecated function. You can write another simple function in place of that.
Aside from the isalpha function, you can do it like this:
char vrbl;
if ((vrbl >= 'a' && vrbl <= 'z') || (vrbl >= 'A' && vrbl <= 'Z'))
{
printf("You entered a letter! You must enter a number!");
}
The strto*() library functions come in handy here:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define SIZE ...
int main(void)
{
char buffer[SIZE];
printf("Gimme an integer value: ");
fflush(stdout);
if (fgets(buffer, sizeof buffer, stdin))
{
long value;
char *check;
/**
* strtol() scans the string and converts it to the equivalent
* integer value. check will point to the first character
* in the buffer that isn't part of a valid integer constant;
* e.g., if you type in "12W", check will point to 'W'.
*
* If check points to something other than whitespace or a 0
* terminator, then the input string is not a valid integer.
*/
value = strtol(buffer, &check, 0);
if (!isspace(*check) && *check != 0)
{
printf("%s is not a valid integer\n", buffer);
}
}
return 0;
}
You can also do it with few simple conditions to check whether a character is alphabet or not
if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
{
printf("Alphabet");
}
Or you can also use ASCII values
if((ch>=97 && ch<=122) || (ch>=65 && ch<=90))
{
printf("Alphabet");
}
int strOnlyNumbers(char *str)
{
char current_character;
/* While current_character isn't null */
while(current_character = *str)
{
if(
(current_character < '0')
||
(current_character > '9')
)
{
return 0;
}
else
{
++str;
}
}
return 1;
}
You can implement the following function that returns a boolean, it checks whether the input is only composed by characters and not numbers, it also ignores spaces. Note that it supposes that the input in collected by fgets and not scanf. You should only change the while condition if you want to use another input method.
bool is_character(char text[])
{
bool just_letters;
just_letters = true;
while((text[i] != '\n') && (just_letters == true))
{
if ((text[i] >= 'A' && text[i] <= 'Z') || (text[i] >= 'a' && text[i] <= 'z') || text[i] == 32)
{
just_letters = true;
}
else
{
just_letters = false;
}
}
return just_letters;
}