Inserting leading zeros into an integer - c

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

Related

Adding leading 'zeros' to a number without library in C

I wanted to store 'two zeros' in a value e.g. answer(which is a BYTE) and send it over to RS-485. How can I do that without any library?
Secondly, I tried by adding char '0' to zero(0) but instead it converted it to equivalent of zero which is '48' and send it over RS-485.
enter image description here
Thanks,
sprintf(buffer, sizeof buffer, "%03d", the_byte_number);
will do what you want, you can then send the buffer contents over the RS-485 using something like:
write(rs_485_file_descriptor, buffer, strlen(buffer);
if you refuse to use even the C standard library, you'll have to do the conversion yourself:
char *zero_pad(uint8_t numb, char *buffer)
{
static char tab[] = "01234567898";
/* you need to have at least three characters for the three digits in
* buffer, no check is made here for efficiency reasons.
* we advance the buffer 3 positions first, and then go backwards,
* filling with characters, from the least significant digit to the most.
* in case you want the string to be null terminated, you must start
* with the '\0' char.
*/
#if YOU_WANT_IT_NULL_TERMINATED
buffer += 4;
*--buffer = '\0';
#else
buffer += 3;
#endif
for (int i = 0; i < 3; i++) {
int dig = numb % 10;
*--buffer = tab[dig];
number /= 10;
}
return buffer;
} /* zero_pad */
What you describe as 'two zeros' are two nibbles in the hexadecimal representation of the BYTE 0. Of course if you add the ASCII character '0' to zero, you get 4810. If you just want to send the BYTE answer, no conversion is needed.
wr = write(fd, &answer, sizeof answer);

Mystery of the mysterious P

Background:
I'm trying to create a program that takes a user name(assuming that input is clean), and prints out the initials of the name.
Objective:
Trying my hand out at C programming with CS50
Getting myself familiar with malloc & realloc
Code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
string prompt(void);
char *getInitials(string input);
char *appendArray(char *output,char c,int count);
//Tracks # of initials
int counter = 0;
int main(void){
string input = prompt();
char *output = getInitials(input);
for(int i = 0; i < counter ; i++){
printf("%c",toupper(output[i]));
}
}
string prompt(void){
string input;
do{
printf("Please enter your name: ");
input = get_string();
}while(input == NULL);
return input;
}
char *getInitials(string input){
bool initials = true;
char *output;
output = malloc(sizeof(char) * counter);
for(int i = 0, n = strlen(input); i < n ; i++){
//32 -> ASCII code for spacebar
//9 -> ASCII code for tab
if(input[i] == 32 || input[i] == 9 ){
//Next char after spaces/tab will be initial
initials = true;
}else{//Not space/tab
if(initials == true){
counter++;
output = appendArray(output,input[i],counter);
initials = false;
}
}
// eprintf("Input[i] is : %c\n",input[i]);
// eprintf("Counter is : %i\n",counter);
// eprintf("i is : %i\n",i);
// eprintf("n is : %i\n",n);
}
return output;
}
char *appendArray(char *output,char c,int count){
// allocate an array of some initial (fairly small) size;
// read into this array, keeping track of how many elements you've read;
// once the array is full, reallocate it, doubling the size and preserving (i.e. copying) the contents;
// repeat until done.
//pointer to memory
char *data = malloc(0);
//Increase array size by 1
data = realloc(output,sizeof(char) * count);
//append the latest initial
strcat(data,&c);
printf("Value of c is :%c\n",c);
printf("Value of &c is :%s\n",&c);
for(int i = 0; i< count ; i++){
printf("Output: %c\n",data[i]);
}
return data;
}
Problem:
The output is not what i expected as there is a mysterious P appearing in the output.
E.g When i enter the name Barack Obama, instead of getting the result:BO, i get the result BP and the same happens for whatever name i choose to enter, with the last initial always being P.
Output:
Please enter your name: Barack Obama
Value of c is :B
Value of &c is :BP
Output: B
Value of c is :O
Value of &c is :OP
Output: B
Output: P
BP
What i've done:
I've traced the problem to the appendArray function, and more specifically to the value of &c (Address of c) though i have no idea what's causing the P to appear,what it means, why it appears and how i can get rid of it.
The value of P shows up no matter when i input.
Insights as to why it's happening and what i can do to solve it will be much appreciated.
Thanks!
Several issues, in decreasing order of importance...
First issue - c in appendArray is not a string - it is not a sequence of character values terminated by a 0. c is a single char object, storing a single char value.
When you try to print c as a string, as in
printf("Value of &c is :%s\n",&c);
printf writes out the sequence of character values starting at the address of c until it sees a 0-valued byte. For whatever reason, the byte immediately following c contains the value 80, which is the ASCII (or UTF-8) code for the character 'P'. The next byte contains a 0 (or there's a sequence of bytes containing non-printable characters, followed by a 0-valued byte).
Similarly, using &c as the argument to strcat is inappropriate, since c is not a string. Instead, you should do something like
data[count-1] = c;
Secondly, if you want to treat the data array as a string, you must make sure to size it at least 1 more than the number of initials and write a 0 to the final element:
data[count-1] = 0; // after all initials have been stored to data
Third,
char *data = malloc(0);
serves no purpose, the behavior is implementation-defined, and you immediately overwrite the result of malloc(0) with a call to realloc:
data = realloc(output,sizeof(char) * count);
So, get rid of the malloc(0) call altogether; either just initialize data to NULL, or initialize it with the realloc call:
char *data = realloc( output, sizeof(char) * count );
Fourth, avoid using "magic numbers" - numeric constants with meaning beyond their immediate, literal value. When you want to compare against character values, use character constants. IOW, change
if(input[i] == 32 || input[i] == 9 ){
to
if ( input[i] == ' ' || input[i] == '\t' )
That way you don't have to worry about whether the character encoding is ASCII, UTF-8, EBCDIC, or some other system. ' ' means space everywhere, '\t' means tab everywhere.
Finally...
I know part of your motivation for this exercise is to get familiar with malloc and realloc, but I want to caution you about some things:
realloc is potentially an expensive operation, it may move data to a new location, and it may fail. You really don't want to realloc a buffer a byte at a time. Instead, it's better to realloc in chunks. A typical strategy is to multiply the current buffer size by some factor > 1 (typically doubling):
char *tmp = realloc( data, current_size * 2 );
if ( tmp )
{
current_size *= 2;
data = tmp;
}
You should always check the result of a malloc, calloc, or realloc call to make sure it succeeded before attempting to access that memory.
Minor stylistic notes:
Avoid global variables where you can. There's no reason counter should be global, especially since you pass it as an argument to appendArray. Declare it local to main and pass it as an argument (by reference) to getInput:
int main( void )
{
int counter = 0;
...
char *output = getInitials( input, &counter );
for(int i = 0; i < counter ; i++)
{
printf("%c",toupper(output[i]));
}
...
}
/**
* The "string" typedef is an abomination that *will* lead you astray,
* and I want to have words with whoever created the CS50 header.
*
* They're trying to abstract away the concept of a "string" in C, but
* they've done it in such a way that the abstraction is "leaky" -
* in order to use and access the input object correctly, you *need to know*
* the representation behind the typedef, which in this case is `char *`.
*
* Secondly, not every `char *` object points to the beginning of a
* *string*.
*
* Hiding pointer types behind typedefs is almost always bad juju.
*/
char *getInitials( const char *input, int *counter )
{
...
(*counter)++; // parens are necessary here
output = appendArray(output,input[i],*counter); // need leading * here
...
}

C: removing new line/null terminate input string

In C I'm using this method from a serial library:
int serialport_read_until(int fd, char* buf, char until, int buf_max, int timeout)
{
char b[1]; // read expects an array, so we give it a 1-byte array
int i=0;
do {
int n = read(fd, b, 1); // read a char at a time
if( n==-1) return -1; // couldn't read
if( n==0 ) {
usleep( 1 * 1000 ); // wait 1 msec try again
timeout--;
if( timeout==0 ) return -2;
continue;
}
#ifdef SERIALPORTDEBUG
printf("serialport_read_until: i=%d, n=%d b='%c'\n",i,n,b[0]); // debug
#endif
buf[i] = b[0];
i++;
} while( b[0] != until && i < buf_max && timeout>0 );
buf[i] = 0; // null terminate the string
return 0;
}
The string that it is going to read is like this:
"111\r\n" (with a carriage + new line behind)
It is being printed out in Arduino using
serial.print("1");
serial.print("1");
serial.println("1");
Using the serialport_read_until method (char until is '\r\n'), I want to ensure that I am reading the entire buffer correctly.
Which of the following below does the char* buf look like in the end?
1) 111\r\n
2) 111\r\n\0
3) 111\0
4) 111
I need to figure out this part before I use sscanf method to convert the string into an integer correctly, but I'm not sure which to use:
sscanf(buf, "%d\r\n", &num); OR sscanf(buf, "%d", &num);
In addition, should I change the 2nd last line: buf[i] = 0; to buf[i-1] = 0; ?
It looks to me like you should expect 111\r\n\0. Note that the condition b[0] != until is checked after incrementing i, so when the newline character is received and the loop exits, i points to the next byte after \n. Then b[i]=0 stores a null byte there.
Note that this code appears to have a bug: if the until character is never received, the loop will run until i == buf_max and then store one byte more with the null terminator. So a total of buf_max+1 bytes are stored, meaning the following code would have a buffer overflow:
char mybuf[123];
serialport_read_until(fd, buf, 'x', 123, 42);
Unless the documentation says that buf_max should be one less than the size of the buffer, which would be counterintuitive and error-prone, the loop termination condition should probably say i+1 < buf_max or something similar.
Also, since i is checked at the end, even with this fix, the code will still store one byte if you pass in buf_max == 0 (but without the fix it will store two bytes). So that's another bug.
The char b[1]; declaration and accompanying comment is a little weird too. It would be more idiomatic to simply declare char b; and then just pass &b to read().
So if this is your code, there's more work to do on it. If it's someone else's code, I'd be very careful using this library, if this function is any indication of the quality.
Doing buf[i-1]=0 at the end would avoid the overflow, but would also mean that if the until character is not received, the last byte received would be lost. It would also break if you ever call the function with buf_max == 0. So that's not what you want.
If you're using sscanf, the question of whether or not there is trailing whitespace is irrelevant; sscanf("%d") will just ignore it. You should have a careful read through your library's documentation of sscanf. In particular, the way it handles whitespace is not always intuitive.

Write a recursive function in C that converts a number into a string

I'm studying software engineering, and came across this exercise: it asks to write a recursive function in C language that receives a positive integer and an empty string, and "translates" the number into a string. Meaning that after calling the function, the string we sent would contain the number but as a string of its digits.
I wrote this function, but when I tried printing the string, it did print the number I sent, but in reverse.
This is the function:
void strnum(int n, char *str)
{
if(n)
{
strnum(n/10, str+1);
*str = n%10 + '0';
}
}
For example, I sent the number 123 on function call, and the output was 321 instead of 123.
I also tried exchanging the two lines within the if statement, and it still does the same. I can't figure out what I did wrong. Can someone help please?
NOTE: Use of while and for loop statements is not allowed for the exercise.
Note: your current implementation design is somewhat dangerous since you have no way of knowing if you are really writing in valid memory; consider implementing the function with a passed in len to know when you shouldn't try to write anymore; ie. to prevent buffer overruns.
Introduction
The problem is that you are shaving off the least significant digit, but assigning it to the most significant position in the buffer pointed to by str.
You will need to have the "off shaving" and the "assigning" synchronized, so that the least significant digit is stored at the end - and not the beginning.
Hints
Easiest solution would be to do what you currently are doing, and then later reverse the buffer, but this will require far more assignments than what is actually required.
The recommended way is to calculate the number of digits in your string, by doing this you'll know at what offset the end will be, and start assigning the least significant digit at that position.
How do I determine the number of digits of an integer in C?
The hack
Another alternative is having the recursive call modify the current value of our pointer, effectively making it assign the right value - at the right offset.
This example is mostly included because it's "fun", there are (as mentioned) other paths to walk.
#include <stdio.h>
void strnum_impl (int n, char ** str) {
if (n) {
strnum_impl (n/10, str);
**str = n%10 + '0';
(*str)++;
}
}
void strnum (int n, char * str) {
if (n == 0) { *str++ = '0'; }
else { strnum_impl (n, &str); }
*str = '\0'; /* null-terminate */
}
int main () {
char buf[256];
strnum (10240123, buf);
printf (">%s<\n", buf);
return 0;
}
>10240123<
As #Robert Harvey commented, as well as others, code is determining the least rather than the most significant digit and placing it in str[0].
It did look like fun to implement, so the below well copes with the entire range of int including INT_MIN and arbitrary sized int.
static char *strnum_helper(int n, char *str) {
str[0] = '0' - n%10;
if (n < -9) {
return strnum_helper(n/10, str - 1);
}
return str;
}
void strnum(int n, char *str) {
char buf[(sizeof n * CHAR_BIT)/3 + 3]; // Sized to any size int
if (n < 0) {
*str++ = '-';
}
else {
n = -n; // By using negative numbers, do not attempt -INT_MIN
}
buf[sizeof buf - 1] = '\0';
strcpy(str, strnum_helper(n, &buf[sizeof buf - 2]));
}
#Filip Roséen - refp pointed out the value of passing in a size. The above strnum() could be adjusted per a size limitation.

sscanf doesn't move, scanning same integer everytime

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.

Resources