Splitting a char based on delimiters and covert them to integers - arrays

I'm trying to split a given char into number of neopixels and colours which the neopixel in the strip should have. The format is: first the neopixel number, then the given color; next neopixel number, next given colour for that neopixel. I managed to split the given char into these and print them out. This is the first piece of the code and is testable and working.
Next I want to evaluate the given data and based on the colour and number of neopixel send them to my setPixel function. The important arguments are the pixelNumber (1st element of the given data) and r,g,b (2d element of the given data). r, g, b values are determind by a switch case, for example 1 needs to be r:255, G:230, C:560 and so on. This piece of code is "pseudocode" and can not be tested yet, it gives an indication of what needs to happen next.
The problem:
I don't know how to convert the split data into variables which I can use in my case() and how to convert the neopixelNumber to an int. Any help appreciated!
Code:
#include <stdio.h>
#include "string.h"
#include <stdlib.h>
int split (const char *txt, char delim, char ***tokens)
{
int *tklen, *t, count = 1;
char **arr, *p = (char *) txt;
while (*p != '\0') if (*p++ == delim) count += 1;
t = tklen = calloc (count, sizeof (int));
for (p = (char *) txt; *p != '\0'; p++) *p == delim ? *t++ : (*t)++;
*tokens = arr = malloc (count * sizeof (char *));
t = tklen;
p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
while (*txt != '\0')
{
if (*txt == delim)
{
p = *arr++ = calloc (*(t++) + 1, sizeof (char *));
txt++;
}
else *p++ = *txt++;
}
free (tklen);
return count;
}
int main()
{
/* will split a given char into a neopixel number and a color (example: 1 : red, 2, blue) */
/* this part is working and testable */
char **singleNeopixelData;
char **splittedIntoPixelNumberAndColour;
const char *fullNeopixelData = "51,0;52,1;60,0;61,1"; // neopixel number , colour ;
int totalAmountNeopixels, counterNeopixelsAmount, totalAmountNeopixelsData, counterNeopixelsData;
totalAmountNeopixels = split (fullNeopixelData, ',', &singleNeopixelData);
for (counterNeopixelsAmount = 0; counterNeopixelsAmount < totalAmountNeopixels; counterNeopixelsAmount++) {
totalAmountNeopixelsData = split (singleNeopixelData[counterNeopixelsAmount], ';', &splittedIntoPixelNumberAndColour);
int pixel = 0;
for (counterNeopixelsData = 0; counterNeopixelsData < totalAmountNeopixelsData; counterNeopixelsData++) {
printf ("%s\n", splittedIntoPixelNumberAndColour[counterNeopixelsData]);
}
}
// bridge between those two pieces of code is what i'm looking for :)
/* pseudo code for what needs to happen
for() { // loop through created array
int r, g, b;
switch (neopixelColour) { // check give number and decide which color the pixel is going to be
case 0:
r = 0;
g = 0;
b = 0;
break;
case 1:
r = 255;
g = 0;
b = 0;
break;
}
setPixel(pPixelArray, pixelNumber, r, g, b); // assign pixelnumer and colour
}
showPixels(); // show output to neopixel strip
*/
}
I tried to convert them using typecasting but unfortunately that doesn't work.

Related

C, how to get undefined part of string

I started learning C language about 1 week, and I'm trying to build my first programs. I'm coming from Python, so the C syntax isn't very clear for me, and I haven't understand the solutions that I found online.
So, if I have this string:
char str[50] = "dimension[1080,720];"
and i want to create two integer variables that containes 1080 and 720 and string var that contains the first string. But these two numbers can change, and they can have random cifras. So, i wanted my output is
int x = 1080
int y = 720
*the values are always two, but the lenght can change.
How can i do that?
Second version:
So, if I have this string:
char str[50] = "dimension["string",1080,720];"
and i want to create two integer variables that containes 1080 and 720 and string var that contains the first string. But these two numbers can change, and they can have random cifras. So, i wanted my output is
char str[1000] = "string";
int x = 1080
int y = 720
the values are always three, but the lenght can change.
How can i do that?
use sscanf function
int main(void)
{
char str[] = "dimension[1080,720];";
int x,y;
if(sscanf(str, "dimension[%d,%d", &x, &y) != 2) {printf("Error\n");}
else printf("X=%d Y=%d", x, y);
}
Question two:
int main(void)
{
char str[] = "dimension[\"string\",1080,720];";
char str1[20];
int x,y;
char *ptr = strstr(str, "[");
char *ptr1;
size_t len;
memcpy(str1, ptr + 2, len = (ptr1 = strchr(ptr + 2, '"')) - (ptr + 2));
str[len] = 0;
if(sscanf(ptr1 + 2, "%d,%d", &x, &y) != 2) {printf("Error\n");}
else printf("Str = %s X=%d Y=%d", str1, x, y);
}
The sscanf answer is perfect, but not very flexible. I add this in case you want more flexibility to parse strings. You can do it with regular expressions and in your case would be something like:
Piece of code taken from https://gist.github.com/ianmackinnon/3294587
I have added some modification to extract the numbers to the variables
#include <stdio.h>
#include <string.h>
#include <regex.h>
#include <stdlib.h>
int main ()
{
// Define regexp and input
char * source = "dimension[\"string\",1080,720];";
char * regexString = "[a-z]+\\[\"([a-z]+)\",([0-9]+),([0-9]+)\\]";
size_t maxMatches = 3;
size_t maxGroups = 4;
// Variables we want to extract from the input
char* str;
int n1;
int n2;
regex_t regexCompiled;
regmatch_t groupArray[maxGroups];
unsigned int m;
char * cursor;
if (regcomp(&regexCompiled, regexString, REG_EXTENDED))
{
printf("Could not compile regular expression.\n");
return 1;
};
m = 0;
cursor = source;
for (m = 0; m < maxMatches; m ++)
{
if (regexec(&regexCompiled, cursor, maxGroups, groupArray, 0))
break; // No more matches
unsigned int g = 0;
unsigned int offset = 0;
for (g = 0; g < maxGroups; g++)
{
if (groupArray[g].rm_so == (size_t)-1)
break; // No more groups
if (g == 0)
offset = groupArray[g].rm_eo;
char cursorCopy[strlen(cursor) + 1];
strcpy(cursorCopy, cursor);
cursorCopy[groupArray[g].rm_eo] = 0;
printf("Match %u, Group %u: [%2u-%2u]: %s\n",
m, g, groupArray[g].rm_so, groupArray[g].rm_eo,
cursorCopy + groupArray[g].rm_so);
switch (g)
{
case 1:
// Copy to the string now that we know the length
str = malloc(strlen(cursor)+1);
strcpy(str,cursorCopy + groupArray[g].rm_so);
break;
case 2:
n1 = (int) strtol(cursorCopy + groupArray[g].rm_so, (char **)NULL, 10); //(cursorCopy + groupArray[g].rm_so,10);
break;
case 3:
n2 = (int) strtol(cursorCopy + groupArray[g].rm_so, (char **)NULL, 10); //(cursorCopy + groupArray[g].rm_so,10);
break;
}
}
cursor += offset;
}
regfree(&regexCompiled);
printf("Matches in variables: %s - %d - %d \n",str,n1,n2);
return 0;
}
This for me prints
Match 0, Group 0: [ 0-28]: dimension["string",1080,720]
Match 0, Group 1: [11-17]: string
Match 0, Group 2: [19-23]: 1080
Match 0, Group 3: [24-27]: 720
Matches in variables: string - 1080 - 720

How to dynamically center align text in a char * string to fit in a total of 16 spaces in C?

I am trying to center align strings in a total of 16 spaces to eventually print them on a 16x2 LCD Display. The values are grabbed from a database, and put in a global variable that is constantly being updated.
The values in the database are already in string format.
What I'd like to do is after getting the value from the DB, update the global variable to contain a string centered in 16 spaces.
I understand using global variables may not be best practice but ignoring that is there a way to do this?
char * systemInfoValues[5] = {" "," "," "," "," "}
for(int i=0; i< 5; i++){
systemInfoValues[i] = PQgetvalue(res,i,0); //get the value from db;
int len = strlen(systemInfoValues[i]);
char tmp[20];
sprintf(tmp,"%*s", (17-len)/2 + len, systemInfoValues[i]);
strcpy(systemInfoValues[i],tmp);
}
0 = a blank space
xxxxx = string from db
If the length of the string is odd
I expect the output to be [00xxxxxxxxxxxxx0]
if the length of the string is even
I expect the output to be [00xxxxxxxxxxxx00]
It is simple 6 line function. symetry is giving you the option
char *centerinstring(char *buff, size_t len, const char *str, int symetry)
{
size_t strl = strlen(str);
size_t pos = (len - strl) / 2 + (strl & 1) * !!symetry;
memset(buff,' ', len);
buff[len] = 0;
memmove(buff + pos, str, strl);
return buff;
}
int main()
{
char buff[11];
printf("|%s|\n", centerinstring(buff, 10, "1234567", 1));
printf("|%s|\n", centerinstring(buff, 10, "1234567", 0));
return 0;
}
or with the option to allocate memory for the buff (if you pass NULL
char *centerinstring(char *buff, size_t len, const char *str, int symetry)
{
size_t strl = strlen(str);
size_t pos = strl / 2 + (strl & 1) * !!symetry;
buff = buff ? malloc(len + 1) : buff;
if(buff)
{
memset(buff,' ', len);
buff[len] = 0;
memmove(buff + pos, str, strl);
}
return buff;
}
sprintf()-comfort:
#include <assert.h>
#include <string.h>
#include <stdio.h>
void center(char *dst, char *src, size_t width)
{
assert(dst && src && width);
size_t src_len = strlen(src);
if (src_len >= width) {
*dst = '\0';
return;
}
int right = (int)(width - src_len) / 2;
int left = right + (int)src_len;
right -= (src_len & 1);
sprintf(dst, "%*s%*s", left, src, right, "");
}
int main(void)
{
char destination[17];
center(destination, "12345678901234", sizeof(destination));
printf("\"%s\"\n", destination);
}
You can do it in another way (without using the sprintf function).
I don't know about any interface of the sprintf function that would allow you to do it, but you can solve the problem using simple strcpy of variables.
This is a main program that would solve your problem, it is documented in itself, so you should be able to understand how to apply this to your code:
#include <stdio.h>
#include <string.h>
/* This simple program would transfer the original string that is in the
* out_value to be centralized in this variable. */
int main(void) {
char out_value[17] = "1234567891";
char temp[20] = {0};
int first_index = 0;
int string_length = 0;
/* Copy the string to the temp variable, to modify the chars in
* out_value. */
strcpy(temp, out_value);
/* Find out the index for the first char to be placed in the centralized
* string. */
string_length = strlen(temp);
first_index = (16 - string_length) / 2;
/* Set all the values of the out_value to be the wanted value of space (here
* it is 0 for visualizing, it can be space to not be present). */
memset(out_value, '0', 16);
/* Copy the original string back, moving the start of it, so it would be
* centralized. */
strncpy(&(out_value[first_index]), temp, string_length);
/* Print the string. */
printf("%s", out_value);
}
When modifying your code to work with this, the code would look something like this:
char * systemInfoValues[5] = {NULL}
for(int i=0; i< 5; i++){
systemInfoValues[i] = PQgetvalue(res,i,0); //get the value from db;
int len = strlen(systemInfoValues[i]);
char tmp[20];
int first_index = 0;
strcpy(tmp, systemInfoValues[i]);
first_index = (16 - len) / 2;
memset(systemInfoValues[i], ' ', 16);
strncpy(&(systemInfoValues[i][first_index]), tmp, len);
}
Note that I changed the initializing of the value of systemInfoValues. When you initialized it, you put empty strings there. Note that this is a bad habit. Putting empty strings there (or strings with a single space) would allocate the memory for this string (which you will never use).
You didn't include the definition to the function of PQgetvalue, but assuming that it would return a char pointer, this should work.
But, this code would change the global value as well. If you don't want to change it, you shoudn't put the result there, but copy the result to the string before doing any changes to it.
After modifying the code, it should look like this:
char systemInfoValues[5][17] = {{0}}
for(int i=0; i< 5; i++){
char *global_reference = PQgetvalue(res,i,0); //get the value from db;
int len = strlen(systemInfoValues[i]);
char tmp[20];
int first_index = 0;
strcpy(tmp, global_reference);
first_index = (16 - len) / 2;
memset(systemInfoValues[i], ' ', 16);
strncpy(&(systemInfoValues[i][first_index]), tmp, len);
}
edit: apperently there is an interface for the sprintf function to work (as you originally wanted). To see it, refer to the answer of Swordfish

I have a string str which has two array (can be more) that I would like to convert to integer arrays in C

As an Example,
Input:
c[] = "[1,2,3][5,7,8]"
Output:
a = [1,2,3] //of type int a[]
b = [5,7,8] //of type int b[]
I have tried using strtok to remove "]". But, when I use strtok next time, I am not able to use it. If I try to print the output, I get
[1,2,3
[1
2
3
instead of
[1,2,3
[1
2
3
[5,7,8
[5
7
8
Code that I have so far
char c[] = "[1,2,3][5,7,8]";
char *token = strtok(c, "]");
for (token; token != NULL ; token = strtok(NULL, "]")){
puts(token);
char *comma = strtok(c, ",");
for (comma; comma != NULL; comma = strtok(NULL, ",")){
puts(comma);
}
}
Your problem is, that strtok has a memory. First time that you pass in a string, it is remembered and then used again and again as long as you pass in NULL as first parameter.
However, within your loop, you call strtok again with a parameter. So this new string (which is the first token only) is placed in strtok's memory, and after it is processed completely in the inner loop, there is nothing left to tokenize in the outer loop.
Have a look at this thread, it explains more detailed how strtok works.
However, you are lucky: strtok is manipulating the string you first passed in place (this is why you have to pass the string to be tokenized as char*, but the delimiters can be a const char*). So you can do this:
char c[] = "[1,2,3][5,7,8]";
char* next = c;
char* token;
while((token = strtok(next, "]")))
{
puts(token);
next += strlen(token) + 1; // move next behind the token
token = strtok(token, ",");
do
{
puts(token);
}
while((token = strtok(NULL, ",")));
}
If you are wondering about the extra parentheses, these are to prevent a warning in the compiler ("possible assignment instead of comparison").
If you are converting a string of character digits to an array of integer values, one character per value (or allowing a - before any character digit to indicate a negative value for your array), you may be better off writing a simple function to step though the string and perform your conversions manually.
An example using array indexing of the string could be written a follows. You could easily change the array index notations to pointer notation which is more intuitive to some.
#include <stdio.h>
#include <string.h>
size_t str2arr (char *d, size_t max, char *s, size_t *ofs);
int main (int argc, char **argv) {
char c[] = "[1,2,3][5,7,8]";
char *p = argc > 1 ? argv[1] : c;
size_t i, offset = 0, na = 0, nb = 0, nchr = strlen (p);
char a[nchr], b[nchr];
memset (a, 0, nchr * sizeof *a); /* zero each VLA */
memset (b, 0, nchr * sizeof *b);
na = str2arr (a, nchr, p, &offset); /* convert first segment */
nb = str2arr (b, nchr, p + offset, &offset); /* convert second segment */
for (i = 0; i < na; i++) /* output results */
printf (" a[%2zu] : % d\n", i, a[i]);
putchar ('\n');
for (i = 0; i < nb; i++)
printf (" b[%2zu] : % d\n", i, b[i]);
putchar ('\n');
return 0;
}
/** convert a string of characters to an array of values
* including accounting for negative values. the destination
* index `di` returns the number of characters conversions, the
* offset of the next segment within 's' is updated in pointer 'ofs'
*/
size_t str2arr (char *d, size_t max, char *s, size_t *ofs)
{
if (!d || !s || !*s) return 0; /* validate input */
size_t di = 0, neg = 0;
register size_t si = 0;
for (; di < max && s[si]; si++, di++) { /* for each character */
if (s[si] == ']') break;
while (s[si] && (s[si] < '0' || ('9' < s[si]))) { /* if not digit */
if (s[si] == '-') neg = 1; /* if '-' sign, set flag */
else neg = 0; /* clear if not last before digit */
si++;
}
if (!s[si]) break; /* validate not end of string */
d[di] = neg ? -(s[si] - '0') : s[si] - '0'; /* convert to digit */
neg = 0; /* reset flag */
}
*ofs = si + 1; /* update offset before return */
return di; /* return number of conversions */
}
Example Use/Output
$ ./bin/str2arr
a[ 0] : 1
a[ 1] : 2
a[ 2] : 3
b[ 0] : 5
b[ 1] : 7
b[ 2] : 8
$ ./bin/str2arr "[1,2,3,4][5,6,-5,7,-1,8,9,2]"
a[ 0] : 1
a[ 1] : 2
a[ 2] : 3
a[ 3] : 4
b[ 0] : 5
b[ 1] : 6
b[ 2] : -5
b[ 3] : 7
b[ 4] : -1
b[ 5] : 8
b[ 6] : 9
b[ 7] : 2
Look it over, compare this approach to the other answers. In C, you have as much fine-grain-control over how you parse data as you want to exercise. If you have no need to handle negative values, then the implementation is much simpler. Let me know if you have any questions.
This solution has two nested loops of strtok_s, because strtok is not re-entrant. This is MSVC, some systems implement the similar strtok_r.
I have created output in accordance with the top of your question, this can be modified to suit other output, it was not very clear. In this case, it was not really necessary to have two nested loops, but your subsequent examples confuse the issue by breaking up the comma'ed input.
#include <stdio.h>
#include <string.h>
int main(void) {
char c[] = "[1,2,3][5,7,8]";
char *tok1 = NULL;
char *tok2 = NULL;
char *end1 = NULL;
char *end2 = NULL;
int comma = 0;
char identifier = 'a';
tok1 = strtok_s(c, "[]", &end1);
while(tok1 != NULL) { // outer loop splitting [bracket] parts
printf("%c = [", identifier);
comma = 0; // control comma output
tok2 = strtok_s(tok1, ",", &end2);
while(tok2 != NULL) { // inner loop splitting ,comma, parts
if(comma) { // check if comma required
printf(",");
}
printf("%s", tok2);
comma = 1; // a comma will be needed
tok2 = strtok_s(NULL, ",", &end2);
}
printf("] //of type int %c[]\n", identifier);
identifier++;
tok1 = strtok_s(NULL, "[]", &end1);
}
return 0;
}
The simpler program where you don't need to examine within the [brackets] is
#include <stdio.h>
#include <string.h>
int main(void) {
char c[] = "[1,2,3][5,7,8]";
char *tok = NULL;
char identifier = 'a';
tok = strtok(c, "[]");
while(tok != NULL) {
printf("%c = [%s] //of type int %c[]\n", identifier, tok, identifier);
identifier++;
tok = strtok(NULL, "[]");
}
return 0;
}
In both cases the output is:
a = [1,2,3] //of type int a[]
b = [5,7,8] //of type int b[]
EDIT altered the second example to give output as per OP's recent comment above.
#include <stdio.h>
#include <string.h>
int main(void) {
char c[] = "[1,2,3][5,7,8]";
char *tok = NULL;
char identifier = 'a';
tok = strtok(c, "[]");
while(tok != NULL) {
printf("int %c[] = { %s };\n", identifier, tok, identifier);
identifier++;
tok = strtok(NULL, "[]");
}
return 0;
}
Program output:
int a[] = { 1,2,3 };
int b[] = { 5,7,8 };

C program using strcat keeps giving unexpected output at runtime

I am trying to create a program that is able to rotate at point k, defined as the "rotation requested."
Example: rotate("derp", 3) => pder
My code for this function is called rotate, as listed below. It takes in both a char pointer array, startString, as defined in my main, and the number of rotations (A long int because I use atol to get the integer from the command line).
int rotate(char *startString, long int rotations) {
char *doubleString = malloc((sizeof startString * 2) + sizeof(char));
strcat(doubleString, startString);
strcat(doubleString, startString);
long int stringSize = (sizeof startString - 1);
long int breakIndex = (rotations % stringSize);
char* rotatedString = malloc((sizeof startString + sizeof(char)));
int i;
for (i = 0; i < stringSize + 1; i++) {
char pushedCharacter = doubleString[(int)breakIndex + i];
strcat(rotatedString, &pushedCharacter);
}
printf("%s\n", rotatedString);
printf("%s\n", doubleString);
return 0;
}
But, when I output, if I use something like doghouse I get a weird ?4??? in front of the output for the rotatedString. It also completely doesn't work for derp, instead printing out pderp with the same ?4??? in front. Where is this runtime error being caused?
EDIT
The answer given was correct, but the goal was to be able to accept rotations greater than the length of the given string. That code is below:
void rotate(char * startString, long int rotations) {
long int stringSize = strlen(startString);
long int breakIndex = (rotations % stringSize);
char *rotatedString = malloc(stringSize + 1); //counting extra char for null terminator
strncpy(rotatedString, startString + breakIndex, stringSize - breakIndex);
strncpy(rotatedString + stringSize - breakIndex, startString, breakIndex);
rotatedString[stringSize] = '\0'; // for the ending null character of the char array
printf("Result: %s\n", rotatedString);
free(rotatedString);
}
Your doublestring initialization allocates too little memory because you're using sizeof(startstring), which is the size of a pointer, not strlen(startstring) + 1 which is the length of the string including the terminating NUL character. This means your code is overwriting the end of the buffer with hilarous results. Try the following:
void rotate(char * startString, int rotation) {
int len = strlen(startString);
if (len == 0 || len <= rotation)
return;
char *rotatedString = malloc(len + 1); /* One extra char for the terminating NUL */
strncpy(rotatedString, startString + rotation, len - rotation);
strncpy(rotatedString + len - rotation, startString, rotation);
rotatedString[len] = '\0';
printf("%s\n", rotatedString);
free(rotatedString); /* don't leak memory! */
}

Find Every Position of a Given Char Within an Array

I made a small function that fills an allocated block of memory containing every position of a given char within a given string and returns a pointer to the memory block.
The only problem with this function is that there is no way to check the size of the memory block; so I also made a function that counts the occurrence of a given char within a string.
Here is an example of use:
/*count occurences of char within a given string*/
size_t strchroc(const char *str, const char ch)
{
int c = 0;
while(*str) if(*(str++) == ch) c++;
return c;
}
/*build array of positions of given char occurences within a given string*/
int *chrpos(const char *str, const char ch)
{
int *array, *tmp, c = 0, i = 0;
if(!(array = malloc(strlen(str) * sizeof(int)))) return 0x00;
while(str[c])
{
if(str[c] == ch) array[i++] = c;
c++;
}
if(!(tmp = realloc(array, i * sizeof(int)))) return 0x00;
array = tmp;
return array;
}
int main(void)
{
char *str = "foobar foobar"; //'o' occurs at str[1], str[2], str[8], and str[9]
int *array, b = 0, d;
if(!(array = chrpos(str, 'o'))) exit(1); //array[0] = 1, array[1] = 2, array[2] = 8, array[3] = 9
/*
* This is okay since I know that 'o'
* only occures 4 times in str. There
* may however be cases where I do not
* know how many times a given char
* occurs so I figure that out before
* utilizing the contents of array.
* I do this with my function strchroc.
* Below is a sample of how I would
* utilize the data contained within
* array. This simply prints out str
* and on a new line prints the given
* char's location within the str
* array
*/
puts(str);
while(b < (int) strchroc(str, 'o')) //loop once for each 'o'
{
for(d = 0; d < (b == 0 ? array[b] : array[b] - array[b - 1] - 1); d++) putc((int) ' ', stdout);
printf("%d", array[b]);
b++;
}
}
Output:
foobar foobar
12 89
My only concern is that if one of these two functions fail, there is no way for the data to be used correctly. I was thinking about making the number of occurrences of char within the string an argument for chrpos but even then I would still have to call both functions.
I was wondering if anybody had any suggestions for a way to do this so that I only need one function to build the array.
The only way I can think of is by storing the number of char occurrences into array[0] and having array[1] through array[char_occurences] holding the positions of char.
If anybody has a better idea I would greatly appreciate it.
As stated in my comment the first thing is to save up the data anyway, in case you can't shrink the allocated memory :
if (!(tmp = realloc(array, i * sizeof(int))))
return array;
return (tmp); //array = tmp; is useless
If you want to protect a bit more your strchroc function add a if (!str) return 0; at the beginning.
You can change your function so that it also "returns" the number of occurrences found. While we cannot actually return multiple values from a function in C, we can pass a pointer as a parameter and have the function write down a value using that pointer:
int *chrpos(const char *str, char ch, int *found) {
/*
...
*/
*found = i;
return array;
}
Note that you don't need the const modifier for ch.

Resources