Im using sscanf to read/parse a 3 char string into an int as:
char strId[4] = "123";
int i;
int count = sscanf(strId, "%i" &i);
Im testing count==1 to check if the parse suceeds or fails.
"123" suceeds correctly - I want to consider this a number.
"aaa" fails correctly - I don't want to consider this a number.
but
"2aa" suceeds (count==1, i==2)- but I would like to identify this as a failure as I don't want to consider this a number.
How to simply parse strId to meet the above conditions?
Use strtol(3). It allows specification of an "end-of-parsing" pointer (endptr below). After the conversion has finished you can check wether or not it points at the end of string. If it doesn't, there were non-number characters left in the input.
long strtol(const char *restrict str, char **restrict endptr, int base);
From man strtol:
If endptr is not NULL, strtol() stores the address of the first
invalid character in *endptr. If there were no digits at all,
however, strtol() stores the original value of str in *endptr. (Thus,
if *str is not '\0' but **endptr is '\0' on return, the entire string
was valid.)
A sscanf() approach, use "%n".
"%n" saves the location where scanning stopped.
char strId[4] = "123";
int n = 0
sscanf(strId, "%i %n" &i, &n);
if (n == 0 || strId[n] != '\0') Handle_ProblemInput(strId);
This will pass: "123", " 123", "123 ".
And fail: "123a", "", " abc".
May/may not detect int overflow "12345678901234567890".
[Edit]
What is nice about the "%n" is that it can be used with complicated formats and with formats that otherwise end with fixed text. IOWs, simply detect if the scan made it all the way to '\0'?
int n = 0;
sscanf(buf, " %i %f %7s ,,, %c blah foo %n", ...);
if (n == 0 || buf[n] != '\0') Handle_ProblemInput(buf);
With strtol that's an easy task just do this
#include <stdio.h>
#include <stdlib.h>
char string[] = "123xyz";
char *endptr;
int value;
value = strtol(string, &endptr, 10);
if ((*string != '\0') && (*endptr == '\0'))
printf("%s is a number and has no unconvertible characters", string);
/* and now 'value' contains the integer value. */
If you want to use sscanf() then this should do it too
#include <stdio.h>
#include <string.h>
char string[] = "123";
int count;
int value;
int length;
length = strlen(string);
if ((sscanf(string, "%d%n", &value, &count) == 1) && (count == length))
printf("%s is a number and has no unconvertible characters", string);
/* and now 'value' contains the integer value. */
char str[255];
int count = sscanf((const char*)strId, "%i%254s", &i, str);
if it comes back with count == 1 it is okay?
Related
Let's say I have the following string stored in char *m;
char *m = "K: someword\r\n";
The m will be inputed by the user so the user will write in the console:
K: someword\r\n
The someword can have different length, while K: \r\n will always be the same.
Now my question is, which is the best way after I read this input to extract someword from it and save it into a new char* variable?
Use sscanf() like this:
#include <stdio.h>
int main (void)
{
char buffer [50], k, return_car, new_line;
int n = sscanf ("K: someword\r\n", "%c: %s%c%c", &k, buffer, &return_car, &new_line);
printf ("The word is \"%s\". sscanf() read %d items.\n", buffer, n);
return 0;
}
Output:
The word is "someword". sscanf() read 4 items
Since both the substrings we aren't interested in ("K: " and "\r\n") are of fixed length, you can do this:
char *s;
size_t len = strlen(m);
s = malloc(len);
strcpy(s, m + 3);
s[len - 4] = 0;
printf("%s\n", s);
free(s);
Note that I declared a new char * variable to copy to since m is in read-only memory, and that robust code would handle the case where malloc failed and returned NULL.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char *m = "K: someword\r\n";
const size_t someword_len = strlen(&m[3]);
char *someword = malloc(someword_len);
if (someword == NULL) { fprintf(stderr, "Malloc error\n"); abort(); }
memcpy(someword, &m[3], someword_len - 2);
someword[someword_len - 1] = '\0';
puts(someword);
free(someword);
}
You assume that string m always starts with "K: " (that's 3 characters) and ends with "\r\n" (that's two characters).
I believe strlen(m) will be faster then strchr(m, '\r') or strrchr(m, '\r') on most platforms
After you have the length of the string, using memcpy instead of strcpy will be faster.
Remember to null terminate your string
Remember to handle errors.
I'm triying to read a string with a specific format in C using scanf()
The string has the format:
<LET,LET,LET> op
where LET is a capital letter and op has to be '+' or '%'.
These are valid entries:
<A,B,C> +
<A,W,Z> %
<Q, X,W> +
These are not:
<A,b,C> +
<A,W,Zddddd> %
<Q,X,W> *
I'm trying something like this
#include <stdio.h>
int ret = 0;
char str[8];
ret = scanf("%8[^\n]",str);
but str ends up with garbage. I just don't know how to read it and how to get only capital letters.
Thanks
try this:
#include <stdio.h>
int main(void) {
char let1[2], let2[2], let3[2], op[2];
char line[80];
while(fgets(line, sizeof line, stdin)){
if(sscanf(line, "<%1[A-Z], %1[A-Z], %1[A-Z]> %1[+%]", let1, let2, let3, op) == 4)
puts("valid");
else
puts("invalid");
}
return 0;
}
This method also detects trail garbage on the line.
"%n" saves the offset of the current scan. Only if n is non-zero and indexes '\0' was there success.
By using string literal concatenation, code can clearly show what format is being used for the various parts of the scan.
#include <stdio.h>
#define F_LET " %1[A-Z]"
#define F_SEP " ,"
#define F_OP " %1[+%]"
int main(void) {
char buf[100];
while (fgets(buf, sizeof buf, stdin)) {
char let[3][2];
char op[2];
int n = 0;
sscanf(buf, " <" F_LET F_SEP F_LET F_SEP F_LET " >" F_OP " %n",
let[0], let[1], let[2], op, &n);
puts((n && buf[n] == '\0') ? "Success" : "Fail");
}
}
An alternative to BLUEPIXY's perfectly good answer, is to simply use char, and scan the input using %c instead, e.g.:
#include <stdio.h>
#include <ctype.h>
int main()
{
char let1, let2, let3, op;
char line[80]; // or some other suitable size
while (fgets(line, sizeof line, stdin))
{
if (sscanf(line, "< %c , %c , %c > %c", &let1, &let2, &let3, &op) == 4 && (op == '+' || op == '%') && isupper(let1) && isupper(let2) && isupper(let3))
{
printf("valid!\nlet1 = %c\nlet2 = %c\nlet3 = %c\nop = %c\n", let1, let2, let3, op);
}
else
{
puts("invalid");
}
}
}
Note that the spaces before and after the %c are important as they allow sscanf to gobble up all whitespace before trying to match the %c or the comma.
Unlike BLUEPIXY's answer however, we have to check that op matches + or % manually.
EDIT: I missed the part where the LET characters must be uppercase. I've fixed the answer, but of course, it is now more complicated.
I would like to understand how to validate a string input and check whether the entered string is Numeric or not? I belive isdigit() function is the right way to do it but i'm able to try it out with one char but when it comes to a string the function isn't helping me.This is what i have got so far,Could any please guide me to validate a full string like
char *Var1 ="12345" and char *var2 ="abcd"
#include <stdio.h>
#include <ctype.h>
int main()
{
char *var1 = "hello";
char *var2 = "12345";
if( isdigit(var1) )
{
printf("var1 = |%s| is a digit\n", var1 );
}
else
{
printf("var1 = |%s| is not a digit\n", var1 );
}
if( isdigit(var2) )
{
printf("var2 = |%s| is a digit\n", var2 );
}
else
{
printf("var2 = |%s| is not a digit\n", var2 );
}
return(0);
}
The program seems to be working fine when the variables are declared and initialized as below,
int var1 = 'h';
int var2 = '2';
But i would like to understand how to validate a full string like *var =" 12345";
Try to make a loop on each string and verify each char alone
isdigit takes a single char, not a char*. If you want to use isdigit, add a loop to do the checking. Since you are planning to use it in several places, make it into a function, like this:
int all_digits(const char* str) {
while (*str) {
if (!isdigit(*str++)) {
return 0;
}
}
return 1;
}
The loop above will end when null terminator of the string is reached without hitting the return statement in the middle, in other words, when all characters have passed the isdigit test.
Note that passing all_digits does not mean that the string represents a value of any supported numeric type, because the length of the string is not taken into account. Therefore, a very long string of digits would return true for all_digits, but if you try converting it to int or long long you would get an overflow.
Use this
int isNumber(const char *const text)
{
char *endptr;
if (text == NULL)
return 0;
strtol(text, &endptr, 10);
return (*endptr == '\0');
}
then
if (isNumeric(var1) == 0)
printf("%s is NOT a number\n", var1);
else
printf("%s is number\n", var1);
the strtol() function will ignore leading whitspace characters.
If a character that cannot be converted is found, the convertion stops, and endptr will point to that character after return, thus checking for *endptr == '\0' will tell you if you are at the end of the string, meaning that all characters where successfuly converted.
If you want to consider leading whitespaces as invalid characters too, then you could just write this instead
int isNumber(const char *text)
{
char *endptr;
if (text == NULL)
return 0;
while ((*text != '\0') && (isspace(*text) != 0))
text++;
if (*text == '\0')
return 0;
strtol(text, &endptr, 10);
return (*endptr == '\0');
}
depending on what you need, but skipping leading whitespace characters is to interpret the numbers as if a human is reading them, since humans "don't see" whitespace characters.
I'm new to C language and I need a help on String functions.
I have a string variable called mcname upon which I would like to compare the characters between special characters.
For example:
*mcname="G2-99-77"
I expect the output to be 99 as this is between the - characters.
How can I do this in C please?
Travel the string (walking pointer) till u hit a special character.
Then start copying the characters into seperate array untill u hit the next special character (Place a null character when u encounter the special character second time)
You can do this by using strtok or sscanf
using sscanf:
#include <stdio.h>
int main()
{
char str[64];
int out;
char mcname[] = "G2-99-77";
sscanf(mcname, "%[^-]-%d", str, &out);
printf("%d\n", out);
return 0;
}
Using strtok:
#include <stdio.h>
#include <string.h>
int main()
{
char *str;
int out;
char mcname[] = "G2-99-77";
str = strtok(mcname, "-");
str = strtok (NULL, "-");
out = atoi(str);
printf("%d\n", out);
return 0;
}
sscanf() has great flexibility. Used correctly, code may readily parse a string.
Be sure to test the sscanf() return value.
%2[A-Z0-9] means to scan up to 2 characters from the set 'A' to 'Z' and '0' to '9'.
Use %2[^-] if code goal is any 2 char other than '-'.
char *mcname = "G2-99-77";
char prefix[3];
char middle[3];
char suffix[3];
int cnt = sscanf(mcname, "%2[A-Z0-9]-%2[A-Z0-9]-%2[A-Z0-9]", prefix, middle,
suffix);
if (cnt != 3) {
puts("Parse Error\n");
}
else {
printf("Prefix:<%s> Middle:<%s> Suffix:<%s>\n", prefix, middle, suffix);
}
I have a file like this:
...
words 13
more words 21
even more words 4
...
(General format is a string of non-digits, then a space, then any number of digits and a newline)
and I'd like to parse every line, putting the words into one field of the structure, and the number into the other. Right now I am using an ugly hack of reading the line while the chars are not numbers, then reading the rest. I believe there's a clearer way.
Edit: You can use pNum-buf to get the length of the alphabetical part of the string, and use strncpy() to copy that into another buffer. Be sure to add a '\0' to the end of the destination buffer. I would insert this code before the pNum++.
int len = pNum-buf;
strncpy(newBuf, buf, len-1);
newBuf[len] = '\0';
You could read the entire line into a buffer and then use:
char *pNum;
if (pNum = strrchr(buf, ' ')) {
pNum++;
}
to get a pointer to the number field.
fscanf(file, "%s %d", word, &value);
This gets the values directly into a string and an integer, and copes with variations in whitespace and numerical formats, etc.
Edit
Ooops, I forgot that you had spaces between the words.
In that case, I'd do the following. (Note that it truncates the original text in 'line')
// Scan to find the last space in the line
char *p = line;
char *lastSpace = null;
while(*p != '\0')
{
if (*p == ' ')
lastSpace = p;
p++;
}
if (lastSpace == null)
return("parse error");
// Replace the last space in the line with a NUL
*lastSpace = '\0';
// Advance past the NUL to the first character of the number field
lastSpace++;
char *word = text;
int number = atoi(lastSpace);
You can solve this using stdlib functions, but the above is likely to be more efficient as you're only searching for the characters you are interested in.
Given the description, I think I'd use a variant of this (now tested) C99 code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
struct word_number
{
char word[128];
long number;
};
int read_word_number(FILE *fp, struct word_number *wnp)
{
char buffer[140];
if (fgets(buffer, sizeof(buffer), fp) == 0)
return EOF;
size_t len = strlen(buffer);
if (buffer[len-1] != '\n') // Error if line too long to fit
return EOF;
buffer[--len] = '\0';
char *num = &buffer[len-1];
while (num > buffer && !isspace((unsigned char)*num))
num--;
if (num == buffer) // No space in input data
return EOF;
char *end;
wnp->number = strtol(num+1, &end, 0);
if (*end != '\0') // Invalid number as last word on line
return EOF;
*num = '\0';
if (num - buffer >= sizeof(wnp->word)) // Non-number part too long
return EOF;
memcpy(wnp->word, buffer, num - buffer);
return(0);
}
int main(void)
{
struct word_number wn;
while (read_word_number(stdin, &wn) != EOF)
printf("Word <<%s>> Number %ld\n", wn.word, wn.number);
return(0);
}
You could improve the error reporting by returning different values for different problems.
You could make it work with dynamically allocated memory for the word portion of the lines.
You could make it work with longer lines than I allow.
You could scan backwards over digits instead of non-spaces - but this allows the user to write "abc 0x123" and the hex value is handled correctly.
You might prefer to ensure there are no digits in the word part; this code does not care.
You could try using strtok() to tokenize each line, and then check whether each token is a number or a word (a fairly trivial check once you have the token string - just look at the first character of the token).
Assuming that the number is immediately followed by '\n'.
you can read each line to chars buffer, use sscanf("%d") on the entire line to get the number, and then calculate the number of chars that this number takes at the end of the text string.
Depending on how complex your strings become you may want to use the PCRE library. At least that way you can compile a perl'ish regular expression to split your lines. It may be overkill though.
Given the description, here's what I'd do: read each line as a single string using fgets() (making sure the target buffer is large enough), then split the line using strtok(). To determine if each token is a word or a number, I'd use strtol() to attempt the conversion and check the error condition. Example:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* Read the next line from the file, splitting the tokens into
* multiple strings and a single integer. Assumes input lines
* never exceed MAX_LINE_LENGTH and each individual string never
* exceeds MAX_STR_SIZE. Otherwise things get a little more
* interesting. Also assumes that the integer is the last
* thing on each line.
*/
int getNextLine(FILE *in, char (*strs)[MAX_STR_SIZE], int *numStrings, int *value)
{
char buffer[MAX_LINE_LENGTH];
int rval = 1;
if (fgets(buffer, buffer, sizeof buffer))
{
char *token = strtok(buffer, " ");
*numStrings = 0;
while (token)
{
char *chk;
*value = (int) strtol(token, &chk, 10);
if (*chk != 0 && *chk != '\n')
{
strcpy(strs[(*numStrings)++], token);
}
token = strtok(NULL, " ");
}
}
else
{
/**
* fgets() hit either EOF or error; either way return 0
*/
rval = 0;
}
return rval;
}
/**
* sample main
*/
int main(void)
{
FILE *input;
char strings[MAX_NUM_STRINGS][MAX_STRING_LENGTH];
int numStrings;
int value;
input = fopen("datafile.txt", "r");
if (input)
{
while (getNextLine(input, &strings, &numStrings, &value))
{
/**
* Do something with strings and value here
*/
}
fclose(input);
}
return 0;
}