How to compare strings containing numbers just by letters in C - c

I need to compare strings comparing letters and numbers just by letters and appearance of a number in C.
For example strings "first%dsecond%dj" should be considered equal to any string like "first[number]second[number]j".
I tried to do that like this:
char *c_i
int i, j;
if (sscanf(c_i, "first%dsecond%dj", &i, &j) == 2 &&
c_i[strlen(in) - 2] == 'j') {
printf("%s", c_i);
}
but sscanf ignores everything behind the last number so strings like "first%dsecond%dthird%dj" also give the true value and is printed.
Generally sscanf cares just about the values of letters before the last specifier and I would like to find something that counts also the letters after, like say a comparer that checks the whole strings and gets any number but number.
Of course I can do that by creating my own char* scanner, but I would like to avoid this if there is a simpler way.

Use %n to get the length of the conversion, then check that the string really ends there:
void checkString(const char *s)
{
int i, j, n = 0;
if (sscanf(s, "first%dsecond%dj%n", &i, &j, &n) == 2 && s[n] == '\0')
{
printf("%s", s);
}
}

Variation on #unwind's good answer.
Use "%n" to record the current position in the scan and "*" to negate the need for saving to an int.
n ... The corresponding argument shall be a pointer to signed integer into which is to be written the number of characters read from the input stream so far by this call to the fscanf function. ... C11 §7.21.6.2 12
An optional assignment-suppressing character *. §7.21.6.2 3
// return true on match
bool IE_compare(const char *c_i) {
int n = 0;
sscanf(c_i, "first%*dsecond%*dj%n", &n);
return n > 0 && c_i[n] == '\0';
}
Note: The format string can be coded as separate sting literals for alternative reading.
sscanf(c_i, "first" "%*d" "second" "%*d" "j" "%n", &n);
The above will match leading spaces before the number like "first +123second -456j" as "%d" allows that. To avoid:
int n1, n2,
int n = 0;
sscanf(c_i, "first%n%*dsecond%n%*dj%n", &n1, &n2, &n);
return n > 0 && !isspace((unsigned char) c_i[n1]) &&
!isspace((unsigned char) c_i[n2]) && c_i[n] == '\0';
The above will match numbers with a leading sign like "first+123second-456j" as "%d" allows that. To avoid:
sscanf(c_i, "first%n%*dsecond%n%*dj%n", &n1, &n2, &n);
return n > 0 && isdigit((unsigned char) c_i[n1]) &&
isdigit((unsigned char) c_i[n2]) && c_i[n] == '\0';
// or
int n = 0;
sscanf(c_i, "first%*[0-9]second%*[0-9]j%n", &n);
return n > 0 && c_i[n] == '\0';
What remains somewhat unclear is if the occurrence of a number is required. "...and appearance of a number" implies yes. Should "firstsecondj" match? The above answers assume no.

Related

Shifting characters in string

Hi I want to write a program which takes a string and a number as input and then shifts the elements of string by given number, and also the string is always capital letters and the output should only be capital letters too
example 1 : -1 , "AB CD"
output 1 : "ZA BC"
example 2 : +3 , "ABC"
output 2 : "DEF"
I have written this code but if there is a space in my string , the program doesnt work properly
for example : -1 "AA AA"
the output is : "ZZ" But i was expecting "ZZ ZZ"
int main()
{
char str[100] = {NULL},y;
int n,x,i=0;
scanf_s("%d", &n);
scanf_s("%s", str);
while (str[i] != NULL) {
x = str[i];
if (x + n >= 65 && x + n <= 90) {
y = x + n;
str[i] = y;
}
else if (x + n < 65) {
while (x + n < 65) {
x += 26;
}
y = x + n;
str[i] = y;
}
else if (x + n > 90) {
while (x + n > 90) {
x -= 26;
}
y = x + n;
str[i] = y;
}
i++;
}
printf("%s", str);
return 0;
}
After reading non-whitespace, the scanf specifier %s stops when it encounters whitespace.
Instead of "%s", use " %99[^\n]" to:
read and ignore leading whitespace, then
read up to 99 characters, or until a newline character is encountered.
Reading at most 99 characters is important, as you must leave room in your buffer for the null-terminating byte, and not limiting the amount of data read can easily lead to buffer overflows.
The return value of scanf_s should be checked to ensure the expected number of conversions took place, before any data is used.
if (1 != scanf_s("%d", &n))
/* error! */;
Note that scanf_s requires a secondary argument when using %s or %[, which is the size of the buffer
char buf[10];
if (1 != scanf_s("%9s", buf, (rsize_t) sizeof buf))
/* error! */;
which is supposed to be of type rsize_t. If you are using the MSVC versions of these functions, this type is unsigned.
Due to the pitfalls involved with using scanf properly, and the portability issues of scanf_s, consider instead using fgets to read lines of input (and strtol to parse an integer).
For clarity, consider using character constants like 'A' and 'Z', instead of integer constants like 65 and 90.
As is, your current code does not account for non-alphabetic characters, and will shift those. Consider the use of isalpha or isupper to filter the application of the shift.
Note NULL is usually considered a pointer value. Use '\0', or just 0, to represent the null-terminating byte.

I wrote a program which should terminate when 'n' or 'N' is entered but it doesn't work

It does not terminate when 'n' or 'N' is entered. How do i make it work?
Also, why will the "\n\nGive n or N to terminate loop" statement not work if I don't add flush(stdin)?
#include <stdio.h>
void main() {
int i, n, sump, sumn;
char ch;
sump = 0;
sumn = 0;
do
{
printf("\nGive integer n ");
scanf("%d", &n);
if (n > 0)
sump = sump + n;
else
sumn = sumn + n;
printf("\n\nGive n or N to terminate loop");
fflush(stdin);
scanf("%c", &ch);
} while ((ch != 'n') || (ch != 'N'));
printf("Sum of positive number=%d", sump);
printf("\nSum of negative number=%d", sumn);
}
When you press the Enter key for the numeric input (for the n variable) then that's added as a newline '\n'.
This newline is still in the input buffer of stdin when you read the character for ch.
And unlike many format which skips white-space (like newline) the %c format reads all characters, including that newline.
The #obvious" solution is simple: To ask scanf to skip any leading space, by adding a single space in the format string:
scanf(" %c", &ch);
// ^
// Note space here
And as mentioned in a comment by Stefan Riedel, the condition is incorrect as well. The negation of ch == 'n' || ch == 'N' is not ch != 'n' || ch != 'N'. Instead De Morgan's laws dictate that the connecting logical operation must change as well:
ch != 'n' && ch != 'N'
Or you could simplify it:
tolower(ch) != 'n'
There's another way to do this:
#include <stdio.h>
#include <stdlib.h>
int main() {
char inp[100];
int n, sump = 0, sumn = 0;
while(1)
{
printf("Give integer, or \"n\" to stop: ");
if(fgets(inp, sizeof(inp), stdin) == NULL) break;
if(inp[0] == 'n' || inp[0] == 'N') break;
n = atoi(inp);
if (n > 0)
sump = sump + n;
else
sumn = sumn + n;
}
printf("Sum of positive number=%d\n", sump);
printf("Sum of negative number=%d\n", sumn);
}
In this program, there's not a separate prompt for the "n".
You can type a number, or type "n".
This is much more convenient for the person entering the numbers.
But it means you can't use scanf to read the input, because there's no way to ask scanf to, "read a number, or maybe the string 'n'".
But it's actually fine to not use scanf for input, because it turns out scanf is actually pretty terrible for user input.
This program reads a whole line of input, as text, using fgets.
Then it looks at the first character of the line to see if it's 'n' or 'N'.
Then, if it wasn't an N, it calls atoi to convert the string into an integer.
There are better ways to convert strings to integers -- that is, atoi has its problems, so it's recommended to use the more sophisticated strtod in "real" programs, but atoi is nice and simple, and sort of an intermediate step here.

Array with stopping condition

The task is: a user types an char array and the programm stops when the last two values make a match with the first and second inserted values, then it prints only inserted int values.
For example, I type: 1,2,f,5,2,g,s,d,c,3,1,2
And get 1,2,5,3
This what I've got for now
int main()
{
setlocale(LC_ALL, "RUS");
char* A;
int i = 2, N;
//making an array
A = (char*)malloc(2 * sizeof(char));
printf("Enter an array \n");
//entering the first value
scanf_s("%c", &A[0]);
//second value
scanf_s("%c", &A[1]);
//next values
while (!(A[i - 1] == A[0] && A[i] == A[1]))
{
i++;
A = (char*)realloc(A, (i + 1)*sizeof(char));
scanf_s("%c", &A[i]);
}
system("pause");
return 0;
}
So now it makes a stop only if the first value makes a match and prints nothing. I am really confused
You can avoid dynamically allocating altogether and just focus on the logic of your task. While you can use scanf for reading character input, you are better served using a character-oriented input function such as getchar.
That said, it appears you want to read characters from stdin, only storing unique digits in your array, and then if the user enters digits that match your first two elements stored in your array, print the values stored in the array and exit. (If I have any of that wrong, please let me know in a comment)
First off, reading characters with scanf can be a bit finicky depending on the values separating the character. However since you are only concerned with storing digits, that makes things a bit easier.
To avoid dealing with malloc, just set some reasonable limit to size your array and check your stored elements against. A simple #define is all you need. For example:
#define MAXE 128
Will define a constant MAXE (for max elements), to test against as you fill your array. Just keep a count of the elements you add to the array, and if you reach your limit, exit.
You want to keep reading characters until one of two conditions are met: (1) you have added 128 values to your array with no exit condition tripped, or (2) the last two characters entered match the digits store in a[0] and a[1]. To set up your read you can do something like:
while (n < MAXE && scanf ("%c", &c) == 1) { ...
note: with getchar() you can avoid the non-portable _s function issue, among other pitfalls with the scanf family of functions. The changes are minimal to use getchar() instead of declaring c as type char, declare c as type int, and then change your assignment to c as follows:
while (n < MAXE && (c = getchar()) != EOF) {
After reading a character, (regardless of how), you want to test whether it is a digit, if not you are not storing it and it can't be part of your exit condition. So you can simply get the next char if it isn't a digit:
if (c < '0' || '9' < c)
continue;
(note: ctype.h provides the isdigit() function that can be used instead of the manual checks)
You want to store the first two digit regardless, and following those two, you want to store any new digits entered (not already stored), so you need to test the current digit against all digits previously stored to insure it is a unique digit. While you can code the logic several ways, in this case your test loop must test all values stored before making a decision to store the current digit. In this situation, and in situations where you need to break control within nested loop, the lowly goto statement is your best friend. Here if the digit is a duplicate, the goto simply skips to the dupes label passing over the assignment:
if (n > 1)
for (i = 0; i < n; i++)
if (c == a[i])
goto dupe;
a[n++] = c;
dupe:;
The last part of the puzzle is your exit condition. This is a bit tricky (but simply solved) because you know you will not store the preceding (or for Leffler, the penultimate) value in the array to test against (it being non-unique to the array). The trick is just to save the character from the last iteration to test against. (maybe in a variable called last). Now you can code your exit clause:
if (n > 2 && a[0] == last && a[1] == c)
break;
Putting it all together, you could do something like the following:
#include <stdio.h>
#define MAXE 128
int main (void) {
char a[MAXE] = "", c, last = 'a';
int i, n = 0;
printf ("Enter an array\n");
while (n < MAXE && scanf ("%c", &c) == 1) {
if (c < '0' || '9' < c)
continue;
if (n > 1)
for (i = 0; i < n; i++)
if (c == a[i])
goto dupe;
a[n++] = c;
dupe:;
if (n > 2 && a[0] == last && a[1] == c)
break;
last = c;
}
if (n < 3) {
fprintf (stderr, "error: minimum of 3 values required.\n");
return 1;
}
if (n == MAXE) {
fprintf (stderr, "warning: limit of values reached.\n");
return 1;
}
printf ("Values in array: ");
for (i = 0; i < n; i++)
putchar (a[i]);
putchar ('\n');
return 0;
}
How you handle the printing and error conditions are up to you. Those included above are just a thought on how you could cover your bases.
Note: gcc does not implement the optional _s functions, so you can make the change back to scanf_s if you need to.
Example Use/Output
$ ./bin/exitonmatch
Enter an array
1
2
f
5
2
g
s
d
c
3
1
2
Values in array: 1253
Look it over and let me know if you have any questions.
Accept Any Char as Termination/Store only Unique Digits
If the logic as you have explained, is to track the first two characters, regardless of whether they are digits and allow any character to serve as the termination check of first two entered sequence, the easiest way to handle that is to simply store the first two characters entered in a separate array (or two variables) and check each sequence of characters entered against them.
Adding this type check takes no more than a slight rearranging of the conditions in the original to allow a few checks on any character entered before considering only digits for storage in your array.
note: the cc (character count) variable was added to track the number of characters entered and the first array was added to hold the first two characters entered.
For example:
char a[MAXE] = "", first[TSTA] = "", last = 'a';
int c, cc = 0, i, n = 0;
printf ("Enter an array\n");
while (n < MAXE && (c = getchar()) != EOF) {
if (c < ' ') continue; /* skip non-print chars */
if (cc < 2) /* fill first[0] & [1] */
first[cc] = c; /* check term condition */
if (++cc > 2 && first[0] == last && first[1] == c)
break;
last = c; /* set last */
if (c < '0' || '9' < c) /* store only numbers */
continue;
if (n > 1) /* skip duplicates */
for (i = 0; i < n; i++)
if (c == a[i])
goto dupe;
a[n++] = c; /* digit and not a dupe - store it */
dupe:;
}
note: getchar is used above, but you can substitute scanf from the first example if you like.
Example Use/Output
Here there first two characters are a and b which serve as the termination sequence despite not being digits.
$ ./bin/exitonmatchgc
Enter an array
a
b
c
4
5
g
h
4
9
3
b
a
b
Values in array: 4593

C programming - sscanf for tkens separated by space

I'm currently trying writing a program using C (very new to C - only been learning it for 2 weeks), and I wanted to get a string of input from the user by stdin, in which the string has a char, followed by 2 floats (each has space in between). Example would be: "y 2.1 1.1".
My question is how can I obtain and store the 3 inputs, while making sure the first is a char, and the following two inputs are floats?
Stick with sscanf(), but don't forget to check its return value (look here). What really happens for input "y 1u 1" is that sscanf will read and store the char, which is valid, then it will read and store the int 1, which is valid, and then stop, because "u" does not match the format string.
Below is example code (using scanf() rather then fgets() and sscanf()).
char in1;
int in2,in3;
int retval;
/*
char array[100] = {'\0'};
fgets(array, 100, stdin);
retval = sscanf(array, "%c %d %d", &in1, &in2, &in3);
*/
retval = scanf("%c %d %d", &in1, &in2, &in3);
printf("Scanned %d items\n", retval);
printf("Here they come: ");
if(retval > 0) {
printf("%c ", in1);
}
if(retval > 1) {
printf("%d ", in2);
}
if(retval > 2) {
printf("%d", in3);
}
putchar('\n');
How can I obtain and store the 3 inputs, while making sure the first is a char, and the following two inputs are ints?
problem with this code is that there are extra spaces at the very end, and I don't know how to get rid of it.
A simple way to use sscanf() and check if there is extra anything after the scanned variable is to use "%n" to record the location of the scan at that point.
char in1;
int in2, in3;
int n = 0;
sscanf(array,"%c %d %d%n", &in1, &in2, &in3, &n);
if (n > 0 && (array[n] == '\n' || array[n] == '\0')) {
Success(in1, in2, in3);
}
It is always important to check the results of sscanf(). One way is to check its return value which should be 3 here. Unfortunately that does not tell us if anything exist after in3. By setting n == 0 and then testing n > 0, code knows that scanning proceeded all the way successfully to "%n". Code can also test what character the scanning stopped at.

What's the better way to check if input is string?

I'm making a program in which i ask for the username name, and i'd like to only accept strings with valid characters only (alphabetic).
I found that i can either use
do{
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n);
}while(1);
or
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name; i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
But i'm not sure if any of those work, and what would be better to check if there isn't anything except alphabetic letters.
Also though about making all input letters (after this verification) on lower case and make the first letter upper case with
//all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
Would this work?
if(scanf("%s", &name) reads in all non-white-space, not just letters, into name and does not return if input is only "\n".
if(isalpha(name[i]) == 0){ loop is not bad, but scanf("%s", &name) still does not return if input is only "\n" or just white-space.
for(i=1; i<strlen(name); i++) name[i] = tolower(name[i]) works to make all following letters lower case, but if inefficient as code repeatedly calculates the string length.
Separate reading data and parsing data. Use fgets() to read the data and various code to test the data for correctness.
char buf[200];
fgets(buf, sizeof buf, stdin);
int n = 0;
// Skip leading white-space
// Look for A-Z, a-z or space (like a space between first & last)
// Skip white-space like \n
// Save into 'n' the current scan position
sscanf(buf, " %*[A-Za-z ] %n", &n);
if (n > 0 && buf[n] == '\0') Success(); // #user3121023
Should code need to rid buf of a potential trailing "\n", suggest:
buf[strcspn(buf, "\n")] = 0;
Let's look at each option.
First option:
do {
//since scanf returns the number of currectly input
if(scanf("%s", &name) == 1)
break;
else printf("Please enter a valid name.\n");
} while(1);
This won't quite work the way you expect. First off, what exactly is name? I'm almost sure that you want scanf("%s", name) instead (name instead of &name), unless you declared it as char name;, which would be catastrophic anyway.
Anyway, the problem I see with this approach is that you don't really validate the string. Read the man page section about %s:
s - Matches a sequence of non-white-space characters; the next pointer
must be a pointer to character array that is long enough to hold
the input sequence and the terminating null byte ('\0'), which is
added automatically. The input string stops at white space or at the
maximum field width, whichever occurs first.
Nothing says that the string is composed of alphabetic characters only.
Second option:
do{
check = 0;
scanf("%s", &name);
for(i=0; i<strlen(name); i++){
//since isalpha() returns != 0 if it's a letter
if(isalpha(name[i]) == 0){
printf("Invalid character. Please enter a valid name.\n");
check = 1;
break;
}
}
}while(check == 1);
Again, you probably want name rather than &name. You also shouldn't be calling strlen() in the for loop condition, because it's inefficient (strlen() is O(n)). A smart compiler may optimize it away, but it's hard for the compiler to know when it is safe to do so. Just call strlen() before the loop and store the result in a variable.
isalpha() expects an integer as an argument, which is expected to be either EOF or an unsigned char converted to int. Again, you don't show the declaration for name, but assuming that it is a character array, you should cast name[i] to unsigned char before calling isalpha(), so that you don't get any sign extension surprises:
if (isalpha((unsigned char) name[i]) == 0) { /* ... */ }
In fact, gcc nowadays will most likely give you a warning if you call any of the ctype family macros / functions with a plain char. The macros are deliberately written in such a way that a warning is shown, precisely because this is a common mistake. It is implementation-defined whether a plain char is signed or unsigned. You would get problems in a platform with signed chars because of sign extension (this is because typically, things like isalpha() are implemented using lookup tables, and extending the sign yields a negative number that would index the lookup table with a negative index - Oops!)
Other than this, this approach seems ok to me.
A third, maybe better option:
Since you mentioned fgets(), I think you could do this easily by combining fgets() with sscanf(). First, you read a line with fgets(). Then, you use sscanf() to match a string consisting of only characters in the range [a-zA-Z]. This can be done with the format specifier %[a-zA-Z]s. Then, you just have to check if this matched the entire line. Here's a working program:
#include <stdio.h>
#include <string.h>
int main(void) {
static char buf[512];
static char name[512];
int is_valid = 0;
while (!is_valid) {
fgets(buf, sizeof(buf), stdin);
size_t line_len = strlen(buf);
if (line_len > 0 && buf[line_len-1] == '\n') {
buf[line_len-1] = '\0';
line_len--;
}
int n = 0;
if (sscanf(buf, " %[a-zA-Z] %n", name, &n) == 1 && buf[n] == '\0') {
is_valid = 1;
} else {
printf("Please enter a valid name.\n");
}
}
printf("Name: %s\n", buf);
return 0;
}
Make sure your buffers are large enough; this code is vulnerable to buffer overflow for arbitrarily long names / lines.
Now let's see the code to make the first letter upper case:
//all to lower except the first letter
for(i=1; i<strlen(name); i++){
name[i] = tolower(name[i]);
}
//first letter to upper
name[0] = toupper(name[i]);
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
Again, remove strlen() from the loop condition. toupper() and tolower() also expect an int as an argument representing either EOF or an unsigned char converted to int. You should cast it to unsigned char to avoid problems with possible sign extension, as I said earlier with the other example.
This is wrong:
//first letter to upper
name[0] = toupper(name[i]);
It should be:
//first letter to upper
name[0] = toupper(name[0]);
(The argument to toupper() is name[0], not name[i]).
Finally, this is useless:
x=1;
while(name[x] != '\0'){
//if the letter before is a white space, even the first letter, it should place the first letter of a name upper
if(name[x-1] == ' ')
name[x] = toupper(name[x]);
x++;
}
%s will never give you a string with whitespaces (refer to the manpage quote I pasted above).
Assuming that you want your name to have only characters a through z or A through Z then you could use this function
//Returns 1 if non alphabetic character is found, 0 otherwise
int NonAlphaCharsFound(char *name)
{
int FoundNonChar = 0;
int i, nameLength;
nameLength = strlen(name);
for(i = 0; i < nameLength; i++)
{
if((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || name[i] == ' ')
{
//do nothing if it's an alphabect character
//name[i] == ' ' is to allow for spaces if you want spaces in the name
}
else
{
FoundNonChar = 1;
break;
}
}
return FoundNonChar;
}

Resources