sscanf doesn't move, scanning same integer everytime - c

I have a string that has ints and I'm trying to get all the ints into another array. When sscanf fails to find an int I want the loop to stop. So, I did the following:
int i;
int getout = 0;
for (i = 0; i < bsize && !getout; i++) {
if (!sscanf(startbuffer, "%d", &startarray[i])) {
getout = 1;
}
}
//startbuffer is a string, startarray is an int array.
This results in having all the elements of startarray to be the first char in startbuffer.
sscanf works fine but it doesn't move onto the next int it just stays at the first position.
Any idea what's wrong? Thanks.

The same string pointer is passed each time you call sscanf. If it were to "move" the input, it would have to move all the bytes of the string each time which would be slow for long strings. Furthermore, it would be moving the bytes that weren't scanned.
Instead, you need to implement this yourself by querying it for the number of bytes consumed and the number of values read. Use that information to adjust the pointers yourself.
int nums_now, bytes_now;
int bytes_consumed = 0, nums_read = 0;
while ( ( nums_now =
sscanf( string + bytes_consumed, "%d%n", arr + nums_read, & bytes_now )
) > 0 ) {
bytes_consumed += bytes_now;
nums_read += nums_now;
}

Convert the string to a stream, then you can use fscanf to get the integers.
Try this.
http://www.gnu.org/software/libc/manual/html_node/String-Streams.html

You are correct: sscanf indeed does not "move", because there is nothing to move. If you need to scan a bunch of ints, you can use strtol - it tells you how much it read, so you can feed the next pointer back to the function on the next iteration.
char str[] = "10 21 32 43 54";
char *p = str;
int i;
for (i = 0 ; i != 5 ; i++) {
int n = strtol(p, &p, 10);
printf("%d\n", n);
}

This is the correct behavior of sscanf. sscanf operates on a const char*, not an input stream from a file, so it will not store any information about what it has consumed.
As for the solution, you can use %n in the format string to obtain the number of characters that it has consumed so far (this is defined in C89 standard).
e.g. sscanf("This is a string", "%10s%10s%n", tok1, tok2, &numChar); numChar will contain the number of characters consumed so far. You can use this as an offset to continue scanning the string.
If the string only contains integers that doesn't exceed the maximum value of long type (or long long type), use strtol or strtoll. Beware that long type can be 32-bit or 64-bit, depending on the system.

Related

Parsing a char[] of integers into ints i

I used fgets() to read input of a file into a
char buf[10];
the input of the file is
10,10,4,10
I want to iterate through the line and store each number in the char array as its own individual integer value but I am a little lost on how to do that. If someone could point me in the right direction I'd really appreciate it, thanks!
There are different approaches to do this. If you are learning C, I would recommend writing your own by-hand solution and after understanding how it works, use some standard library functions like strtok as #Jeff Holt said.
The easiest approach is to create a variable which will be the current number which we are reading and iterate over the buf array and at each step check if the current character is a number or not.
See this question about converting characters to integers.
So the result will be something like that:
const int size = 10;
char buf[size];
// fill buf, but be sure that input is less than 10 characters
//create array for result
char result[size];
int result_size = 0;
//can also be char, but if input numbers are big enough, it might overflow
int current_value = 0;
for (int i = 0; i < size; i++) {
if (buf[i] >= '0' && buf[i] <= '9') {
//convert to int and add to current_value
} else {
//store parsed integer
result[current_size] = current_value;
current_size++;
current_value = 0;
}
}

Add up digit from char array in c language

I am new to C programming and trying to make a program to add up the digits from the input like this:
input = 12345 <= 5 digit
output = 15 <= add up digit
I try to convert the char index to int but it dosent seems to work! Can anyone help?
Here's my code:
#include <stdio.h>
#include <string.h>
int main(){
char nilai[5];
int j,length,nilai_asli=0,i;
printf("nilai: ");
scanf("%s",&nilai);
length = strlen(nilai);
for(i=0; i<length; i++){
int nilai1 = nilai[i];
printf("%d",nilai1);
}
}
Output:
nilai: 12345
4950515253
You have two problems with the code you show.
First lets talk about the problem you ask about... You display the encoded character value. All characters in C are encoded in one way or another. The most common encoding scheme is called ASCII where the digits are encoded with '0' starting at 48 up to '9' at 57.
Using this knowledge it should be quite easy to figure out a way to convert a digit character to the integer value of the digit: Subtract the character '0'. As in
int nilai1 = nilai[i] - '0'; // "Convert" digit character to its integer value
Now for the second problem: Strings in C are really called null-terminated byte strings. That null-terminated bit is quite important, and all strings functions (like strlen) will look for that to know when the string ends.
When you input five character for the scanf call, the scanf function will write the null-terminator on the sixth position in the five-element array. That is out of bounds and leads to undefined behavior.
You can solve this by either making the array longer, or by telling scanf not to write more characters into the array than it can actually fit:
scanf("%4s", nilai); // Read at most four characters
// which will fit with the terminator in a five-element array
First of all, your buffer isn't big enough. String input is null-terminated, so if you want to read in your output 12345 of 5 numbers, you need a buffer of at least 6 chars:
char nilai[6];
And if your input is bigger than 5 chars, then your buffer has to be bigger, too.
But the problem with adding up the digits is that you're not actually adding up anything. You're just assigning to int nilai1 over and over and discarding the result. Instead, put int nilai1 before the loop and increase it in the loop. Also, to convert from a char to the int it represents, subtract '0'. All in all this part should look like this:
int nilai1 = 0;
for (i = 0; i < length; i++) {
nilai1 += nilai[i] - '0';
}
printf("%d\n", nilai1);
For starters according to the C Standard the function main without parameters shall be declared like
int main( void )
This character array
char nilai[5];
can not contain a string with 5 digits. Declare the array with at least one more character to store the terminating zero of a string.
char nilai[6];
In the call of scanf
scanf("%s",&nilai);
remove the operator & before the name nilai. And such a call is unsafe. You could use for example the standard function fgets.
This call
length = strlen(nilai);
is redundant and moreover the variable length should be declared having the type size_t.
This loop
for(i=0; i<length; i++){
int nilai1 = nilai[i];
printf("%d",nilai1);
}
entirely does not make sense.
The program can look the following way
#include <stdio.h>
#include <ctype.h>
int main(void)
{
enum { N = 6 };
char nilai[N];
printf( "nilai: ");
fgets( nilai, sizeof( nilai ), stdin );
int nilai1 = 0;
for ( const char *p = nilai; *p != '\0'; ++p )
{
if ( isdigit( ( unsigned char ) *p ) ) nilai1 += *p - '0';
}
printf( "%d\n", nilai1 );
return 0;
}
Its output might look like
nilai: 12345
15

C - Print ASCII Value for Each Character in a String

I'm new to C and I'm trying to write a program that prints the ASCII value for every letter in a name that the user enters. I attempted to store the letters in an array and try to print each ASCII value and letter of the name separately but, for some reason, it only prints the value of the first letter.
For example, if I write "Anna" it just prints 65 and not the values for the other letters in the name. I think it has something to do with my sizeof(name)/sizeof(char) part of the for loop, because when I print it separately, it only prints out 1.
I can't figure out how to fix it:
#include <stdio.h>
int main(){
int e;
char name[] = "";
printf("Enter a name : \n");
scanf("%c",&name);
for(int i = 0; i < (sizeof(name)/sizeof(char)); i++){
e = name[i];
printf("The ASCII value of the letter %c is : %d \n",name[i],e);
}
int n = (sizeof(name)/sizeof(char));
printf("%d", n);
}
Here's a corrected, annotated version:
#include <stdio.h>
#include <string.h>
int main() {
int e;
char name[100] = ""; // Allow for up to 100 characters
printf("Enter a name : \n");
// scanf("%c", &name); // %c reads a single character
scanf("%99s", name); // Use %s to read a string! %99s to limit input size!
// for (int i = 0; i < (sizeof(name) / sizeof(char)); i++) { // sizeof(name) / sizeof(char) is a fixed value!
size_t len = strlen(name); // Use this library function to get string length
for (size_t i = 0; i < len; i++) { // Saves calculating each time!
e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
printf("\n Name length = %zu\n", strlen(name)); // Given length!
int n = (sizeof(name) / sizeof(char)); // As noted above, this will be ...
printf("%d", n); // ... a fixed value (100, as it stands).
return 0; // ALWAYS return an integer from main!
}
But also read the comments given in your question!
This is a rather long answer, feel free to skip to the end for the code example.
First of all, by initialising a char array with unspecified length, you are making that array have length 1 (it only contains the empty string). The key issue here is that arrays in C are fixed size, so name will not grow larger.
Second, the format specifier %c causes scanf to only ever read one byte. This means that even if you had made a larger array, you would only be reading one byte to it anyway.
The parameter you're giving to scanf is erroneous, but accidentally works - you're passing a pointer to an array when it expects a pointer to char. It works because the pointer to the array points at the first element of the array. Luckily this is an easy fix, an array of a type can be passed to a function expecting a pointer to that type - it is said to "decay" to a pointer. So you could just pass name instead.
As a result of these two actions, you now have a situation where name is of length 1, and you have read exactly one byte into it. The next issue is sizeof(name)/sizeof(char) - this will always equal 1 in your program. sizeof char is defined to always equal 1, so using it as a divisor causes no effect, and we already know sizeof name is equal to 1. This means your for loop will only ever read one byte from the array. For the exact same reason n is equal to 1. This is not erroneous per se, it's just probably not what you expected.
The solution to this can be done in a couple of ways, but I'll show one. First of all, you don't want to initialize name as you do, because it always creates an array of size 1. Instead you want to manually specify a larger size for the array, for instance 100 bytes (of which the last one will be dedicated to the terminating null byte).
char name[100];
/* You might want to zero out the array too by eg. using memset. It's not
necessary in this case, but arrays are allowed to contain anything unless
and until you replace their contents.
Parameters are target, byte to fill it with, and amount of bytes to fill */
memset(name, 0, sizeof(name));
Second, you don't necessarily want to use scanf at all if you're reading just a byte string from standard input instead of a more complex formatted string. You could eg. use fgets to read an entire line from standard input, though that also includes the newline character, which we'll have to strip.
/* The parameters are target to write to, bytes to write, and file to read from.
fgets writes a null terminator automatically after the string, so we will
read at most sizeof(name) - 1 bytes.
*/
fgets(name, sizeof(name), stdin);
Now you've read the name to memory. But the size of name the array hasn't changed, so if you used the rest of the code as is you would get a lot of messages saying The ASCII value of the letter is : 0. To get the meaningful length of the string, we'll use strlen.
NOTE: strlen is generally unsafe to use on arbitrary strings that might not be properly null-terminated as it will keep reading until it finds a zero byte, but we only get a portable bounds-checked version strnlen_s in C11. In this case we also know that the string is null-terminated, because fgets deals with that.
/* size_t is a large, unsigned integer type big enough to contain the
theoretical maximum size of an object, so size functions often return
size_t.
strlen counts the amount of bytes before the first null (0) byte */
size_t n = strlen(name);
Now that we have the length of the string, we can check if the last byte is the newline character, and remove it if so.
/* Assuming every line ends with a newline, we can simply zero out the last
byte if it's '\n' */
if (name[n - 1] == '\n') {
name[n - 1] = '\0';
/* The string is now 1 byte shorter, because we removed the newline.
We don't need to calculate strlen again, we can just do it manually. */
--n;
}
The loop looks quite similar, as it was mostly fine to begin with. Mostly, we want to avoid issues that can arise from comparing a signed int and an unsigned size_t, so we'll also make i be type size_t.
for (size_t i = 0; i < n; i++) {
int e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
Putting it all together, we get
#include <stdio.h>
#include <string.h>
int main() {
char name[100];
memset(name, 0, sizeof(name));
printf("Enter a name : \n");
fgets(name, sizeof(name), stdin);
size_t n = strlen(name);
if (n > 0 && name[n - 1] == '\n') {
name[n - 1] = '\0';
--n;
}
for (size_t i = 0; i < n; i++){
int e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
/* To correctly print a size_t, use %zu */
printf("%zu\n", n);
/* In C99 main implicitly returns 0 if you don't add a return value
yourself, but it's a good habit to remember to return from functions. */
return 0;
}
Which should work pretty much as expected.
Additional notes:
This code should be valid C99, but I believe it's not valid C89. If you need to write to the older standard, there are several things you need to do differently. Fortunately, your compiler should warn you about those issues if you tell it which standard you want to use. C99 is probably the default these days, but older code still exists.
It's a bit inflexible to be reading strings into fixed-size buffers like this, so in a real situation you might want to have a way of dynamically increasing the size of the buffer as necessary. This will probably require you to use C's manual memory management functionality like malloc and realloc, which aren't particularly difficult but take greater care to avoid issues like memory leaks.
It's not guaranteed the strings you're reading are in any specific encoding, and C strings aren't really ideal for handling text that isn't encoded in a single-byte encoding. There is support for "wide character strings" but probably more often you'll be handling char strings containing UTF-8 where a single codepoint might be multiple bytes, and might not even represent an individual letter as such. In a more general-purpose program, you should keep this in mind.
If we need write a code to get ASCII values of all elements in a string, then we need to use "%d" instead of "%c". By doing this %d takes the corresponding ascii value of the following character.
If we need to only print the ascii value of each character in the string. Then this code will work:
#include <stdio.h>
char str[100];
int x;
int main(){
scanf("%s",str);
for(x=0;str[x]!='\0';x++){
printf("%d\n",str[x]);
}
}
To store all corresponding ASCII value of character in a new variable, we need to declare an integer variable and assign it to character. By this way the integer variable stores ascii value of character. The code is:
#include <stdio.h>
char str[100];
int x,ascii;
int main(){
scanf("%s",str);
for(x=0;str[x]!='\0';x++){
ascii=str[x];
printf("%d\n",ascii);
}
}
I hope this answer helped you.....😊

C: Check if a string is an integer and save it

I've been searching the internet for some time, but didn't find a simple solution for a actually simple problem in my eyes. I guess it has been asked already:
I'm reading a value like 20.1 or XYZ via sscanf from a file and saving it in char *width_as_string.
All functions should be valid in -std=c99.
Now I want to check if the value in width_as_string is an integer. If true, it should be saved in int width. If false, width should remain with the value 0.
My approaches:
int width = 0;
if (isdigit(width_as_string)) {
width = atoi(width_as_string);
}
Alternatively, convert width_as_string to int width and convert it back to a string. Then compare if it is the same. But I'm not sure how to achieve that. I already tried itoa.
Functions like isdigit and itoa are not valid in std=c99, therefore I can't use them.
Thanks.
Read carefully some documentation of sscanf. It returns a count, and accepts the %n conversion specifier to give the number of character (bytes) scanned so far. Perhaps you want:
int endpos = 0;
int width = 0;
if (sscanf(width_as_string, "%d %n", &width, &endpos)>=1 && endpos>0) {
behappywith(width);
};
Perhaps you want also to add && width_as_string[endpos]==(char)0 (to check that the number is perhaps space suffixed, then reaching the end of string) after endpos>0
You could also consider the standard strtol which sets an end pointer:
char*endp = NULL;
width = (int) strtol(width_as_string, &endp, 0);
if (endp>width_as_string && *endp==(char)0 && width>=0) {
behappywith(width);
}
The *endp == (char)0 is testing that the end of number pointer -filled by strtol- is the end of string pointer (since a string is terminated with a zero byte). You could make that more fancy if you want to accept trailing spaces.
PS. Actually, you need to specify precisely what is an acceptable input (perhaps by some EBNF syntax). We don't know if "1 " or "2!" or "3+4" are (as C strings) acceptable to you.
How about strtol?
This gives a clear return value if something goes wrong, i think this is what you're looking for
http://www.cplusplus.com/reference/cstdlib/strtol/
Actually, you could use sscanf at the very beginning to check whether the number is integer or not. Something like this
#include <stdio.h>
#include <string.h>
int
main (int argc, char *argv[])
{
int wc; // width to check
int w; // width
char *string = "20.1";
printf("string = %s\n", string);
if (strchr(string, '.') != NULL)
{
wc = 0;
printf("wc = %d\n", wc);
}
else if ((sscanf(string, "%d", &w)) > 0)
{
wc = w;
printf("wc = %d\n", wc);
} else w = 0;
return 0;
}
This is a sample program of course, it first searches the string for a "." to verify if the number could be float and discards it in such a case, then tries to read an integer if no "." are found.
Changed thanks to ameyCU's suggestion
Reference page for sscanf

Inserting leading zeros into an integer

I have a function in C which generates a number of hours from a rtc peripheral, which I then want to fill an array within a struct object. The array is set up to take 5 digits, but I need to prepend leading zeros to the number when it is less than 5 digits.
Could anyone advise on an easy way of achieving this?
char array[5];
snprintf(array, 5, "%05d", number)
A couple little routines that'll do what you want without having a printf() library hanging around. Note that there's not a way for the routines to tell you that the int you've passed in is too big for the buffer size passed in - I'll leave adding that as an exercise for the reader. But these are safe against buffer overflow (unless I've left a bug in there).
void uint_to_str_leading( char* dst, unsigned int i, size_t size )
{
char* pCurChar;
if (size == 0) return;
pCurChar = dst + size; // we'll be working the buffer backwards
*--pCurChar = '\0'; // take this out if you don't want an ASCIIZ output
// but think hard before doing that...
while (pCurChar != dst) {
int digit = i % 10;
i = i /10;
*--pCurChar = '0' + digit;
}
return;
}
void int_to_str_leading( char* dst, int i, size_t size )
{
if (size && (i < 0)) {
*dst++ = '-';
size -= 1;
i *= -1;
}
uint_to_str_leading( dst, i, size);
return;
}
Note that these routines pass in the buffer size and terminate with a '\0', so your resulting strings will have one less character than the size you've passed in (so don't just pass in the field size you're looking for).
If you don't want the terminating '\0' character because you're dealing with a fixed char[] array that's not terminated, it's easy enough to take out that one line of code that does the termination (but please consider terminating the char[] array - if you don't, I'll bet you'll see at least one bug related to that sometime over the next 6 months).
Edit to answer some questions:
dst is a pointer to a destination buffer. The caller is responsible to have a place to put the string that's produced by the function, much like the standard library function strcpy().
The pointer pCurChar is a pointer to the location that the next digit character that's produced will go. It's used a little differently than most character pointers because the algorithm starts at the end of the buffer and moves toward the start (that's because we produce the digits from the 'end' of the integer). Actually, pCurChar points just past the place in the buffer where it's going to put the next digit. When the algorithm goes to add a digit to the buffer, it decrements the pointer before dereferencing it. The expression:
*--pCurChar = digit;
Is equivalent to:
pCurChar = pCurChar-1; /* move to previous character in buffer */
*pCurChar = digit;
It does this because the test for when we're done is:
while (pCurChar == dst) { /* when we get to the start of the buffer we're done */
The second function is just a simple routine that handles signed ints by turning negative numbers into positive ones and letting the 1st function do all the real work, like so:
putting a '-' character at the start of the buffer,
adjusting the buffer pointer and size, and
negating the number to make it positive
passing that onto the function that converts an unsigned int to do the real work
An example of using these functions:
char buffer[80];
uint_to_str_leading( buffer, 0, 5);
printf( "%s\n", buffer);
uint_to_str_leading( buffer, 123, 6);
printf( "%s\n", buffer);
uint_to_str_leading( buffer, UINT_MAX, 14);
printf( "%s\n", buffer);
int_to_str_leading( buffer, INT_MAX, 14);
printf( "%s\n", buffer);
int_to_str_leading( buffer, INT_MIN, 14);
printf( "%s\n", buffer);
Which produces:
0000
00123
0004294967295
0002147483647
-002147483648
like #Aaron's solution, the only way to do it in C is to treat it like a character instead of a number. Leading zeros are ignored when they occur as a value and indicate an octal constant when appearing in code.
int a = 0000015; // translates to decimal 13.
Initialize every element in the array to 0 before inserting the digits into the array. That way you're guaranteed to always have 5 digits with leading zeroes.
If the type is int, then no. Set the type to string?
try this :
char source[5+1] = { '1','2', 0, 0, 0, 0 };
char dest[5+1];
int nd = strlen(source) ;
memset ( dest, '0', 5 - nd );
sprintf ( dest+nd+1, "%s", source );
Doesn't simple total bzero() or something like this and then filling with new value suit you? Could you please describe more details if not?
I'm not joking, I have seen several cases when total filling with 0 is faster then tricks to add 0s, especially if memory was cached and bzero() had some burst write support.
int newNumber [5] = 0;
for ( int i=0; givenNumber != 0 && i < 5 ; i++ ) {
newNumber[i] = givenNumber%10;
givenNumber = givenNumer/10;
}
apologies for the lack of detail. My code generates a 32-bit integer, which I am expecting to only get to 5 decimal digits in size. I then call the function:
numArray = numArrayFill(12345, buf, sizeof(buf));
(uint32_t number, char *buffer, int size)
{
char *curr = &buffer[size];
*--curr = 0;
do
{
*--curr = (number % 10) + '0';
number /= 10;
}
while (number != 0);
if (curr == buffer && number != 0)
return NULL;
return curr;
}
The problem comes when I put in a less than 5 digit number and I get random behaviour. What I need to do is to append zeros to the front so that it is always a 5 digit number.
Thanks
Dave

Resources