Convert part of a string to int in one command in C - c

I have a long string where I know the location of start and end of an integer. I want to extract the integer and store it in a variable. This is how I can do it now.
//MY_STRING, INT_START, INT_END are the string, start
//position of the int and end position respectively.
char * temp;
strncpy(temp, MY_STRING+INT_START, INT_END-INT_START);
int n = atoi(temp);
The question is can I do this in one line, instead of using a temp string?

can I do this in one line, instead of using a temp string?
Use sscanf() for a quick dirty no-temp.
int n;
// v--- scan width limit
// | v---------------v
if (sscanf(MY_STRING + INT_START, "%*d", INT_END-INT_START, &n) == 1) {
puts("Success");
}
Better code would consider trouble with overflow.
OP's code is no good as temp is not certain to point to a string. It may lack a null character.
strncpy(temp, MY_STRING+INT_START, INT_END-INT_START);
int n = atoi(temp); // bad

No, you cannot do it in one line and the number of source code lines is most often an irrelevant metric. The only things that matter are safety, readability and performance. strncpy and atoi are both unsafe functions.
The correct way to do this is however quite similar:
int strn_to_int (const char* src, size_t n)
{
char str [LARGE_ENOUGH];
memcpy(str, src, n);
str[n] = '\0';
return strtol(str, NULL, 10);
}
...
int n = strn_to_int (&MY_STRING[INT_START], INT_END-INT_START);

Related

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.

For loop doesn't stop on the strlen

My biggest problem is on this line:
for(i=0;i<=strlen(enc);i++) ->
7th line of the function decifrar:
It keeps the loop even with the memset used to clear the memory (it's even bigger than the string length)
Note if I use the actual length of the string in that line the code does works (i.e. replacing strlen(enc) with 60 )
void decipher(int k, char *enc){
char alfa[]="9876543210zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA9876543210zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";
char *pch;
int i;
for(i=0;i<=strlen(enc);i++){
pch=strchr(alfa, enc[i]);
if (pch) enc[i] = *(pch + k),enc[i]=tolower(enc[i]);
}
printf("%s",enc);
}
int main(){
int keys[6]={1,4,15,24,12,20},i;
char *txt="rfgr r hz grkgb fvzcyrf dhr cergraqr fre grfgnqb ab cebtenzn";
char *txttemp=malloc(sizeof(char)*1024);
for(i=0;i<6;i++){
printf("\n\n\t Attempt number: %d\n\n",i+1);
memset(txttemp,'\0',sizeof(char)*strlen(txt)+30);
memcpy(txttemp, txt, strlen(txt));
decipher(keys[i],txttemp);
}
return 0;
}
What is the point that I am missing? Is the usage of strlen wrong?
It's < not <= in the for-loop.
But as a Note: avoid that pattern. Strlen means you count the length of the string, but you probably should already know it from somewhere (i.e. when you receive it: the file length, the number of charecters returned etc...). Save it and take that value as the length. This is the source of a lot of security holes in programs (a buffer overflow).
std::string has this as a built-in functionality and it's what I would recommend over a plain char* almost all of the time (that is if you can use C++)

strcat() for formatted strings

I'm building a string piece by piece in my program and am currently using a mix of strcat() when I'm adding a simple string onto the end, but when im adding a formatted string I'm using sprintf() e.g.:
int one = 1;
sprintf(instruction + strlen(instruction), " number %d", one);
is it possible to concatenate formatted string using strcat() or what is the preferred method for this?
Your solution will work. Calling strlen is a bit awkward (particularly if the string gets quite long). sprintf() will return the length you have used [strcat won't], so one thing you can do is something like this:
char str[MAX_SIZE];
char *target = str;
target += sprintf(target, "%s", str_value);
target += sprintf(target, "somestuff %d", number);
if (something)
{
target += sprintf(target, "%s", str_value2);
}
else
{
target += sprintf(target, "%08x", num2);
}
I'm not sure strcat is much more efficient than sprintf() is when used in this way.
Edit: should write smaller examples...
no it's not possible but you could use sprintf() on those simple strings and avoid calling strlen() every time:
len = 0;
len += sprintf(buf+len, "%s", str);
len += sprintf(buf+len, " number %d", one);
To answer the direct question, sure, it's possible to use strcat to append formatted strings. You just have to build the formatted string first, and then you can use strcat to append it:
#include <stdio.h>
#include <string.h>
int main(void) {
char s[100];
char s1[20];
char s2[30];
int n = 42;
double x = 22.0/7.0;
strcpy(s, "n = ");
sprintf(s1, "%d", n);
strcat(s, s1);
strcat(s, ", x = ");
sprintf(s2, "%.6f", x);
strcat(s, s2);
puts(s);
return 0;
}
Output:
n = 42, x = 3.142857
But this is not a particularly good approach.
sprintf works just as well writing to the end of an existing string. See Mats's answer and mux's answer for examples. The individual arrays used to hold individual fields are not necessary, at least not in this case.
And since this code doesn't keep track of the end of the string, the performance is likely to be poor. strcat(s1, s2) first has to scan s1 to find the terminating '\0', and then copy the contents of s2 into it. The other answers avoid this by advancing an index or a pointer to keep track of the end of the string without having to recompute it.
Also, the code makes no effort to avoid buffer overruns. strncat() can do this, but it just truncates the string; it doesn't tell you that it was truncated. snprintf() is a good choice; it returns the number of characters that it would have written if enough space were available. If this exceeds the size you specify, then the string was truncated.
/* other declarations as above */
size_t count;
count = snprintf(s, sizeof s, "n = %d, x = %.6f", n, x);
if (count > sizeof s) {
/* the string was truncated */
}
And to append multiple strings (say, if some are appended conditionally or repeatedly), you can use the methods in the other answers to keep track of the end of the target string.
So yes, it's possible to append formatted strings with strcat(). It's just not likely to be a good idea.
What the preferred method is, depends on what you are willing to use. Instead of doing all those manual (and potentially dangerous) string operations, I would use the GString data structure from GLib or GLib's g_strdup_print function. For your problem, GString provides the g_string_append_printf function.
Write your own wrapper for your need.
A call to this would look like this :-
result = universal_concatenator(4,result,"numbers are %d %f\n",5,16.045);
result = universal_concatenator(2,result,"tail_string");
You could define one function, that would take care of worrying about, if you need to use sprintf() or strcat(). This is what the function would look like :-
/* you should pass the number of arguments
* make sure the second argument is a pointer to the result always
* if non formatted concatenation:
* call function with number_of_args = 2
* else
* call function with number of args according to format
* that is, if five inputs to sprintf(), then 5.
*
* NOTE : Here you make an assumption that result has been allocated enough memory to
* hold your concatenated string. This assumption holds true for strcat() or
* sprintf() of your previous implementation
*/
char* universal_concaternator(int number_of_args,...)
{
va_list args_list;
va_start(args_list,number_of_args);
int counter = number_of_args;
char *result = va_arg(args_list, char*);
char *format;
if(counter == 2) /* it is a non-formatted concatenation */
{
result = strcat(result,va_arg(args_list,char*));
va_end(args_list);
return result;
}
/* else part - here you perform formatted concatenation using sprintf*/
format = va_arg(args_list,char*);
vsprintf(result + strlen(result),format,args_list);
va_end(args_list);
return result;
}
/* dont forget to include the header
* <stdarg.h> #FOR-ANSI
* or <varargs.h> #FOR-UNIX
*/
It should firstly, determine, which of the two it should call(strcat or sprintf), then it should make the call, and make it easy for you to concentrate on the actual logic of whatever you are working on!
Just ctrl+c code above and ctrl+v into your code base.
Note : Matt's answer is a good alternative for long strings. But for short string lengths(<250), this should do.

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

The simplest way of printing a portion of a char[] in C

Let's say I have a char* str = "0123456789" and I want to cut the first and the last three letters and print just the middle, what is the simplest, and safest, way of doing it?
Now the trick: The portion to cut and the portion to print are of variable size, so I could have a very long char*, or a very small one.
You can use printf(), and a special format string:
char *str = "0123456789";
printf("%.6s\n", str + 1);
The precision in the %s conversion specifier specifies the maximum number of characters to print. You can use a variable to specify the precision at runtime as well:
int length = 6;
char *str = "0123456789";
printf("%.*s\n", length, str + 1);
In this example, the * is used to indicate that the next argument (length) will contain the precision for the %s conversion, the corresponding argument must be an int.
Pointer arithmetic can be used to specify the starting position as I did above.
[EDIT]
One more point, if your string is shorter than your precision specifier, less characters will be printed, for example:
int length = 10;
char *str = "0123456789";
printf("%.*s\n", length, str + 5);
Will print "56789". If you always want to print a certain number of characters, specify both a minimum field width and a precision:
printf("%10.10s\n", str + 5);
or
printf("%*.*s\n", length, length, str + 5);
which will print:
" 56789"
You can use the minus sign to left-justify the output in the field:
printf("%-10.10s\n", str + 5);
Finally, the minimum field width and the precision can be different, i.e.
printf("%8.5s\n", str);
will print at most 5 characters right-justified in an 8 character field.
Robert Gamble and Steve separately have most of the pieces.
Assembled into a whole:
void print_substring(const char *str, int skip, int tail)
{
int len = strlen(str);
assert(skip >= 0);
assert(tail >= 0 && tail < len);
assert(len > skip + tail);
printf("%.*s", len - skip - tail, str + skip);
}
Invocation for the example:
print_substring("0123456789", 1, 3);
If you don't mind modifying the data, you could just do some pointer arithmetic. This is assuming that str is a char pointer and not an array:
char string[] = "0123456789";
char *str = string;
str += 3; // "removes" the first 3 items
str[4] = '\0'; // sets the 5th item to NULL, effectively truncating the string
printf(str); // prints "3456"
Here is a clean and simple substring function I dug up from my personal library that may be useful:
char *
substr(const char *src, size_t start, size_t len)
{
char *dest = malloc(len+1);
if (dest) {
memcpy(dest, src+start, len);
dest[len] = '\0';
}
return dest;
}
It's probably self-explanatory but it takes a string, a starting position (starting at zero), and a length and returns a substring of the original string or a null pointer if malloc fails. The pointer returned can be free'd by the caller when the memory is no longer needed. In the spirit of C, the function doesn't validate the starting position and length provided.
I believe there is some magic you can do with printf that will only print a certain number of characters, but it's not commonly understood or used. We tried to do it at a previous job and couldn't get it to work consistently.
What I would do is save off a character, null that character in the string, print it, then save it back.

Resources