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?
Related
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/
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;
}
I'm writing a program to check whether a string is palindrome or not using recursion.Palindrome string is the one that can be read backwards just the same as reading it forward. However following is my code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num;
printf("Enter the number of characters in your string\n");
scanf("%d",&num);
char string[num];
char string2[num];
int i;
printf("Enter your string\n");
for (i=0;i<num;i++)
{
scanf("%c",&string[i]);
}
fillString(string,string2,num);
int palin = isPalind(string,string2,num);
if (palin) printf("The string you entered is palindrome");
else printf("The string you entered is not palindrome");
return 0;
}
int isPalind(char string[],char string2[],int num)
{
int i=0;
while (i<num)
{
if (string[i]!=string2[i])
{
i++;
return 0;
}
else{
i++;
return 1*isPalind(string,string2,num);
}
}
}
void fillString(char string[],char string2[],int num)
{
int i;
for (i=1;i<num;i++)
string2[i-1]=string[num-i];
}
I have a logical error, the program compiles fine and executes but it always gives out "The string is not palindrome"
In the fillString the loop is iterating num-1 times (i is from 1 to num-1), so not the whole string is copied. The first character of the original string is omitted. You should do
for (i=1;i<=num;i++) ...
As for the recursive function, it is not really recursive. In recursive call modified input should be passed, but in your case exactly the same input is passed. So in case of true palindrome, it's likely you will get stack overflow due to non-termination. I would propose another approach, to work with single string recursively:
1) Base case: String is a palindrome if of length 0 or 1
2) Recursion step: String is a palindrome if the first character equals to the last and the string without first and last characters is palindrome.
Is your fillString() function reversing your string as expected? It looks like the first letter of string1 is not getting added into the last position of string2 because the for loop stops when i < num fails. Try switching it to i <= num and seeing if that helps.
Double-check this example:
Given: String1 = "Hello". String2 = null for now. num = 5.
void fillString(char string[],char string2[],int num)
{
int i;
for (i=1;i<num;i++)
string2[i-1]=string[num-i];
}
When i = 4, you have string2 = 'olle'. When i = 5, the for loop condition fails, so it doesn't populate string2[4] = 'H'.
updated:
void fillString(char string[],char string2[],int num)
{
int i;
for (i=1;i<=num;i++)
string2[i-1]=string[num-i];
}
The both functions are wrong. They can be written the following way
int isPalind( const char string[], const char string2[], int num )
{
return ( num == 0 ) ||
( string[0] == string[--num] && isPalind( string + 1, string2, num ) );
}
void fillString( const char string[], char string2[], int num )
{
int i;
for ( i = 0; i < num; i++ ) string2[i] = string[num-i-1];
}
If you need not necessary a recursive function then you could use simply standard function memcmp that to determine whether two strings are equal.