How to input numbers into a vector until I press enter - arrays

I want to have a versatile code for which I don't have to input the number of numbers.
Instead of doing something like this
#include <stdio.h>
void main(){
int n,a[100];
scanf("%d", &n);
for(int i=0;i<n;i++)
scanf("%d ", a[i]); }
Here I would first input n and then input n numbers in my array.
To give a more concrete example, I want to code the union and reunion of two sets. Instead of thinking of the number of elements of each sets, I would just enter them as I please without limiting to the "n" I mentioned above.
Is there any way in which I could skip inputting n? And how would I declare my array as to not lose memory.

You'll need to use a pointer to a block of memory which you keep increasing in size each time you go get a new number:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main()
{
int *a = NULL;
int n = 0;
char str[10000];
while(fgets(str, sizeof str, stdin) != NULL &&
strlen(str) > 1)
{
n += 1;
a = realloc(a, n * sizeof(int));
a[n-1] = atoi(str);
}
for(int i = 0 ; i < n ; ++i)
printf("a[%d] = %d\n", i, a[i]);
}
OnlineGDB here

You need to grow your memory buffer dynamically. Here's a method that reads the first array as space separated integers, and looks for a semi-colon in the input to tell when the second array starts:
/* Read two sets of integers */
#include <stdio.h>
#include <stddef.h>
#include <ctype.h>
#include <stdlib.h>
void * xrealloc(void *buf, size_t nmemb, size_t size, void *iterator);
struct buffer {
int *start;
int *end;
size_t cap;
};
void push(int c, struct buffer *);
int
main(void)
{
int c;
struct buffer a = {0};
struct buffer b = {0};
while( scanf("%d", &c) == 1 ){
push(c, &a);
}
if( (c = getchar()) != ';' ){
fprintf(stderr, "Unexpected input\n");
return EXIT_FAILURE;
}
while( scanf("%d", &c) == 1 ){
push(c, &b);
}
printf("set a: ");
for( int *p = a.start; p < a.end; p++ ){
printf("%s%d", p > a.start ? ", " : "", *p);
}
printf("\nset b: ");
for( int *p = b.start; p < b.end; p++ ){
printf("%s%d", p > b.start ? ", " : "", *p);
}
putchar('\n');
return 0;
}
void
push(int c, struct buffer *b)
{
if( b->start == NULL || b->end >= b->start + b->cap ) {
b->start = xrealloc(b->start, b->cap += BUFSIZ, 1, &b->end);
}
*b->end++ = c;
}
void *
xrealloc(void *buf, size_t num, size_t siz, void *endvp)
{
void **endp = endvp;
ptrdiff_t offset = buf && endp && *endp ? *endp - buf : 0;
buf = realloc(buf, num * siz);
if( buf == NULL ){
perror("realloc");
exit(EXIT_FAILURE);
}
if( endp != NULL ){
*endp = buf + offset;
}
return buf;
}
A simple run looks like:
$ echo '1 3 5; 3 5 9 3 5' | ./a.out
set a: 1, 3, 5
set b: 3, 5, 9, 3, 5

The usual way to do this is to use fgets to read a line, then use sscanf to parse that line. Something like:
char buffer[2048];
int a[100], n;
if (fgets(buffer, sizeof(buffer), stdin)) {
n = 0;
char *p = buffer;
int len;
while(n < 100 && sscanf("%d%n", &a[n], &len) > 0) {
++n;
p += len; } }
You might also want to use malloc/realloc for the array a so that it can start small and grow as large as you need based on the input, but that is a different question (with many answers here).

Related

Trying to make QuizMaker in C, but some steps are ignored with !X

Basically I'm trying to make QuizMaker by asking user how many questions they want, then make a string of an array. I'm new to C, so I might be missing some little details. Please look at the screenshot of console which I included.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 4096
int input(char *str, int n);
int main() {
int a, i, length = 0;
printf("How many questions do you want?\n");
scanf("%d", &a);
char *strarr[a];
char buffer[BUFFER_SIZE]; //string holder
while (getchar() != '\n'); //in case there is \n, flush it
for(i = 0; i < a; i++) {
printf("Question number #%d:\n", i + 1);
length = input(buffer, BUFFER_SIZE);
//input method returns number of chars we've entered
strarr[i] = malloc((length) * sizeof(char));
//allocating memory for each pointers to array of chars
strcpy(strarr[i], buffer);
//copy the string you've just created to an array of strings
}
//printing results
printf("_____________\n");
for(i = 0; i < a; i++) {
printf("%s\n", strarr[i]);
}
return 0;
}
int input(char *str, int n) {
int ch, i = 0;
while ((ch = getchar()) != '\n') {
if (i < n)
str[i++] = ch;
str[i] = '\0';
}
return i;
}
There are some problems in the code:
the test if (i < n) is not strict enough: you should stop storing characters to the destination array before it is full to save space for the null terminator.
you must allocate one extra byte for the null terminator: malloc((length + 1) * sizeof(char)) or just malloc(length + 1) as sizeof(char) is 1 by definition.
you should test for EOF in addition to '\n' in the reading loops.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 4096
int input(char *str, int n);
int main() {
int a, c, i, length = 0;
printf("How many questions do you want?\n");
if (scanf("%d", &a) != 1 || a <= 0) {
fprintf(stderr, "invalid input\n");
return 1;
}
char *strarr[a];
char buffer[BUFFER_SIZE]; //string holder
// flush the rest of the input line
while ((c = getchar()) != EOF && c != '\n')
continue;
for (i = 0; i < a; i++) {
printf("Question number #%d:\n", i + 1);
//input method returns number of chars we've entered
length = input(buffer, BUFFER_SIZE);
//allocating memory for each pointers to array of chars
strarr[i] = malloc((length + 1) * sizeof(char));
if (strarr[i] == NULL) {
fprintf(stderr, "allocation error\n");
a = i;
break;
}
//copy the string you've just created to an array of strings
strcpy(strarr[i], buffer);
}
//printing results
printf("_____________\n");
for (i = 0; i < a; i++) {
printf("%s\n", strarr[i]);
}
return 0;
}
int input(char *str, int n) {
int ch, i = 0;
while ((ch = getchar()) != EOF && ch != '\n') {
if (i + 1 < n)
str[i++] = ch;
}
if (i < n)
str[i] = '\0';
return i;
}
Also note that you can use strdup() to allocate and copy the string in a single call:
for (i = 0; i < a; i++) {
printf("Question number #%d:\n", i + 1);
input(buffer, BUFFER_SIZE);
/* allocate a copy of the string */
strarr[i] = strdup(buffer);
if (strarr[i] == NULL) {
fprintf(stderr, "allocation error\n");
a = i;
break;
}
}

How could I correct my code? The main goal of my code is initializing new string from another one, with just copying n first letters in C

Can you give me advice to correct my code? It should initialize new_string from another string, with copying the n first letters from this string. Output should be string. But my code prints nothing. How can I fix it?
Here is my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char str[99];
int n, i, len;
printf("Enter a string:");
scanf("%s", str);
printf("enter n:");
scanf("%i", &n);
if (n > len) {
n = len;
}
char *new_string = malloc(n + 1);
for (int i = 0; i < n; i++) {
new_string[i] = str[i];
}
new_string[i] = '\0';
printf("STring:%s", new_string);
return 0;
}
You could use strncpy as suggested by d.j.yotta in the comments:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void)
{
char str[100];
printf("Enter string: ");
/*
* take spaces into account (optional),
* prevent buffer overflow and
* check return value of `scanf`
*/
if (scanf("%99[^\n]", str) != 1) {
fprintf(stderr, "Error: invalid input\n");
return EXIT_FAILURE;
}
int n;
printf("Enter index: ");
/* check return value of `scanf` */
if(scanf("%d", &n) != 1) {
fprintf(stderr, "Error: invalid input\n");
return EXIT_FAILURE;
}
/* initialize `len` */
int len = strlen(str);
if (n > len)
n = len;
char *new_str = malloc(n + 1);
strncpy(new_str, str, n);
printf("New string: %s\n", new_str);
return EXIT_SUCCESS;
}
Or you could make the changes explained in the following code's comments:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void)
{
char str[99];
printf("Enter string: ");
/*
* take spaces into account (optional),
* prevent buffer overflow and
* check return value of `scanf`
*/
if (scanf("%99[^\n]", str) != 1) {
fprintf(stderr, "Error: invalid input\n");
return EXIT_FAILURE;
}
int n;
printf("Enter index: ");
/* check return value of `scanf` */
if(scanf("%d", &n) != 1) {
fprintf(stderr, "Error: invalid input\n");
return EXIT_FAILURE;
}
/* initialize `len` */
int len = strlen(str);
if (n > len)
n = len;
char *new_str = malloc(n + 1);
for (int i = 0; i < n; ++i)
new_str[i] = str[i];
/* you don't need `i` here */
new_str[n + 1]= '\0';
printf("New string: %s\n", new_str);
return EXIT_SUCCESS;
}
The problem in your code is not easy to spot: you define two distinct local variables named i: one in the scope of the body of the main function in int n,i, len; and another one in the scope of the for loop: for (int i = 0; i < n; i++) { new_string[i] = str[i]; }. The latter one goes out of scope at the end of the loop and the first one is used to set the null terminator new_string[i]='\0';. This i variable is uninitialized so the statement has undefined behavior and new_string is not properly null terminated.
There are other problems:
you do not prevent buffer overflow in scanf("%s",str);
you do not check for input or conversion failure in scanf().
n is uninitialized in if (n > len) { n = len; }
you do not check for negative n.
you do check for allocation failure after char *new_string = malloc(n + 1);
you do not free new_string after use.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char str[100];
int n, len;
printf("Enter a string:");
if (scanf("%99s", str) != 1)
return 1;
printf("enter n:");
if (scanf("%i", &n) != 1)
return 1;
len = strlen(str);
if (n > len) {
n = len;
} else
if (n < 0) {
n = 0;
}
char *new_string = malloc(n + 1);
if (new_string == NULL) {
fprintf(stderr, "allocation failure for %d bytes\n", n);
return 1;
}
for (int i = 0; i < n; i++) {
new_string[i] = str[i];
}
new_string[n] = '\0';
printf("String: %s\n", new_string);
free(new_string);
return 0;
}
Note also some alternatives to copy at most n characters from a string:
// assuming new_string points to an array of at least n+1 bytes
strncpy(new_string, str, n);
// assuming new_string points to an array of at least n+1 bytes
snprintf(new_string, n + 1, "%s", str);
// assuming new_string points to an array of at least new_string_size bytes
// and n has type int
snprintf(new_string, new_string_size, "%.*s", n, str);
Since you know that n >= 0 && n <= len, you can use memcpy:
memcpy(new_string, str, n);
new_string[n] = '\0';
You can also use the Posix function strndup() defined in <string.h> that combines allocating enough space, copying at most n bytes and setting the null terminator:
char *new_string = strndup(str, n);
strndup() will be part of the upcoming version of the C Standard (C2x) and is widely available on most platforms as it has been part of the Posix standard for more than 10 years.
#include <stdio.h>
#define N 100
int main(){
char str[N];
scanf("%s",str);
char str1[N];
int i;
for(i=0;i<N;i++){
str1[i]=str[i];
}
printf("%s",str1);
}
i think if you just want to copy a string in a new char variable you can do this please tell if you find anything wrong

What am I doing wrong with malloc and realloc of array of struct?

I'm trying to build in C an array of structures without defining the length of the maximum size of the array.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct text {
char *final;
} text;
int main() {
int n, sizearray = 10, i;
char *str;
text *testo;
testo = (text *)malloc(sizeof(text) * sizearray);
fgets(str, 1024, stdin);
i = 0;
while (str[0] != 'q') {
if (i == sizearray - 1) {
testo = (text *)realloc(testo, sizearray * 2 * sizeof(text));
}
n = strlen(str);
n = n + 1;
testo[i].finale = (char *)malloc(sizeof(char) * n);
strcpy(testo[i].finale, str);
i++;
fgets(str, 1024, stdin);
}
for (i = 0; i < sizearray; i++)
printf("%s \n", testo[i].finale);
return 0;
}
this gives me
process finished with exit code 139 (interrupted by signal 11:SIGSEV).
What am I doing wrong?
There are multiple issues in your code:
[major] str is an uninitialized pointer. You should make it an array of char defined with char str[1024].
[major] you do not adjust sizearray when you double the size of the array, hence you will never reallocate the array after the initial attempt at i = 9.
[major] the final loop goes to sizearray but there are potentially many uninitialized entries at the end of the array. You should stop at the last entry stored into the array.
you should also check the return value of fgets() to avoid an infinite loop upon premature end of file.
you should test for potential memory allocation failures to avoid undefined behavior.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct text {
char *finale;
} text;
int main() {
char str[1024];
text *testo = NULL;
size_t sizearray = 0;
size_t i, n = 0;
while (fgets(str, sizeof str, stdin) && *str != 'q') {
if (n == sizearray) {
/* increase the size of the array by the golden ratio */
sizearray += sizearray / 2 + sizearray / 8 + 10;
testo = realloc(testo, sizearray * sizeof(text));
if (testo == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
}
testo[n].finale = strdup(str);
if (testo[n].finale == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
n++;
}
for (i = 0; i < n; i++) {
printf("%s", testo[i].finale);
}
for (i = 0; i < n; i++) {
free(testo[i].finale);
}
free(testo);
return 0;
}
str is uninitialized. Either allocate memory with malloc or define it as an array with char str[1024].

Is my usage of fgets() and strtok() incorrect for parsing a multi-line input?

I'm writing an implementation of the Moore Voting algorithm for finding the majority element (i.e. the element which occurs more than size/2 times) in an array. The code should return the majority element if it exists or else it should return -1. Now my version of the majorityElement(int size, int arr[]) seems to work perfectly fine if I directly hardcode the integer array in the main() function and invoke it from there.
int majorityElement(int size, int arr[])
{
int majorityindex = 0;
int votes = 1;
int index;
for (index = 1; index < size; index++)
{
if (arr[index] == arr[majorityindex])
votes++;
else
votes--;
if (votes == 0)
{
majorityindex = index;
votes = 1;
}
}
int count = 0;
int i;
for (i = 0; i < size; i++)
{
if(arr[majorityindex] == arr[i])
count++;
}
if (count > (size/2))
return arr[majorityindex];
return -1;
}
However, I'm facing some issues if I try to read an input stream like these:
2
5
3 1 3 3 2
3
1 2 3
The first line of the input contains the number of test cases. The first line of the test case will be the size of the array and the second line will be the elements of the array.
I tried to read the input stream from within the main() function like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
int majorityElement(int size, int arr[]);
int main()
{
char buf[3];
fgets(buf, MAX, stdin);
int n = atoi(buf);
char a[3];
char b[MAX];
int i;
int count;
int* num;
for (i = 0; i < n; i++)
{
count = 0;
fgets(a, MAX, stdin);
fgets(b, MAX, stdin);
int x = atoi(a);
char* num[x];
int arr[x];
int k = 0;
char* token = strtok(b, " ");
while (token != NULL)
{
num[k] = token;
arr[k] = atoi(num[k]);
token = strtok(NULL, " ");
k++;
}
printf("%d\n", majorityElement(x, arr));
}
return 1;
}
I took the size of buf[] and a[] as 3 during declaration as they must have sufficient space for the \n character read by fgets() as well as the terminating \0 character. As far as I know, the atoi() function ignores the \n character while converting the character array (string) into an integer. I tried to store the first entry of the input (i.e. the number of entries) in a character array buf, converted it into a string and stored it in a variable n. Similarly, I tried to obtain the size of a test array in a variable x and the test arrays (second line of test case) in an integer array arr. While buf and n seem to obtain the correct values in all cases, I'm not quite sure about arr. I'm aware that fgets() leaves a terminal \n character and that might be causing some havoc during tokenization using strtok, although I can't finger at why. I tried submitting this code on GeeksForGeeks. It gives absolutely correct outputs for the sample test case:
2
5
3 1 3 3 2
3
1 2 3
that is
3
-1
However, when I try to "submit" my solution it says:
Possibly your code doesn't work correctly for multiple test-cases (TCs).
The first test case where your code failed:
Input:
4
1 2 2 1
Its Correct output is:
-1
And Your Code's output is:
1
I can't seem to make sense of this. If I manually write this in stdin:
1
4
1 2 2 1
the code outputs
-1
which is indeed the correct solution. This doesn't match with the output claimed during the submission i.e. 1. So I'm not really sure where I'm going wrong. Have I used fgets() or strtok() incorrectly in the main() function? Or is it something else?
Updated the main() function according to suggestions in the comments.
int main()
{
char buf[MAX];
fgets(buf, MAX, stdin);
int n = atoi(buf);
char a[MAX];
char b[MAX];
int i;
int count;
int* num;
for (i = 0; i < n; i++)
{
count = 0;
fgets(a, MAX, stdin);
fgets(b, sizeof(a), stdin);
a[sizeof(a)-1] = '\0';
b[sizeof(b)-1] = '\0';
int x = atoi(a);
int arr[x];
int k = 0;
char* token = strtok(b, " ");
while (token != NULL)
{
if (k > x)
break;
arr[k] = atoi(token);
token = strtok(NULL, " ");
k++;
}
printf("%d\n", majorityElement(x, arr));
}
return 1;
}
As pointed out by #Vlad, the MAX was set too low in my original array. The question says that the number of entries in an array is upper bounded by 10^7 and each array entry is upper bounded by 10^6 (7 digits). So MAX needs to be of the order 10^8. According to the suggestions in the comments, I'm now using dynamic allocation instead of variable length arrays.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 10000000
int majorityElement(int size, int arr[])
{
int majorityindex = 0;
int votes = 1;
int index;
for (index = 1; index < size; index++)
{
if (arr[index] == arr[majorityindex])
votes++;
else
votes--;
if (votes == 0)
{
majorityindex = index;
votes = 1;
}
}
int count = 0;
int i;
for (i = 0; i < size; i++)
{
if(arr[majorityindex] == arr[i])
count++;
}
if (count > (size/2))
return arr[majorityindex];
return -1;
}
int main()
{
char* buf = calloc (MAX, sizeof(char));
fgets(buf, MAX, stdin);
int n = atoi(buf);
char* a = calloc (MAX, sizeof(char));
char* b = calloc(MAX, sizeof(char));
int i;
for (i = 0; i < n; i++)
{
fgets(a, MAX, stdin);
fgets(b, MAX, stdin);
a[strlen(a)-1] = '\0';
b[strlen(b)-1] = '\0';
int x = atoi(a);
int *arr = calloc(x, sizeof(int));
int k = 0;
char* token = strtok(b, " ");
while (token != NULL)
{
if (k > x)
break;
arr[k] = atoi(token);
token = strtok(NULL, " ");
k++;
}
printf("%d\n", majorityElement(x, arr));
free(arr)
}
free(buf);
free(a);
free(b);
return 1;
}
If I set MAX to 10^7 then the code passes all the test cases and is accepted for submission. However, if I set MAX to 10^8 (as required), I get a segmentation fault. How to overcome this?
Your program has several drawbacks.
For example within the function main there are unused variables declared like
int count;
int* num;
The function does take into account that -1 can be a valid value of the array.
There is a problem with the number of elements that can be specified in a test. It is a very big number (according to the description 1 <= N <= 10000000). So the value of MAX equal to 100 is too low. As a result the data can be read incorrectly and not completely. Also there can occur problems with the variable length arrays.
There is no need to use the function fgets because each integer number can be read using scanf.
I could suggest the following solution. Try it and see whether it will pass the tests.
#include <stdio.h>
#include <stdlib.h>
size_t majorityElement( const int a[], size_t n )
{
size_t majority_index = 0;
for ( size_t i = 1, votes = 1; i < n; i++ )
{
if ( a[majority_index] == a[i] )
{
++votes;
}
else
{
--votes;
}
if ( votes == 0 )
{
majority_index = i;
++votes;
}
}
size_t count = 0;
for ( size_t i = 0; i < n; i++ ) count += a[i] == a[majority_index];
return n / 2 < count ? majority_index : n;
}
int main(void)
{
size_t n = 0;
scanf( "%zu", &n );
for ( size_t i = 0; i < n; i++ )
{
size_t m = 0;
scanf( "%zu", &m );
if ( m != 0 )
{
int *a = calloc( m, sizeof( int ) );
for ( size_t j = 0; j < m; j++ ) scanf( "%d", a + j );
size_t majority_index = majorityElement( a, m );
printf( "%d\n", majority_index == m ? -1 : a[majority_index] );
free( a );
}
}
return 0;
}
If it will not pass the tests then it seems there is a bug in tests.:)
Or if the function return type may not be changed then the function definition can look like
int majorityElement( const int a[], size_t n )
{
size_t majority_index = 0;
for ( size_t i = 1, votes = 1; i < n; i++ )
{
if ( a[majority_index] == a[i] )
{
++votes;
}
else
{
--votes;
}
if ( votes == 0 )
{
majority_index = i;
++votes;
}
}
size_t count = 0;
for ( size_t i = 0; i < n; i++ ) count += a[i] == a[majority_index];
return n / 2 < count ? a[majority_index] : -1;
}

How to convert comma separated char* to uint32_t[] array in C

I want to convert a comma separated char* to an uint32_array[] in C. Is there an easy method/routine to do that?
I already spend a lot of time on SO and found many solutions on C++, but not an C like that :
Parsing a comma-delimited std::string
But I think it is not a good solution to cast from char* to string to string stream to vector and work with the vector.
char input[] = "1 , 2 , 34, 12, 46, 100";
to
uint32_t output[] = { 1 , 2 , 34, 12, 46, 100 };
I would appreciate any kind of help. Thanks a lot.
Here's a recursive algorithm that only makes a single pass. It allocates at the deepest level and fills in on the way out:
int *cvt(char *input, int *level)
{
char *cp = strtok(input, ", ");
if (cp == NULL) {
/* No more separators */
return (int *) malloc(sizeof(int) * *level);
}
int my_index = -1;
int n;
if (sscanf(cp, "%d", &n) == 1) {
my_index = *level;
*level += 1;
} else {
printf("Invalid integer token '%s'\n", cp);
}
int *array = cvt(NULL, level);
if (my_index >= 0) {
array[my_index] = n;
}
return array;
}
Call with:
int main(int ac, char **av)
{
char input[] = "1, 2, bogus, 4, 8, 22, 33, 55";
int n_array = 0;
int *array = cvt(input, &n_array);
int i;
printf("Got %d members:\n", n_array);
for (i = 0; i < n_array; ++i)
printf("%d ", array[i]);
printf("\n");
return 0;
}
One method (of many):
int converted = 0 ;
char* tok = input ;
int i = 0 ;
do
{
converted = sscanf( tok, "%d", &output[i] ) ;
tok = strchr( tok, ',' ) + 1 ;
i++ ;
} while( tok != NULL && converted != 0 ) ;
You could use strtok() instead of sscanf() and strchr(), but that modifies input which may be undesirable.
If the input is a stream rather than a string, then it is simpler:
int converted = 0 ;
int i = 0 ;
do
{
converted = fscanf( fd, "%d,", &output[i] ) ;
i++ ;
} while( !feof( fd ) && converted != 0 ) ;
I have not included any means to prevent output[i] from exceeding the bounds - you may need to consider that too.
Here is one way to do it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int* values;
int count;
}
output_t;
output_t Read(char input[])
{
int* values = NULL;
int count = 0;
char* pch = strtok(input,", ");
while (pch != NULL)
{
values = realloc(values,(count+1)*sizeof(*values));
values[count++] = atoi(pch);
pch = strtok(NULL,", ");
}
return (output_t){values,count};
}
And here is a usage example:
void Example()
{
char input[] = "1 , 2 , 34, 12, 46, 100";
output_t output = Read(input);
for (int i=0; i<output.count; i++)
printf("%d\n",output.values[i]);
free(output.values);
}
I'll throw my hat into the ring and do a single pass of the data. I estimate the required array size to be the worst case where every data is of the form "n," so two bytes per number, and resize it afterwards.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned int uint32_t;
int main (void) {
char input[] = "1 , 2 , 34, 12, 46, 100";
uint32_t *output, *temp;
char *tok;
int elements = 0;
int len = 1 + strlen(input) / 2; // estimate max num of elements
output = malloc(len * sizeof(*output));
if (output == NULL)
exit(-1); // memory alloc error
tok = strtok(input, ", "); // parse the string
while (tok != NULL) {
if (elements >= len)
exit(-2); // error in length assumption
if (1 != sscanf(tok, "%u", output + elements))
exit(-3); // error in string format
elements++;
tok = strtok(NULL, ", ");
}
temp = realloc(output, elements * sizeof(*output)); // resize the array
if (temp == NULL)
exit(-4); // error in reallocating memory
output = temp;
for (len=0; len<elements; len++)
printf("%u ", output[len]);
printf("\n");
free(output);
return 0;
}
Program output:
1 2 34 12 46 100
Read through the string once to figure out how to size your array:
uint32_t n = 1;
for (uint32_t idx = 0; idx < strlen(input); idx++) {
if (input[idx] == ',') {
n++;
}
}
There's a different way to do this that doesn't require reading through the string, but it requires resizing the destination array as new elements come in, which makes the code more complex. It's easy enough to read through the string once for small strings.
Make your destination array:
uint32_t* output = NULL;
output = malloc(sizeof(*output) * n);
if (!output) {
fprintf(stderr, "Error: Could not allocate space for output array!\n");
exit(EXIT_FAILURE);
}
Populate your array. One way to do this without clobbering the string is to keep a couple pointers to the start and end of a substring that contains the desired numeric element in the comma-separated string, and just loop over all the characters in the string:
#define MAX_LEN 13
char* start = &input[0];
char* end = &input[0];
char entry[MAX_LEN];
uint32_t entry_idx = 0;
int finished = 0; // false
do {
end = strchr(start, ',');
if (!end) {
end = input + strlen(input);
finished = 1;
}
memcpy(entry, start, end - start);
entry[end - start] = '\0';
sscanf(entry, "%u", &output[entry_idx++]);
start = end + 1;
} while (!finished);
MAX_LEN is 13 because it is unlikely that a uint32_t will be longer than 13 digits. You can make this longer to future-proof this for computers made in the year 2100.
Be sure to free the array when you're done with it:
free(output);
output = NULL;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int getsize(char* str, char* delimiters) //give the size of the final uint32_t array[]
{
int count = 0;
char* st = strdup(str), *t = strtok(st, delimiters);
while(t)
{
count++;
t = strtok(NULL, delimiters);
}
free(st);
return count;
}
uint32_t* Char_to_Array(char *data, char* delimiters, int *siz) //siz is a pointer to get the size of the array
{
char* st = strdup(data), *t = NULL; //dup string, strtok mage change on the passed string
*siz = getsize(data, delimiters);
uint32_t* buf=(uint32_t *)malloc((*siz)*4);
t = strtok(st, delimiters); //split string by " "
int i = 0;
while(t)
{
buf[i] = atoi(t);
t = strtok(NULL, delimiters);
i++;
}
free(st);
return buf;
}
here a test with a main function
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int main()
{
int getsize(char* str, char* delimiters), siz = 0, i = 0;
uint32_t* Char_to_Array(char *data, char* delimiters, int *x);
uint32_t* t = Char_to_Array("123, 156, 4658,7878", " ,", &siz);
while(i<siz)
{
printf("array[%d] = %d\n", i, t[i]);
i++;
}
free(t);
return 0;
}
2 pass approach:
1) Count the number of commas and allocate an array.
2) Parse the string - look for errors.
[Late to the uint32 comma party]
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
typedef struct {
uint32_t *u32;
size_t count;
bool error;
} CSV_32_T;
CSV_32_T CSV_to_int32_list(const char *csv) {
CSV_32_T list = { NULL, 1, false };
// 1st pass: Quickly go through list counting commas
const char *p = csv;
for (p = csv; *p; p++) {
if (*p == ',') {
list.count++;
}
}
size_t i = 0;
list.u32 = malloc(list.count * sizeof *list.u32);
if (list.u32) {
// 2nd pass: scan
p = csv;
for (i = 0; i < list.count; i++) {
if (i > 0 && *p++ != ',') {
break;
}
int n = 0;
if (1 != sscanf(p, "%" SCNu32 " %n", &list.u32[i], &n)) {
break;
}
p += n;
}
}
if (i != list.count || *p) {
free(list.u32);
return (CSV_32_T ) { NULL, 0, true } ;
}
return list;
}
void testCSV(const char *csv) {
CSV_32_T y = CSV_to_int32_list(csv);
printf("%d %zu \"%s\"\n", y.error, y.count, csv);
}
int main(void) {
testCSV("1 , 2 , 34, 12, 46, 100");
testCSV("1 2 , 34, 12, 46, 100");
return 0;
}

Resources