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 ) );
}
Related
I have a problem I am trying to solve. I have an array of string and integers and I want to convert only the integers(163) to actual integers in C.
I have managed to locate my desired numbers (163) array location but i am unsure how to convert them to numbers. I have tried to use strtol, atoi and strtoumax but I havent been succesful.
I have added my code below.
char busy[30] = {"this is; it was; 163; 234;;"};
int tag = 0;
int location = 0;
for (int i = sizeof(busy); i > 0; i--) {
printf("%c\n", busy[i]);
if (busy[i] == ';') {
tag = tag+1;
printf("tag is %i \n", tag);
}
if (tag == 4) {
printf("for loop is %i\n", i);
location = i;
break;
}
}
location = location+1;
int loca_saved = 0;
int loca_finish = 0;
int tag1 = 0;
while (busy[location] != ';') {
if (busy[location] == ' ' && busy[location - 1] == ';') {
//printf("not printing whitespace between semicolon and characters\n");
location++;
}
else {
if (tag1 == 0) {
tag1 = tag1+1;
loca_saved = location; //this is to tell me the array location for the first char
}
if (busy[location + 1] == ';') {
loca_finish = location; //this is to tell me the array location for the last char
}
putchar(busy[location]); //this is to print my desired characters(163)
location++;
}
}
strspn and strcspn can be used to parse a string.
Instead of sscanf, strtol could be used to get the number.
#include <stdio.h>
#include <string.h>
int main ( void) {
char busy[] = "this is; it was; 163; 234;";
char *dlm = "0123456789";
char *parse = busy;
int number = 0;
while ( *parse) {
parse += strcspn ( parse, dlm);//count to next delimiter
if ( 1 == sscanf ( parse, "%d", &number)) {
printf ( "%d\n", number);
}
parse += strspn ( parse, dlm);//skip delimeters
}
return 0;
}
Parsing could also work off the semicolon.
Some fields will not have an integer.
#include <stdio.h>
#include <string.h>
int main ( void) {
char busy[] = "this is; it was; 163; 234;";
char *dlm = ";";
char *parse = busy;
int number = 0;
while ( *parse) {
if ( 1 == sscanf ( parse, "%d", &number)) {
printf ( "%d\n", number);
}
else {
printf ( "could not parse integer\n");
}
parse += strcspn ( parse, dlm);//count to next delimiter
parse += strspn ( parse, dlm);//skip delimeters
//the above line will skip all consecutive delimiters
//to process each delimiter use the line below
//++parse;//skip one delimiter
}
return 0;
}
Since you have a modifiable string you can use strtok to separate it into substrings based on the ; delimiter. This code does that in in case a substring is a number, it gets printed.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (void)
{
char str[30] = {"this is; it was; 163; 234;;"};
for(char* p=strtok(str, ";"); p!=NULL; p=strtok(NULL,str))
{
char* endptr;
int i = strtol(p,&endptr,10);
if(endptr != p)
{
printf("%d\n", i);
}
}
}
Output:
163
234
strtol sets the "endptr" parameter to point at the beginning of the string in case it fails, so we can use that to determine if a substring was a number or not.
It all depends on the exact grammar that you would like to parse. Note, for example, that negative integers start with a - character, and that there has to be a maximum integer value. Since you are working quite "low level", you may want to perform the conversion to an integer while you are reading the string, e.g.:
#include <ctype.h>
#include <stdio.h>
int main(void)
{
char busy[30] = {"this is; it was; 163; 234;;"};
for (char *p = busy; *p; ++p) // Loop over the string
if (isdigit(*p)) // Found first digit of an unsigned integer
{
unsigned n = (unsigned) (*p - '0'); // Store the value of the first digit in 'n'
// While reading the number, shift the digits to the appropriate positions
while (isdigit(*++p))
n = n * 10U + (unsigned) (*p - '0');
printf("%u\n", n); // We have finished parsing the integer, print it
}
}
Using atoi can also work, but it will require two passes over each integer: one in atoi itself and another one to move past the integer in the string you're parsing.
The function strtol allows you to pass a pointer to your pointer which is set to one place after the read integer, which avoids the issue. You can use this to advance the pointer. In this code example, the pointer is either advanced manually or by strtol:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char busy[30] = {"this is; it was; 163; 234;;"};
for (char *p = busy; *p; )
if (isdigit(*p))
printf("%ld\n", strtol(p, &p, 10));
else
++p;
}
Another alternative, without the use of isdigit:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char busy[30] = {"this is; it was; 163; 234;;"};
for (char *p = busy; *p; )
{
char *end;
long n = strtol(p, &end, 10);
if (p != end) // Pointer was advanced, so an integer was read
{
printf("%ld\n", n);
p = end;
}
else // Pointer was not advanced, do so manually
++p;
}
}
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.
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.
Is there a proper way of comparing two char-arrays if they aren't equal by length?
How to check which character isn't equal?
strcmp seems to give me only bigger or lesser number, not the position of unequal character.
For example, strings:
/home/jjjj/ and
/home/jjjj/kkkk/asdasd
Should return 12
Using strlen() and strstr() you can achieves this in a two-step approach:
#include <string.h>
#include <stdio.h>
...
char str1[] = "this is a long string";
char str2[] = "long";
{
char * ss = NULL;
char * sg = NULL;
size_t size1 = strlen(str1)
size_t size2 = strlen(str2);
size_t size_ss = 0;
/* step 1: determine which of the two strings tobe compared it the smaller/greater one. */
if (size1 > size2)
{
size_ss = size2;
ss = str2;
sg = str1;
}
else
{
size_ss = size1;
ss = str1;
sg = str2;
}
/* step 2: find out where the smaller string is located in the greater one, if ever... */
{
char * p = strstr(sg, ss);
if (p)
{
printf("'%s' is the same as '%s' from character %zu to character %zu.\n",
sg, ss, p - sg, p - sg + size_ss);
}
else
{
/* printf("The strings are 100%% differently!\n"); */ /* changed as per Jonathan's comment. */
printf("'%s' does not appear in '%s'.\n", ss, sg);
}
}
}
This solution does not take into account that the shorter string could appear more than once in the longer string. It always notifies about the first occurrence.
There isn't a standard C function that returns the first point of discrepancy between two strings.
It wouldn't be hard to create one; take a version of strcmp() from a text book and modify it so that returns the offset of the strings at the point where the result is 'interesting'. If the strings are equal, that will be the offset of the null terminator ('\0'); otherwise, it will be the offset where the two strings are different.
Maybe something like this:
const char* strcmp_plusplus (const char* str1, const char* str2)
{
const char* result = NULL; // return NULL if equal
while(*str1 != '\0')
{
if(*str1 != *str2)
{
result = str1; // point at where in str1 they are different
break;
}
str1++;
str2++;
}
return result;
}
Note that we won't have to check if str2 is \0, because the C standard allows us to read one element beyond an array without invoking undefined behavior. If str2 ends before str1, the function will return a pointer to str1's null termination.
This function attempts to do it all at once. Since a function can only return one value, one of the resulting values (the difference) has to be passed back to the caller via a pointer to it.
#include <stdio.h>
size_t lead_cmp( const char * one, const char * two, int *result);
size_t lead_cmp( const char * one, const char * two, int *result)
{
size_t pos;
for(pos=0; one[pos] && two[pos]; pos++) {
if (one[pos] != two[pos]) break;
}
*result = one[pos] - two[pos];
return pos;
}
int main(int argc, char **argv)
{
size_t len;
int diff;
len = lead_cmp (argv[1], argv[2], &diff );
printf( "Pos=%zu, Rc=%d\n", len, diff);
return 0;
}
Result:
$ ./a.out /home/jjjj/ /home/jjjj/kkkk/
Pos=11, Rc=-107
$
The found position is 11, not 12, since C uses 0-based indexing.
It returns the number of matching characters: the length of the common prefix.
I have a string "14 22 33 48". I need to insert each of the values in the string into the respective location in the array:
int matrix[5];
so that
matrix[0] = 14;
matrix[1] = 22;
matrix[2] = 33;
matrix[3] = 48;
How do I do this?
You can use sscanf:
sscanf(val,
"%d %d %d %d",
&matrix[0],
&matrix[1],
&matrix[2],
&matrix[3]
);
const char *p = input;
int i = 0, len;
while (i < sizeof(matrix)/sizeof(matrix[0])
&& sscanf(p, "%d%n", &matrix[i], &len) > 0) {
p += len;
i++;
}
For extra credit, dynamically reallocate matrix to make is as large as it needs to be ...
I would recommend strtol, which provides better error handling than atoi or sscanf. This version will stop if it encounters a bad character or runs out of space in the array. Of course if there aren't enough integers in the string to fill the array, then the remainder of it will remain uninitialized.
#include <stdio.h>
#include <stdlib.h>
#define ARRAY_SIZE 5
int main(int argc, char *argv[])
{
/* string to convert */
const char * str = "14 22 33 48";
/* array to place converted integers into */
int array[ARRAY_SIZE];
/* this variable will keep track of the next index into the array */
size_t index = 0;
/* this will keep track of the next character to convert */
/* unfortunately, strtol wants a pointer to non-const */
char * pos = (char *)str;
/* keep going until pos points to the terminating null character */
while (*pos && index < ARRAY_SIZE)
{
long value = strtol(pos, &pos, 10);
if (*pos != ' ' && *pos != '\0')
{
fprintf(stderr, "Invalid character at %s\n", pos);
break;
}
array[index] = (int)value;
index++;
}
}
Robust string processing in C is never simple.
Here's a procedure that popped immediately to mind for me. Use strtok() to break the string into individual tokens, then convert each token to an integer value using strtol().
Things to be aware of: strtok() modifies its input (writes 0 over the delimiters). This means we have to pass it a writable string. In the example below, I create a buffer dynamically to hold the input string, and then pass that dynamic buffer to strtok(). This guarantees that strtok() is operating on writable memory, and as a bonus, we preserve our input in case we need it later.
Also, strtol() allows us to catch badly formed input; the second argument will point to the first character in the string that wasn't converted. If that character is anything other than whitespace or 0, then the string was not a valid integer, and we can reject it before assigning it to our target array.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/**
* Convert a space-delimited string of integers to an array of corresponding
* integer values.
*
* #param str [in] -- input string
* #param arr [in] -- destination array
* #param arrSize [in] -- max number of elements in destination array
* #param count [out] -- number of elements assigned to destination array
*/
void stringToIntList(char *str, int *arr, size_t arrSize, size_t *count)
{
/**
* The token variable holds the next token read from the input string
*/
char *token;
/**
* Make a copy of the input string since we are going to use strtok(),
* which modifies its input.
*/
char *localStr = malloc(strlen(str) + 1);
if (!localStr)
{
/**
* malloc() failed; we're going to treat this as a fatal error
*/
exit(-1);
}
strcpy(localStr, str);
/**
* Initialize our output
*/
*count = 0;
/**
* Retrieve the first token from the input string.
*/
token = strtok(localStr, " ");
while (token && *count < arrSize)
{
char *chk;
int val = (int) strtol(token, &chk, 10);
if (isspace(*chk) || *chk == 0)
{
arr[(*count)++] = val;
}
else
{
printf("\"%s\" is not a valid integer\n", token);
}
/**
* Get the next token
*/
token = strtok(NULL, " ");
}
/**
* We're done with the dynamic buffer at this point.
*/
free(localStr);
}
int main(void)
{
int values[5];
char *str = "14 22 33 48 5q";
size_t converted;
stringToIntList(str, values, sizeof values / sizeof values[0], &converted);
if (converted > 0)
{
size_t i;
printf("Converted %lu items from \"%s\":\n", (unsigned long) converted, str);
for (i = 0; i < converted; i++)
printf("arr[%lu] = %d\n", (unsigned long) i, arr[i]);
}
else
{
printf("Could not convert any of \"%s\" to equivalent integer values\n", str);
}
return 0;
}
the better way to convert a string of ints into actual ints and store them in an array.
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int atoi1(char*,int,int);
void main(){
char arr1[1000];
int array[1000];
gets(arr1);
int i=0,j,k=0;
while(i<strlen(arr1)){
j=i+1;
while(*(arr1+j)!=' ')
j++;
*(array+k)=atoi1(arr1,i,j-1);
i=j+1;k++;
}
}
int atoi1(char *arr1,int start,int end){
char arr2[1000];int j=0,i;
for(i=start;i<end+1;i++){
*(arr2+j)=*(arr1+i);
j++;
}
*(arr2+j)='\0';
return atoi(arr2);
}