How to extract various integers from a string in C? - c

I was wondering how to extract various numbers from a string. I understand that strtol works, however it appears to only work for the first digit.
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
long v1, v2, v3;
char str[20] = "16,23";
char *d;
v1 = strtol(str, &d, 10);
v2 = strtol(str, &d, 10);
printf("string is %s\nv1 is:%i\nv2 is:%d\n",str , v1,v2);
return 0;
}
In this example I would like to output v1 = 16 and v2 = 23.
Another example, if the str was "12,23,34", I would like v3= 34
Thanks in advance :)

You can have many approaches. One of them is to make use of the endptr, populated by the previous strtol() call as the source of the next strtol().
Otherwise, for a better and flexible approach, you also have an option of using strtok() with a predefined delimiter (the , here) to get the tokens one by one and convert them to int or long (as you wish) until strtok() returns NULL.

Use long strtol(const char * nptr, char ** endptr, int base). The endptr allows for easy subsequent parsing as that is where parsing stopped.
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
int string_to_longs(const char *s) {
#define N 3
long v[N];
int i;
for (i=0; i<N; i++) {
errno = 0;
char *end;
v[i] = strtol(s, &end, 10);
if (errno) return -1; // overflow
if (s == end) return -1; // no conversion
printf("v[%d] = %ld\n", i, v[i]);
if (*end == 0) break; // we are done
if (*end != ',') return -1; // missing comma
s = (const char *) (end + 1);
}
return i;
}
int main(void) {
string_to_longs("16,23");
string_to_longs("12,23,34");
return 0;
}

strtol just converts a character array to a long int. It stops when it finds the first character that wouldn't make sense into interpreting an integer from.
There is a function in string.h named strtok which helps you tokenize a string.
Beware that strtok mutates the original character array contents.

Related

Writing concise code in C

strcat(msg, ": ");
strcat(msg, buf);
Is there a way to do this in one line? I want to make my code cleaner and reduce clutter
Instead of doing multiple concatenations, try creating a formatted string. Try something like this:
#include <stdio.h>
#include <string.h>
int main()
{
char *before_colon = "Text before colon";
char *after_colon = "Text after colon";
// Make a string that is the size of both formatted strings, plus a
// character for the space, colon, and null character.
char final_string[strlen(before_colon) + strlen(after_colon) + 3];
// This works just like any other C formatted function (i.e printf, scanf)
sprintf(final_string, "%s: %s", before_colon, after_colon);
printf("%s\n", final_string);
}
output:
Text before colon: Text after colon
Here's a modified code of Charlie Sale with its own function to count characters in the string. Thus, StrLen is called in array declaration.
#include <stdio.h>
#include <string.h>
int StrLen(char* PtrFirstChar)
{
int n = 0;
while(*(PtrFirstChar++)) // will evaluate to FALSE when '\0' reached
n++;
return n;
}
int main()
{
char *before_colon = "Text before colon";
char *after_colon = "Text after colon";
// Make a string that is the size of both formatted strings, plus a
// character for the space, colon, and null character.
char final_string[StrLen(before_colon) + StrLen(after_colon) + 3];
// This works just like any other C formatted function (i.e printf, scanf)
sprintf(final_string, "%s: %s", before_colon, after_colon);
printf("%s\n", final_string);
}
You could write your own variant of strcat!
I'm going to use strncat as a basis because strcat is a really bad idea:
#include <stddef.h> /* for size_t */
#include <stdarg.h> /* for va_* */
char *
mstrncat(char *d, size_t maxlen, ...)
{
va_list ap;
va_start(ap, maxlen);
char *ret = d;
/* Fast-forward */
for (; *d && maxlen; ++d, --maxlen);
/* Reserve a space for the terminator */
if (maxlen)
--maxlen;
const char *p;
/* Concatenate parameters one by one */
while (maxlen && (p = va_arg(ap, const char *))) {
while (*p && maxlen--)
*d++ = *p++;
}
/* Terminate the string */
*d = 0;
va_end(ap);
return ret;
}
You can use it like this:
#include <stdio.h>
int
main()
{
char test[128]="test";
mstrncat(test, sizeof(test), "1", "two", "3", NULL);
puts(test);
return 0;
}

Separating two values in a char and assigning them to varables

New to C and Arduino programming, my issue, how to split char c into two separate integer variables? char c's value looks like this 140,100, both numbers are integers. I need to create two integer variables, SpeedX and SpeedY from this data so that SpeedX = 140 and SpeedY = 100. Thanks for your help!
how to split char c into two separate integer variables?
First of all, what you have is a string, not a char.
You can use the strtok() function to split a string (and specify the delimiter to use), like this :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char nums[] = "140,100";
char *str;
int num;
int SpeedX, SpeedY, i = 0;
str = strtok (nums, ",");
while (str != NULL)
{
num = atoi(str);
if (i == 0)
SpeedX = num;
else if (i == 1)
SpeedY = num;
str = strtok (NULL, ",");
i++;
}
return 0;
}
NOTE : If, generally speaking about such a problem to solve in c and not in arduino, you do not know for sure that you will have only two elements in nums, you should use dynamic memory allocation (read more about it here), and modify your code like this :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char nums[] = "140,100,500";
char *str;
int num;
int len, i = 0;
int *p;
str = strtok (nums, ",");
len = strlen(str);
p = malloc((len-1)*sizeof(int));
while (str != NULL)
{
num = atoi(str);
*(p+i) = num;
i++;
str = strtok (NULL, ",");
}
for (i = 0; i < len-1; i++)
printf("%d\n", *(p+i));
return 0;
}
The char you are talking about is most likely string or char array, which is really a pointer to a number of chars. A char can only store 1 character.
The function strtol defined in stdlib.h takes three arguments:
A char pointer to the beginning of your string (or the place you want to start looking
A pointer to a char pointer that will be updated to point to the first char after the end of the number
The base the number is represented in, most likely 10 in your case
Your data type is probably char* or char[50], because char can only represent one character, not a sequence of characters like "140,100".
Anyway, use sscanf, which scans a string and extracts values according to a given format:
char *myNums = "140,100";
int first,second;
int elementsRead = sscanf(myNums,"%d,%d", &first, &second);
// note: elementsRead is `2` if both format specifiers `%d` and `%d` have been scanned successfully

inserting comma in a large int number

i am trying to insert a comma in a large integer number eg, 870120000 and present it as: 870,120 and the last three digits are discarded. The format will allways be the same, ie., xxxxxxxxx number where i am only interested in the first 6 digits.
What i have been doing so fare is using snprintf:
#include <stdio.h>
#include <string.h>
int main()
{
char buffer[5];
unsigned int lNum= 870120000;
int cx;
memset(buffer,0,sizeof(buffer));
cx = snprintf(buffer, 4, "%d,", lNum);
buffer[3] = ',';
printf("%s\n",buffer);
cx = snprintf(buffer+4, 4, "321"); // Note 1 ???
printf("%s\n", buffer);
return 0;
}
My problem is that i am stuck at (comment // Note 1), how do I add the last next three digits in to buffer ?
in addition I would like to know if this approch is ideal or is there any better(easier) way to do this?
You can do that in two steps. First convert the lNum into a string, then cut the needed parts out of this string. Something like that.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char tmp[256], out[256];
unsigned int lNum= 870120000;
sprintf(tmp, "%u", lNum);
sprintf(out, "%3.3s,%3.3s", tmp, tmp+3);
printf("%s\n", out); // prints 870,120
return 0;
}
See http://ideone.com/bnQNou
Or you can convert it into a float and change the locale to have "," as delimiter:
#include <stdio.h>
#include <locale.h>
int main()
{
unsigned int lNum = 870120000;
setlocale(LC_NUMERIC, "de_DE");
printf("%3.3f\n", (float)(870120000 / 1000000)); // prints 870,120
return 0;
}
First of all to hold 870,120 you have to define a 8 chars buffer.
So you can use %f format specifier to do what you the job, e.g.:
#include <stdio.h>
#include <string.h>
int main()
{
char buffer[8] = {0};
unsigned int lNum= 870120000;
snprintf(buffer, sizeof(buffer), "%3.3f", (float)(lNum)/(1000000));
char *temp = strstr(buffer, ".");
if (temp != NULL)
{
*temp = ',';
}
printf("%s\n",buffer);
return 0;
}
or another example using double snprintf and a given number of integer part digits and decimal part digits.
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define INT_DIGIT 3
#define DEC_DIGIT 3
int main()
{
char temp_buffer[128];
char buffer[INT_DIGIT+DEC_DIGIT+2] = {0};
unsigned int lNum= 87012000;
snprintf(temp_buffer, sizeof(temp_buffer), "%u",lNum);
snprintf(buffer, sizeof(buffer), "%.*s,%.*s", INT_DIGIT, temp_buffer, DEC_DIGIT, &temp_buffer[INT_DIGIT]);
printf("%s\n",buffer);
return 0;
}

Convert to ascii to hex without using print

So I want to be able to somehow change a string into hex like so: "ab.c2" --> "61622e6332". All the help I've found online shows how to do it by using print, but I don't want to use print because it doesn't store the hex value.
What I know so far is that if you take a char and cast it to an int you get the ascii value and with that I can get the hex, which is where I'm stumped.
Here's one way to do it, a complete program but the "meat" is in the tohex function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * tohex (unsigned char *s) {
size_t i, len = strlen (s) * 2;
// Allocate buffer for hex string result.
// Only output if allocation worked.
char *buff = malloc (len + 1);
if (buff != NULL) {
// Each char converted to hex digit string
// and put in correct place.
for (i = 0; i < len ; i += 2) {
sprintf (&(buff[i]), "%02x", *s++);
}
}
// Return allocated string (or NULL on failure).
return buff;
}
int main (void) {
char *input = "ab.c2";
char *hexbit = tohex (input);
printf ("[%s] -> [%s]\n", input, hexbit);
free (hexbit);
return 0;
}
There are of course other ways to achieve the same result, such as avoiding memory allocation if you can ensure you provide your own buffer that's big enough, something like:
#include <stdio.h>
void tohex (unsigned char *in, char *out) {
while (*in != '\0') {
sprintf (out, "%02x", *in++);
out += 2;
}
}
int main (void) {
char input[] = "ab.c2";
char output[sizeof(input) * 2 - 1];
tohex (input, output);
printf ("[%s] -> [%s]\n", input, output);
return 0;
}

Progress string parsing in C

I have the following character string:
"..1....10..20....30...40....50...80..."
and I need to extract all numbers from it into array.
What is the best way to do it in C?
Perhaps the easiest way is to use the strtok() function (or strtok_r() if reentrancy is a concern):
char str[] = "..1...10...20";
char *p = strtok(str, ".");
while (p != NULL) {
printf("%d\n", atoi(p));
p = strtok(NULL, ".");
}
Once you have the results of calling atoi(), it should be a simple matter to save those integers into an array.
You can use a sscanf code with suppressed assignment (%*[.]) to skip over the dots (or any other character you want), and a scanned character count code %n to advance the string pointer.
const char *s = "..1....10..20....30...40....50...80...";
int num, nc;
while (sscanf(s, "%*[.]%d%n", &num, &nc) == 1) {
printf("%d\n", num);
s += nc;
}
Here is the correct way to do it, it is a little longer than the simplest way but it doesn't suffer from undefined behavior if the value read is out of range, works properly if the first character is not a dot, etc. You didn't specify whether the numbers could be negative so I used a signed type but only allow positive values, you can easily change this by allowing the negative sign at the top of the inner while loop. This version allows any non-digit characters to delimit integers, if you only want dots to be allowed you can modify the inner loop to skip only dots and then check for a digit.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#define ARRAY_SIZE 10
size_t store_numbers (const char *s, long *array, size_t elems)
{
/* Scan string s, returning the number of integers found, delimited by
* non-digit characters. If array is not null, store the first elems
* numbers into the provided array */
long value;
char *endptr;
size_t index = 0;
while (*s)
{
/* Skip any non-digits, add '-' to support negative numbers */
while (!isdigit(*s) && *s != '\0')
s++;
/* Try to read a number with strtol, set errno to 0 first as
* we need it to detect a range error. */
errno = 0;
value = strtol(s, &endptr, 10);
if (s == endptr) break; /* Conversion failed, end of input */
if (errno != 0) { /* Error handling for out of range values here */ }
/* Store value if array is not null and index is within array bounds */
if (array && index < elems) array[index] = value;
index++;
/* Update s to point to the first character not processed by strtol */
s = endptr;
}
/* Return the number of numbers found which may be more than were stored */
return index;
}
void print_numbers (const long *a, size_t elems)
{
size_t idx;
for (idx = 0; idx < elems; idx++) printf("%ld\n", a[idx]);
return;
}
int main (void)
{
size_t found, stored;
long numbers[ARRAY_SIZE];
found = store_numbers("..1....10..20....30...40....50...80...", numbers, ARRAY_SIZE);
if (found > ARRAY_SIZE)
stored = ARRAY_SIZE;
else
stored = found;
printf("Found %zu numbers, stored %zu numbers:\n", found, stored);
print_numbers(numbers, stored);
return 0;
}
I prefer the use of strtok in a for loop. Makes it feel more natural, though the syntax looks a little weird.
char str[] = "..1....10..20....30...40....50...80..."
for ( char* p = strtok( strtok, "." ); p != NULL; p = strtok( NULL, "." ) )
{
printf( "%d\n", atoi( p ) );
}

Resources