Can you please check what is wrong with this function? This function checks whether a string is numeric or not. If it is numeric (positive and negative integers and decimals are allowed), it returns a value of 1. Else, it returns 0. I tried to run the code and the output always returns 0.
{int is_valid (char strval[])
{
int strlen, ascii_code, i;
int decimal=-1, negative=-1;
strlen=strlen_safe(strval);
for (i=0; i<strlen; i++)
{
ascii_code=(int)strval[i];
if(ascii_code==44)
return 0;
else if(ascii_code==45)
{
negative++;
if(negative||i!=0)
return 0;
}
else if(ascii_code==46)
{
decimal++;
if(decimal>0)
return 0;
}
else if(ascii_code<48||ascii_code>57)
return 0;
}
return 1;
}
If you're fine with checking that a string is a number on the current platform (which might not be using ASCII as its encoding), you should just use strtod(). It will handle all the subtle ways a floating-point number can be formatted, and generally be much nicer to your users:
#include <stdlib.h>
int is_valid_number(const char *s, size_t maxdigits)
{
char *endp;
strtod(s, &endp);
const size_t digits = (size_t) (endp - s);
return *endp == '\0' && digits <= maxdigits;
}
The above is very conservative, it requires that all characters of the incoming string be part of the number. This means "43 " (note trailing space) would be rejected.
To make it accept just a prefix, change the final line to
return endp != s;
You don't need to convert strval[i] to int.
If '-' appears not in the beginning of the string, it will still reguard the string as a number.
If strlen_safe is your own function, it may be where the code fails.
This code should work.
int is_valid (char strval[])
{
if(!*strval)
return 0;
if(*strval=='-')
strval++;
char decimal=0;
while(*strval)
{
if(!isdigit(*strval))
{
if(*strval=='.')
{
if(decimal)
return 0;
decimal=1;
}
else
return 0;
}
strval++;
}
return 1;
}
Related
I am a beginner programmer and I'm having trouble with this task.
I have to write a function that checks if the digits in the given string are strictly ascending, or better say is each digit larger than the previous one.
Function returns true or false, but it also has to be able to return -1 if there are no numbers in the string.
So for the string: "a1b2c3d" the function should return 1,
but for "a1b3c3d" the function should return 0 because they are not strictly ascending.
I cannot use arrays or create new strings.
#include <stdio.h>
#include<ctype.h>
//we have to enter our strings like this
void unesi(char niz[],int velicina)
{
char znak=getchar();
if(znak=='\n')
znak=getchar();
int i=0;
while(i<velicina-1 && znak!='\n')
{
niz[i]=znak;
i++;
znak=getchar();
}
niz[i]='\0';
}
//function checks if the string contains any digits
int cifre(const char *s)
{
int istina=1;
if(isdigit(*s++))
istina=0;
return istina;
}
int srce(const char *s)
{
int broj=1,istina=0;
if(cifre(s)==1)
return -1;
else
{
while(*s!='\0')
{
if(isdigit(*s)==1)
{
if(*s<broj)
{
broj=*s;
istina=1;
s++;
}
}
else
s++;
}
return istina;
}
}
int main() {
char a[100];
unesi(a,100);
printf("%d",srce(a));
return 0;
}
Logic is quite simple.
The function returns -1 if there is no digits
1 if they are sorted in ascending order or zero if not
int isAsc(char *str)
{
int result = -1;
int last = -1;
while(*str)
{
if(isdigit((unsigned char)*str))
{
result = 1;
if(last != -1)
{
if(*str - '0' < last)
{
result = 0;
break;
}
}
last = *str - '0';
}
str++;
}
return result;
}
int main(void)
{
printf("%d\n", isAsc("dsfgdfgdfgfgd"));
printf("%d\n", isAsc("d0sfg1dfg4dfg7fgd"));
printf("%d\n", isAsc("d0sfg1dfg4dfg7f2gd"));
}
//we have to enter our strings like this
It's unclear to me if the whole unesi function is given as part of the assignment and can't be modified or not, so I won't comment it.
Just read about fgets, in the future.
I cannot use arrays
Well, I guess that restriction is somehow relaxed:
char a[100];
That's "literally" an array of 100 chars. Hopefully, it will be null-terminated, so that we can consider it a "string".
//function checks if the string contains any digits
That comment is lying, cifre is only checking the first character, because there is no loop, nor any calling to a function that could traverse a char array inside it.
Besides, instead of checking beforehand if the string contains any digit, you could start searching for the first digit and then check if the other digits are in strictly ascending order.
In srce function, we can find
int srce(const char *s)
{
int broj=1, istina=0;
// ^^^^^^ ^^ Note that the starting value of broj is 1.
// [...]
while(*s!='\0')
{
if(isdigit(*s)==1)
{
if( *s < broj )
{ //^^^^^^^^^ Are any of '0', '1', '2'... less than 1?
// Is this supposed to be the path taken when
// the digits are not in STRICTLY ascending order?
// In that case you should check if the current digit
// is less than OR EQUAL to the previous one.
broj=*s;
istina=1; // Shouldn't it be 0 when the order is not correct?
s++; // Shouldn't the traversal of the string just end now?
}
}
else
s++;
}
return istina;
}
Another subtle issue with the previous code is that isdigit1 returns a non-zero value if the character is a numeric character, not necessarly 1.
(1) About isdigit call, see also: Why is it OK to pass mismatch argument into isdigit function?
So I am trying to write code that will allow me to count the number of letters a user has entered.
My code runs well if I simply type one word.
The moment that I include any character that is not a letter, my terminal stops working.
What can I do to fix it?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
int count_letters(string text);
int main(void)
{
string text = get_string("text? ");
int letters = count_letters(text);
printf("%i letters\n", letters);
}
int count_letters(string text)
{
int i = 0;
do
{
if (isalpha(text[i]))
{
i++;
}
else if (isspace(text[i]))
{
i = + 0;
}
else
{
i = +0;
}
} while (text[i] != 0);
return i;
}
i is the do ... while counter, do not use it to count the number of letters, use another variable, something like:
int count_letters(string text)
{
int i = 0, letters = 0;
do
{
if (isalpha((unsigned char)text[i]))
{
letters++;
}
i++;
}
while (text[i] != 0);
return letters;
}
Notice that with your approach the loop is also testing the NUL terminator in an empty string, use a while loop instead to exit the loop as soon as the NUL is found:
int count_letters(string text)
{
int letters = 0;
while (*text)
{
if (isalpha((unsigned char)*text))
{
letters++;
}
text++;
}
return letters;
}
Consider what happens in your loop: i only increments when the character at text[i] is an alphabetic character. Otherwise it resets to zero.
Thus, the loop will never complete for a string that is not entirely alphabetic characters because the loop will "reset" to the beginning with ever non-alphabetic character.
We can increment an index from zero until the character at that index in the string is the null-terminator of zero (which is false in C).
int count_letters(string text) {
int count = 0;
for (int i = 0; text[i]; i++) {
if (isalpha(text[i])) count++;
}
return count;
}
We can however, use pointers to iterate over your string, taking advantage of the detection of the null terminator to terminate the loop. If each character is an alphabetic character, increment a counter. A for loop can handle giving you a temp pointer and initializing it, testing for termination of the loop and incrementing the pointer.
int count_letters(string text) {
int count = 0;
for (char *ch = text; *ch; ch++) {
if (isalpha(*ch)) count++;
}
return count;
}
As text is a pointer passed by value (being hidden by CS50's string typedef), it can be modified without affecting the original string as long as we don't dereference and modify the individual characters, so we can avoid the extra char pointer.
int count_letters(string text) {
int count = 0;
for (; *text; text++) {
if (isalpha(*text)) count++;
}
return count;
}
As pointed out in the comment, i cannot serve two purposes (index and counter) except for the special case of a string comprised of only letters.
Below is a compact function that counts 'hits' when isalpha() has returned a non-zero value (indicating the current letter is in the range "A-Za-z").
The unusual "!!" transforms the non-zero positive value into C's true (or false) having values of 1 (or 0) respectively. Thusly, the value of 1 or 0 is appropriately added to the accumulator to be returned to the caller.
int count_letters( string text ) {
int i = 0, cnt = 0;
if( text != NULL ) // trust is good, testing is better
while( text[i] )
cnt += !!isalpha( (unsigned char)text[i++] );
return cnt;
}
EDIT and Credit: #Aconcagua for pointing out the need for casting each char to unsigned char to avoid UB.
Given a string containing alphanumeric characters, calculate the sum of all numbers present in the string.
The problem with my code is that it displays the integers present before the characters, but it is not summing up the integers after the characters.
The execution is easy in python and C++ but I cant get it done using C! Can anyone please verify where I have done wrong? << thank you !
enter code here
#include<stdio.h>
#include<string.h>
int convert(char[]);
int main()
{
char ch[100],temp[100]={0};
int i=0,s=0,j=0,n;
scanf("%s",ch);
for(i=0;i<strlen(ch);i++)
{
if((ch[i]>='0') && (ch[i]<='9'))
{
temp[j]=ch[i];
j++;
}
else
{
if(temp[0]== '\0')
{
continue;
}
else
{
n=convert(temp);
s+=n;
temp[0]= '\0';
j=0;
}
}
}
printf("%d",s);
return 0;
}
int convert(char s[]) //converting string to integer
{
int n=0;
for(int i=0;i<strlen(s);i++)
{
n= n * 10 + s[i] - '0';
}
return n;
}
Input : 12abcd4
Expected output : 16
But the output is 12 for my code.
There are two problems in your code. The first was mentioned in the comments : if the last character is a digit, the last "number section" will not be taken into account. But I don't think that the solution given in the comments is good because if the last character is not a digit, you will have a wrong value. To correct this, I added an if statement that check if the last character is a digit, if so call convert().
The second problem is that strlen return the number of characters in you string from the beginning until it finds an '\0'. The way you used your string lead to the follow problem :
ch = "12abcd4".
At first you have temp = '1' + '2' + '\0'...
After calling convert() you set temp[0] to '\0', thus temp = '\0' + '2' + '\0'... .
And when you start reading digit again, you set '4' in temp[0]. Your string is now : '4' + '2' + '\0'... .
The n returned will be 42 and your result 54 (12+42). There are several solution to have the expected behavior, I chose to use your variable j to indicate how many characters should be read instead of using strlen() :
#include<stdio.h>
#include<string.h>
int convert(char[], int size);
int main() {
char ch[100],temp[100]={0};
int i=0,s=0,j=0,n;
scanf("%s",ch);
for(i=0;i<strlen(ch);i++) {
if((ch[i]>='0') && (ch[i]<='9')) {
temp[j]=ch[i];
j++;
// change here
if(i == strlen(ch) - 1) {
n=convert(temp, j);
s+=n;
}
}
else {
// change here
n=convert(temp, j);
s+=n;
if(temp[0]== '\0') {
continue;
}
temp[0]= '\0';
j=0;
}
}
printf("%d\n",s);
return 0;
}
//change here
int convert(char s[], int size) {
int n=0;
for(int i=0;i<size;i++) {
n= n * 10 + s[i] - '0';
}
return n;
}
You could use a combination of strtoul() and strpbrk() to do this.
Declare two character pointers start_ptr and end_ptr and make start_ptr point to the beginning of the string under consideration.
char *start_ptr=s, *end_ptr;
where s is the character array of size 100 holding the string.
Since your string has only alphanumeric characters, there is no - sign and hence there are no negative numbers. So we can get away with using unsigned integers.
We are using strtoul() from stdlib.h to perform the string to integer conversion. So let's declare two variables: rv for holding the value returned by strtoul() and sum to hold the sum of numbers.
unsigned long rv, sum_val=0;
Now use a loop:
for(; start_ptr!=NULL; )
{
rv = strtoul(start_ptr, &end_ptr, 10);
if(rv==ULONG_MAX && errno==ERANGE)
{
//out of range!
printf("\nOut of range.");
break;
}
else
{
printf("\n%lu", rv);
sum_val += rv;
start_ptr=strpbrk(end_ptr, "0123456789");
}
}
strtoul() will convert as much part of the string as possible and then make end_ptr point to the first character of the part of the string that could not be converted.
It will return ULONG_MAX if the number is too big and errno would be set to ERANGE.
Otherwise the converted number is returned.
strpbrk() would search for a set of characters (in this case the characters 0-9) and return a pointer to the first match. Otherwise NULL is returned.
Don't forget to include the following header files:
stdlib.h ---> strtoul
string.h ---> strpbrk
limits.h ---> ULONG_MAX
errno.h ---> errno
In short, we could make the program to something like
for(; start_ptr!=NULL; sum_val += rv, start_ptr=strpbrk(end_ptr, "0123456789"))
{
rv = strtoul(start_ptr, &end_ptr, 10);
if(rv==ULONG_MAX && errno==ERANGE)
{
//out of range!
break;
}
}
printf("\n\n%lu", sum_val);
So the value of sum_val for the string "12abcd4" would be 16.
scanf() is usually not the best way to accept input that is not well-formatted. Maybe you can use fgets()-sscanf() combo instead.
If you must use scanf(), make sure that you check the value returned by it, which in your case must be 1 (the number of successful assignments that scanf() made).
And to prevent overflow, use a width specifier as in
scanf("%99s",ch);
instead of
scanf("%s",ch);
as 100 is the size of the ch character array and we need one extra byte to store the string delimiter (the \0 character).
I want the code to check input in command line is integer. i.e. 10b is not valid. I tried isdigit() but is not working? Thanks in advance.
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc == 2)
{
int key = atoi(argv[1]);
if (isdigit(key))
{
printf("Success\n\%i\n", key);
exit(0);
}
}
printf("Usage: ./caesar key\n");
return 1;
}
Function isDigit checks if a single character is a digit, i.e. in the range between '0'..'9'. To check if a string is a number, I'd suggest to use function strtol.
long strtol(const char *str, char **str_end, int base ) converts a string str to an integral number and also sets the pointer str_end to the first character that took not part in the conversion any more. If you require that no characters must follow your number, then str_end must point to the string's end, i.e. to string termination character '\0':
#include <stdio.h>
#include <stdlib.h>
int isNumber(const char* str) {
if (!str || *str=='\0') { // NULL str or empty str: not a number
return 0;
}
char* endOfNum;
strtol(str,&endOfNum,10);
if (*endOfNum == '\0') { // string is at its end; everything in it was a valid part of the number
return 1;
} else {
return 0; // something that is not part of a number followed.
}
}
int main() {
const char* vals[] = {
"10b",
"-123",
"134 ",
" 345",
"",
NULL
};
for (int i=0; vals[i]; i++) {
printf("testing '%s': %d\n", vals[i], isNumber(vals[i]));
}
}
Output:
testing '10b': 0
testing '-123': 1
testing '134 ': 0
testing ' 345': 1
testing '': 0
Adapt the meaning of corner cases like empty strings or NULL-strings to your needs.
The isdigit function checks whether a single character represents a single digit. (numbers − 0 1 2 3 4 5 6 7 8 9).
In order to check if a string is an integer you could use a function like that. It will
bool isNumber(char number[])
{
int i = 0;
// only if you need to handle negative numbers
if (number[0] == '-')
i = 1;
for (; number[i] != 0; i++)
{
if (!isdigit(number[i]))
return false;
}
return true;
}
My first thought would be to use something like:
int inputvalue = 0;
if (sscanf(argv[i], "%d", &inputvalue) == 1)
{
// it's ok....
}
else
{
// not an integer!
}
or something like that. See http://www.cplusplus.com/reference/cstdio/sscanf/
I'm trying to create a function which can determine if an input can be converted perfectly into a double and then be able to store it into an array of doubles. For example, an input of "12.3a" is invalid. From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer. In my function doubleable, it filters whether the input string consists only of digits. However, I'm aware that double has "." like in "12.3", and my function will return 'X' (invalid).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char doubleable (char unsure[], int length){
int i;
int flag;
for (i=0; i<length; i++ ){
if(isdigit(unsure[i]==0)){
printf("You have invalid input.\n");
flag=1;
break;
}
}
//check for '.' (?)
if(flag==1)
return 'X';
else
return 'A';
}
int main(){
char input [10];
double converted[5];
char *ptr;
int i;
for(i=0; i<5; i++){
fgets(input, 10, stdin);
//some code here to replace '\n' to '\0' in input
if(doubleable(input, strlen(input))=='X'){
break;
}
converted[i]=strtod(input, &ptr);
//printf("%lf", converted[i]);
}
return 0;
}
I'm thinking of something like checking for the occurrence of "." in input, and by how much (for inputs like 12.3.12, which can be considered invalid). Am I on the right track? or are there easier ways to get through this? I've also read about the strtok function, will it be helpful here? That function is still quite vague to me, though.
EDIT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
double HUGE_VAL= 1000000;
void string_cleaner (char *dirty){
int i=0;
while(1){
if (dirty[i]=='\n'){
dirty[i]='\0';
break;
}
i++;
}
}
int doubleable2(const char *str)
{
char *end_ptr;
double result;
result = strtod(str, &end_ptr);
if (result == HUGE_VAL || result == 0 && end_ptr == str)
return 0; // Could not be converted
if (end_ptr < str + strlen(str))
return 0; // Other input in the string after the number
return 1; // All of the string is part of the number
}
int main(){
char input [10];
double converted[10];
char *ptr;
int i;
for(i=0; i<5; i++){
while (1){
printf("Please enter:");
fgets(input, 10, stdin);
string_cleaner(input);
if (doubleable2(input)==0)
continue;
else if (doubleable2(input)==1)
break;
}
converted[i]=strtod(input, &ptr);
printf("%lf\n", converted[i]);
}
return 0;
}
thank you! It works just fine! I have a follow up question. If I enter a string that is too long, the program breaks. If I am to limit the input to, let's say, a maximum of 9 characters in input[], how am I to do that?
from what I understand about fgets(xx, size, stdin), it only gets up to size characters (including \n, \0), and then stores it to xx. In my program, I thought if I set it to 10, anything beyond 10 will not be considered. However, if I input a string that is too long, my program breaks.
You can indeed use strtod and check the returned value and the pointer given as the second argument:
int doubleable(const char *str)
{
const char *end_ptr;
double result;
result = strtod(str, &end_ptr);
if (result == HUGE_VAL || result == 0 && end_ptr == str)
return 0; // Could not be converted
if (end_ptr < str + strlen(str))
return 0; // Other input in the string after the number
return 1; // All of the string is part of the number
}
Note that you need to remove the newline that fgets most of the time adds to the string before calling this function.
From what I know, strtod can still convert it to 12.3 and the remaining will be stored to the pointer.
That is correct – see its man page:
If endptr is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.
So use that information!
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
bool doable (const char *buf)
{
char *endptr;
errno = 0;
if (!buf || (strtod (buf, &endptr) == 0 && errno))
return 0;
if (*endptr)
return 0;
return 1;
}
int main (void)
{
printf ("doable: %d\n", doable ("12.3"));
printf ("doable: %d\n", doable ("12.3a"));
printf ("doable: %d\n", doable ("abc"));
printf ("doable: %d\n", doable (NULL));
return 0;
}
results in
doable: 1
doable: 0
doable: 0
doable: 0
After accept answer
Using strtod() is the right approach, but it has some challenges
#include <ctype.h>
#include <stdlib.h>
int doubleable3(const char *str) {
if (str == NULL) {
return 0; // Test against NULL if desired.
}
char *end_ptr; // const char *end_ptr here is a problem in C for strtod()
double result = strtod(str, &end_ptr);
if (str == end_ptr) {
return 0; // No conversion
}
// Add this if code should accept trailing white-space like a \n
while (isspace((unsigned char) *endptr)) {
endptr++;
}
if (*end_ptr) {
return 0; // Text after the last converted character
}
// Result overflowed or maybe underflowed
// The underflow case is not defined to set errno - implementation defined.
// So let code accept all underflow cases
if (errno) {
if (fabs(result) == HUGE_VAL) {
return 0; // value too large
}
}
return 1; // Success
}
OP's code
No value with result == 0 in result == 0 && end_ptr == str. Simplify to end_ptr == str.
Instead of if (end_ptr < str + strlen(str)), a simple if (*end_ptr) is sufficient.
if (result == HUGE_VAL ... is a problem for 2 reasons. 1) When result == HUGE_VAL happens in 2 situations: A legitimate conversion and an overflow conversion. Need to test errno to tell the difference. 2) the test should be if (fabs(result) == HUGE_VAL ... to handle negative numbers.