I have a string that I get by getline() (more precisely I use a loop and getline to read a file line by line)
Let's say the line is 12|34|
Then I use strtok() to cut it down by substr = strtok(line, "|");
and store them into a string array with a loop, part[index] = substr;
So the part[0] here should be "12" and part[0] is "34"
I would like to use strtol, but I have checked that it can't be used on string literal, then I try following code.
char *temp = strdup(part[1]);
char **ptr;
long ret = strtol(temp, ptr, 10);
printf("%x\n", ret);
and when I read the second line, it causes segmentation fault. By how can I really use strtol to convert the string into integer
the issue is that ptr isn't initialised. So when strtol tries to write at the address ptr, it crashes (or undefined behaviour).
You have to pass the valid address of a pointer to store the last unprocessed char, like:
char *ptr;
long ret = strtol(temp, &ptr, 10);
&ptr is valid and points to the auto variable storage location to ptr
You're misusing strtol. It takes a char** because it intends to set the char* it points to, per the man page:
If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr.
By passing it an uninitialized char** you invoke undefined behavior when it tries to dereference it. Change the code to:
char *ptr; // Place to put the end ptr
long ret = strtol(temp, &ptr, 10); // Pass address of the location it can set
Alternatively, if you never use ptr, just do:
long ret = strtol(temp, NULL, 10); // Don't care about end ptr; strtol won't set on NULL
char **ptr;
long ret = strtol(temp, ptr, 10);
is wrong. ptr is not initialized and doesn't refer to anything useful.
The second parameter of strtol() must refer to the address of an actual char * value that stores the address of the first non-converted character. Per 7.22.1.3 The strtod, strtof, and strtold functions of the C standard:
A pointer to the final string is stored in the object pointed to by
endptr, provided that endptr is not a null pointer.
The proper code would be
char *endptr;
long ret = strtol(temp, &endptr, 10);
or
char *endptr;
char **ptr = &endptr;
long ret = strtol(temp, ptr, 10);
In this case, after the call to strtol() the value in endptr would be the address of the first character that wasn't converted to the resulting long value.
If you don't care what the first non-converted character is:
char **ptr;
long ret = strtol(temp, NULL, 10);
Function strtol(const char *str, char **str_end, int base); will dereference str_end and will do something like *str_end = one_after_end_of_parsed_long; So when you pass a pointer of type char**, which does not point to a valid pointer object that can be modified by strtol then, you'll yield undefined behaviour.
You'd rather write
char *ptr; // space for taking on a pointer value
long ret = strtol(temp, &ptr, 10);
or (not the preferred variant):
char **ptr = malloc(sizeof(char*));
long ret = strtol(temp, ptr, 10);
...
free(*ptr);
Here you already have separated your string.
So each string contains one long number. The second argument is used to know where the conversion stopped in the string.
If you don't need it, pass in NULL
char *temp = strdup(part[1]);
long ret = strtol(temp, NULL, 10);
printf("%lx\n", ret);
In addition printf for long number requires different format flags. Here lx for long hexadecimal.
There is absolutely no need to use strtok() at all, because strtol() sets the pointer pointed by the second parameter to point to the character following the number parsed.
A complete example program:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
char *line_ptr = NULL;
size_t line_max = 0;
ssize_t line_len;
long *number = NULL;
size_t numbers = 0;
size_t numbers_max = 0;
char *curr, *next, *ends;
long temp;
size_t i;
while (1) {
line_len = getline(&line_ptr, &line_max, stdin);
if (line_len < 1)
break;
curr = line_ptr;
ends = line_ptr + line_len;
numbers = 0;
while (1) {
/* Parse next long. */
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break;
if (next == curr)
break;
/* Need to grow number array first? */
if (numbers >= numbers_max) {
size_t temp_max = (numbers | 1023) + 1025 - 16;
long *temp_ptr;
temp_ptr = realloc(number, temp_max * sizeof number[0]);
if (!temp_ptr) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
numbers_max = temp_max;
number = temp_ptr;
}
/* Save parsed number. */
number[numbers++] = temp;
/* Skip trailing whitespace, */
curr = next;
while (curr < ends && (*curr == '\t' || *curr == '\n' || *curr == '\v' ||
*curr == '\f' || *curr == '\r' || *curr == ' '))
curr++;
/* Skip separator. */
if (*curr == '|')
curr++;
else
break; /* No separator, so that was the final number. */
}
printf("Parsed %zu longs:", numbers);
for (i = 0; i < numbers; i++)
printf(" %ld", number[i]);
printf("\n");
fflush(stdout);
}
if (ferror(in)) {
fprintf(stderr, "Error reading standard input.\n");
exit(EXIT_FAILURE);
}
free(line_ptr);
line_ptr = NULL;
line_max = 0;
free(number);
number = NULL;
numbers = 0;
numbers_max = 0;
return EXIT_SUCCESS;
}
Other than the available memory, this program has no limits wrt. line length or the amount of numbers it stores in the array. The growth policy for the number array is funky (just my style); feel free to replace it with anything you prefer. Just make sure temp_max is at least numbers + 1. Making it larger means you allocate more at once, and therefore do fewer "slow" realloc() calls.
The outer while loop iterates over lines read from standard input.
The inner while loop parses the longs from that line, separated by a pipe character |. strtol() ignores leading whitespace. In case there is whitespace between the number and the following pipe character, we need to explicitly skip that; you could also use just while (curr < ends && isspace(*curr)) curr++; for that.
If you want to collect all the longs into a single array, rather than per line, just omit the numbers = 0; before the inner while loop. (And move printing out the numbers after the outer while loop.)
The actual conversion,
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break; /* errno == ERANGE; number too large in magnitude! */
if (next == curr)
break; /* end of input, no number */
relies on the fact that if the number to be converted is too large in magnitude, strtol() will set errno = ERANGE and return LONG_MIN (if the number in the string was negative) or LONG_MAX (if positive). To detect that, we must set errno to zero first. If the string is empty (or there is a stray nul char, \0, in the line), strtol() will return 0 with next == curr.
Related
I did this something like this:
/* convert the argv[1] into Integer and store the result in key
* using library function: strtol() to do it */
char **flag = NULL;
key = strtol(argv[1], flag, 10);
// if argv[1] is not all digits
if (**flag != '\0')
{
printf("Usage: ./caesar key\n");
return 1;
}
But it throws a segmentation fault. I am not sure why.
In the C documentation, strtol is prototyped as long int strtol(const char *nptr, char **endptr, int base). Why am I getting a segmentation fault?
And when change some parts of the code to char *flag, strtol(argv[1], &flag, 10), and if (*flag != '\0'), everything works as expected.
I understand (sort of) how making the edits corrects the code. However, I do not know why the original code does not work. Does anyone have a clue?
You need to pass the address of your char * pointer, so that strtol can update it.
char *flag = NULL;
key = strtol(argv[1], &flag, 10);
// if argv[1] is not all digits
if (*flag != '\0')
After the call, the pointer will be modified to point to the end of the parsed region of the input string.
The man page wording is admittedly confusing here.
I do not know why the original code does not work? Does anyone have any clue?
Because here
char **flag = NULL;
flag is set to NULL, then here
key = strtol(argv[1], flag, 10);
flags's value (NULL) is passed to strtol(), which does not change flags's value in any way, it is NULL before and after the call to strtol().
and finally here
if (**flag != '\0')
flag, which has the value NULL, is dereferenced, which invokes undefined behaviour, which could lead to anything, which in your case is a crash.
Consider this simple function which multiplies a number by 2.
void Times2(int number, int *result)
{
*result = number * 2;
}
You should call it like this:
int result;
Times2(3, &result);
printf("%d\n", result);
and the expected output would be 4.
But if you call it like this (which is basically what you're doing when you call strtol in your buggy code):
int *result = NULL;
Times2(3, result);
printf("%d\n", result);
your program will most likely end up with a segmentation fault, because in the Times2 function you dereference a NULL pointer.
Why second argument not working with strol?
char **flag = NULL;
key = strtol(argv[1], flag, 10);
It is working. Passing is a null pointer is fine, but flag will not get changed in the calling code.
When the 2nd argument is a null pointer, nothing is updated about where the conversion ended.
Later code: *flag in if (**flag != '\0') is bad (undefined behavior) as that attempts to de-reference a null pointer.
Instead pass the address of a known pointer:
//char **flag = NULL;
char *flag;
//key = strtol(argv[1], flag, 10);
key = strtol(argv[1], &flag, 10);
// if (**flag != '\0')
if (*flag != '\0')
Following is a complete test conversion of a char* to an int.
#include <stdlib.h>
#incluse <ctype.h>
// Return error flag
int my_strtol(long *destination, const char *s) {
if (destination == NULL) {
return 1;
}
if (s == NULL) {
*destination = 0;
return 1;
}
char *endptr;
errno = 0;
*destination = strtol(s, &endptr, 0);
// If no conversion or out-of-range
if (s == endptr || errno == ERANGE) {
return 1;
}
// I like to tolerate trailing white-space.
while (isspace(*(const unsigned char *) endptr)) {
endptr++;
}
return *endptr != '\0';
}
My str_split function returns (or at least I think it does) a char** - so a list of strings essentially. It takes a string parameter, a char delimiter to split the string on, and a pointer to an int to place the number of strings detected.
The way I did it, which may be highly inefficient, is to make a buffer of x length (x = length of string), then copy element of string until we reach delimiter, or '\0' character. Then it copies the buffer to the char**, which is what we are returning (and has been malloced earlier, and can be freed from main()), then clears the buffer and repeats.
Although the algorithm may be iffy, the logic is definitely sound as my debug code (the _D) shows it's being copied correctly. The part I'm stuck on is when I make a char** in main, set it equal to my function. It doesn't return null, crash the program, or throw any errors, but it doesn't quite seem to work either. I'm assuming this is what is meant be the term Undefined Behavior.
Anyhow, after a lot of thinking (I'm new to all this) I tried something else, which you will see in the code, currently commented out. When I use malloc to copy the buffer to a new string, and pass that copy to aforementioned char**, it seems to work perfectly. HOWEVER, this creates an obvious memory leak as I can't free it later... so I'm lost.
When I did some research I found this post, which follows the idea of my code almost exactly and works, meaning there isn't an inherent problem with the format (return value, parameters, etc) of my str_split function. YET his only has 1 malloc, for the char**, and works just fine.
Below is my code. I've been trying to figure this out and it's scrambling my brain, so I'd really appreciate help!! Sorry in advance for the 'i', 'b', 'c' it's a bit convoluted I know.
Edit: should mention that with the following code,
ret[c] = buffer;
printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
it does indeed print correctly. It's only when I call the function from main that it gets weird. I'm guessing it's because it's out of scope ?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEBUG
#ifdef DEBUG
#define _D if (1)
#else
#define _D if (0)
#endif
char **str_split(char[], char, int*);
int count_char(char[], char);
int main(void) {
int num_strings = 0;
char **result = str_split("Helo_World_poopy_pants", '_', &num_strings);
if (result == NULL) {
printf("result is NULL\n");
return 0;
}
if (num_strings > 0) {
for (int i = 0; i < num_strings; i++) {
printf("\"%s\" \n", result[i]);
}
}
free(result);
return 0;
}
char **str_split(char string[], char delim, int *num_strings) {
int num_delim = count_char(string, delim);
*num_strings = num_delim + 1;
if (*num_strings < 2) {
return NULL;
}
//return value
char **ret = malloc((*num_strings) * sizeof(char*));
if (ret == NULL) {
_D printf("ret is null.\n");
return NULL;
}
int slen = strlen(string);
char buffer[slen];
/* b is the buffer index, c is the index for **ret */
int b = 0, c = 0;
for (int i = 0; i < slen + 1; i++) {
char cur = string[i];
if (cur == delim || cur == '\0') {
_D printf("Copying content of buffer to ret[%i]\n", c);
//char *tmp = malloc(sizeof(char) * slen + 1);
//strcpy(tmp, buffer);
//ret[c] = tmp;
ret[c] = buffer;
_D printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
//free(tmp);
c++;
b = 0;
continue;
}
//otherwise
_D printf("{%i} Copying char[%c] to index [%i] of buffer\n", c, cur, b);
buffer[b] = cur;
buffer[b+1] = '\0'; /* extend the null char */
b++;
_D printf("Buffer is now equal to: \"%s\"\n", buffer);
}
return ret;
}
int count_char(char base[], char c) {
int count = 0;
int i = 0;
while (base[i] != '\0') {
if (base[i++] == c) {
count++;
}
}
_D printf("Found %i occurence(s) of '%c'\n", count, c);
return count;
}
You are storing pointers to a buffer that exists on the stack. Using those pointers after returning from the function results in undefined behavior.
To get around this requires one of the following:
Allow the function to modify the input string (i.e. replace delimiters with null-terminator characters) and return pointers into it. The caller must be aware that this can happen. Note that supplying a string literal as you are doing here is illegal in C, so you would instead need to do:
char my_string[] = "Helo_World_poopy_pants";
char **result = str_split(my_string, '_', &num_strings);
In this case, the function should also make it clear that a string literal is not acceptable input, and define its first parameter as const char* string (instead of char string[]).
Allow the function to make a copy of the string and then modify the copy. You have expressed concerns about leaking this memory, but that concern is mostly to do with your program's design rather than a necessity.
It's perfectly valid to duplicate each string individually and then clean them all up later. The main issue is that it's inconvenient, and also slightly pointless.
Let's address the second point. You have several options, but if you insist that the result be easily cleaned-up with a call to free, then try this strategy:
When you allocate the pointer array, also make it large enough to hold a copy of the string:
// Allocate storage for `num_strings` pointers, plus a copy of the original string,
// then copy the string into memory immediately following the pointer storage.
char **ret = malloc((*num_strings) * sizeof(char*) + strlen(string) + 1);
char *buffer = (char*)&ret[*num_strings];
strcpy(buffer, string);
Now, do all your string operations on buffer. For example:
// Extract all delimited substrings. Here, buffer will always point at the
// current substring, and p will search for the delimiter. Once found,
// the substring is terminated, its pointer appended to the substring array,
// and then buffer is pointed at the next substring, if any.
int c = 0;
for(char *p = buffer; *buffer; ++p)
{
if (*p == delim || !*p) {
char *next = p;
if (*p) {
*p = '\0';
++next;
}
ret[c++] = buffer;
buffer = next;
}
}
When you need to clean up, it's just a single call to free, because everything was stored together.
The string pointers you store into the res with ret[c] = buffer; array point to an automatic array that goes out of scope when the function returns. The code subsequently has undefined behavior. You should allocate these strings with strdup().
Note also that it might not be appropriate to return NULL when the string does not contain a separator. Why not return an array with a single string?
Here is a simpler implementation:
#include <stdlib.h>
char **str_split(const char *string, char delim, int *num_strings) {
int i, n, from, to;
char **res;
for (n = 1, i = 0; string[i]; i++)
n += (string[i] == delim);
*num_strings = 0;
res = malloc(sizeof(*res) * n);
if (res == NULL)
return NULL;
for (i = from = to = 0;; from = to + 1) {
for (to = from; string[to] != delim && string[to] != '\0'; to++)
continue;
res[i] = malloc(to - from + 1);
if (res[i] == NULL) {
/* allocation failure: free memory allocated so far */
while (i > 0)
free(res[--i]);
free(res);
return NULL;
}
memcpy(res[i], string + from, to - from);
res[i][to - from] = '\0';
i++;
if (string[to] == '\0')
break;
}
*num_strings = n;
return res;
}
"01110011 01100001 01100100 " This would be one line having the 8 bits separated by spaces in the file.
Currently I have:
if (fr != NULL) //see if file opens or not
{
char chter[500]; //char to get string from text
char *ptr; //pointer to char
//char store[100][32];
fgets(chter, 1000, fr); //gets text from file
printf("%s", chter); //prints current text to cmd from textfile
puts("\n");
for (int i = 0; i < 1; i++)
{
li1 = strtol(chter, &ptr, 2); //convert 1st binary set to alphabet
printf("%c", li1); //Not sure how to get the rest of the sets from here on
}
puts("\n");
fclose(fr);
}
I was thinking of using a 2d array to have multiple strings stored, however I'm stuck. As I don't know how to seperatly extract the binary bits from the other string.
After each call to strtol, ptr will point to the first byte in the string that's not part of the parsed integer. That will be your starting point on the next iteration. Since fgets returns a string with a newline, loop until ptr points to either the newline or the null byte at the end of the string.
char *ptr, *tmp;
tmp = chter;
do
{
li1 = strtol(tmp, &ptr, 2);
printf("%c", li1);
tmp = ptr;
} while ((*ptr != '\n') && (*ptr != '\0'));
The primary problem you have is failing to use separate pointers for strtol (ptr, &endptr, 2) which would then allow you to work through all values held in chter. Secondly, you risk Undefined Behavior by potentially reading 1000 characters where chter will only hold 500. Thirdly, you need to properly validate the results of the strtol conversion by checking (1) whether digits were converted; and (2) whether overflow/undeflow occurred by checking errno.
Putting those together, you could do:
#include <errno.h>
...
if (fr != NULL) //see if file opens or not
{
char chter[500]; //char to get string from text
char *ptr = chter; /* assign chter to the pointer */
char *endptr; /* separate end-pointer for strtol */
fgets(chter, sizeof chter, fr); /* properly limit read size */
printf("%s", chter); //prints current text to cmd from textfile
puts("\n");
errno = 0; /* set errno zero */
while (*ptr && *ptr != '\n') /* loop over all values */
{
long li1 = strtol (ptr, &endptr, 2); /* convert to long */
if (ptr == endptr) { /* validate digits converted */
fputs ("error: no digits converted.\n", stderr);
/* handle error */
break;
}
else if (errno) { /* validate no over/underflow */
perror ("strtol-conversion_failed");
/* handle error */
break;
}
printf (" %ld", li1); /* output value */
ptr = endptr; /* advance pointer */
}
// puts("\n");
putchar ('\n'); /* use putchar for single-char output */
fclose(fr);
}
(note: not compiled, so drop a note if you have problems)
It would be better If I show you guys an example of what my program is supposed to do.
Input:
3
Double Double End
Triple Double End
Quadruple Double Triple End
Output:
4
6
24
So, the first sentence Double Double means 2*2 and Triple Double means 3*2 and so on.
The word End signifies the end of the string.
It looks very simple, but I have no idea how to work with strings and give them a value and continue on from there.
Here is all I have done so far:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num_of_orders,i,j;
char orders[25];
char str1[25] = "Double";
char str2[25] = "Triple";
char str3[25] = "Quadruple";
scanf("%d", &num_of_orders);
for (i=0; i<num_of_orders+1; i++){
scanf("%s", orders);
}
return 0;
}
There are a number of ways to approach this problem, as indicated by the variety of answers. There is often no one right answer for how to approach a problem in C. The standard library provides a variety of tools that allow you to craft a number of solutions to just about any problem. As long as the code is correct and protects against error, then the choice of which approach to take largely boils down to a question of efficiency. For small bits of example code, that is rarely a consideration.
One approach to take is to recognize that you do not need the first line in your data file (except to read it/discard it to move the file-position-indicator to the start of the first line containing data.)
This allows you to simply use a line-oriented input function (fgets or getline) to read the remaining lines in the file. strtok then provides a simple way to split each line into words (remembering to strip the '\n' or discard the last word in each line). Then it is a small matter of using strcmp to compare each word and multiply by the correct amount. Finally, output the product of the multiplication.
Here is one slightly different approach to the problem. The program will read from the filename given as the first argument (or from stdin by default):
#include <stdio.h>
#include <string.h>
enum { MAXC = 64 };
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
char *delims = " \n"; /* delimiters */
int idx = 0; /* line index */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file pointer */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
if (!idx++) continue; /* discard line 1 */
char *p = buf;
size_t len = strlen (p); /* get length */
int prod = 1;
if (len && buf[len-1] == '\n') /* check for '\n' */
buf[--len] = 0; /* remove newline */
printf (" %s", buf); /* output buf before strtok */
/* tokenize line/separate on delims */
for (p = strtok (p, delims); p; p = strtok (NULL, delims))
{ /* make comparson and multiply product */
if (strcmp (p, "Double") == 0) prod *= 2;
if (strcmp (p, "Triple") == 0) prod *= 3;
if (strcmp (p, "Quadruple") == 0) prod *= 4;
}
printf (" = %d\n", prod); /* output product */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Use/Output
$ ./bin/dbltrpl <../dat/dbltrpl.txt
Double Double End = 4
Triple Double End = 6
Quadruple Double Triple End = 24
Look it over and let me know if you have questions.
When it comes to reading the input, you can use strtok with a " " as a parameter to delimite the words you're reading from the input. This is a function filling all of the words read on the input into an array of strings:
PARAMETERS:
char **words: array of strings where you will store all of the words read in the input
char *input: the input you read (i.e. "Double Double end")
char *s: the delimiter you'll use to read words in the input (i.e. " ", "\n")
void getWords(char **words, char *input, char *s){
*words = strtok(str, s);
while(*words){
words++;
*words = strtok(NULL, s);
}
words++;
*words=NULL; //last element will point to NULL
}
Once you have read the words from the input, and filled them inside an array of strings, you could do something like this to calculate the output:
int calculate(char **words){
int result = 1;
while(*words){
if (strcmp(*words, "Quadruple") == 0){
result *= 4;
}else if (strcmp(*words, "Triple") == 0){
result *= 3;
}else if (strcmp(*words, "Double") == 0){
result *= 2;
}else if (strcmp(*words, "End") == 0){
return result;
}
words++;
}
}
Note that you need to correctly initialize the parameters you're passing before calling those functions. Otherwise, it may cause a Segmentation Fault.
You will have to use the methods from the string.h library, such as: strcmp(to compare two strings), strcpy(to copy one string to another) etc. which are generally used when dealing with strings manipulation in c.
Since, we do not know the size of the results array at compile time, we will have to allocate memory to it dynamically. For this purpose I have used malloc and free.
Here is the code to do that:
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
int main()
{
int num_of_orders, i, j;
char orders[25];
char str1[25];
strcpy(str1,"Double");
char str2[25];
strcpy(str2,"Triple");
char str3[25];
strcpy(str3,"Quadruple");
scanf("%d", &num_of_orders);
getchar();
int *results = malloc(num_of_orders*sizeof(int));
for (i=0; i < num_of_orders; i++)
{
results[i] = 1;
strcpy(orders,"");
while(strcmp(orders,"End") != 0)
{
scanf("%s", orders);
getchar();
if(strcmp(orders,str1)==0)
results[i] *= 2;
else if(strcmp(orders,str2) == 0)
results[i] *= 3;
else if(strcmp(orders,str3)==0)
results[i] *= 4;
}
}
for(i = 0; i < num_of_orders; i++)
printf("%d\n", results[i]);
free(results);
return 0;
}
Note: This program uses strcmp, which does case-sensitive comparison. If you want case-insensitive comparison, then use strcasecmp instead.
Don't forget the fact that the multiplication of integers is commutative:
#include <stdio.h>
#include <string.h>
int main(void)
{
int num_of_orders, i;
char orders[25];
int result;
char *ptr;
scanf("%d", &num_of_orders);
getchar(); // To comsume '\n'
for (i = 0; i < num_of_orders; i++)
{
fgets(orders, sizeof orders, stdin);
result = 1;
ptr = orders;
while(ptr = strstr(ptr, "Double"))
{
result *= 2;
ptr++;
}
ptr = orders;
while(ptr = strstr(ptr, "Triple"))
{
result *= 3;
ptr++;
}
ptr = orders;
while(ptr = strstr(ptr, "Quadruple"))
{
result *= 4;
ptr++;
}
printf("%d\n", result);
}
return 0;
}
What a trivial approach!
Note that strtok() is destructive, namely it will modify order, which can cause some problems if you want to use it later. Also, I think programs using strtok() are less readable. So it might be better to avoid it when possible.
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 ) );
}