How to easily parse an array from an INI file to an array in C? - c

In my code I use the iniparser (https://github.com/ndevilla/iniparser) to parse double, int and strings. However, I'm interested into parsing arrays, delimited with comma such as
arr = val1, val2, ..., valn
Any easy and quick way, like the parser above?

The best way is to make your own structure.
You can find easily on the web structures that you can import for your code. Another solution, not so great is to put your values as void pointers. And when you want to take your values back you take the void pointer and cast it with which kind of value you want( int,double,char ect). But this may conflict the values so you must be careful. You must know what kind value is in which cell of the pointer. It is not the ideal way but its a cheat way to avoid making your own structure.

You can use libconfini, which has array support.
test.conf:
arr = val1, val2, ..., valn
test.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <confini.h>
struct my_conf_T {
size_t arrlen;
char ** arr;
};
static int my_listnr (IniDispatch * this, void * v_conf) {
#define conf ((struct my_conf_T *) v_conf)
if (ini_string_match_si("arr", this->data, this->format)) {
conf->arrlen = ini_array_get_length(this->value, INI_COMMA, this->format);
if (!conf->arrlen || !this->v_len) {
/* Array is empty */
return 0;
}
conf->arr = (char **) malloc(conf->arrlen * sizeof(char *) + this->v_len + 1);
if (!conf->arr) {
fprintf(stderr, "malloc() failed\n");
exit(1);
}
char * remnant = (char *) ((char **) conf->arr + conf->arrlen);
memcpy(remnant, this->value, this->v_len + 1);
for (size_t idx = 0; idx < conf->arrlen; idx++) {
conf->arr[idx] = ini_array_release(&remnant, INI_COMMA, this->format);
ini_string_parse(conf->arr[idx], this->format);
}
}
return 0;
#undef conf
}
int main () {
struct my_conf_T my_conf = (struct my_conf_T) { 0 };
if (load_ini_path("test.conf", INI_DEFAULT_FORMAT, NULL, my_listnr, &my_conf)) {
fprintf(stderr, "Sorry, something went wrong :-(\n");
return 1;
}
if (my_conf.arr) {
/* Do something with `my_conf.arr`... */
for (size_t idx = 0; idx < my_conf.arrlen; idx++) {
printf("arr[%zu] = %s\n", idx, my_conf.arr[idx]);
}
free(my_conf.arr);
}
return 0;
}
Output:
arr[0] = val1
arr[1] = val2
arr[2] = ...
arr[3] = valn
P.S. I happen to be the author.

Related

Is there a way to split an array of strings into subarray of strings on token

Basically, is there any way to split an array of strings into arrays of strings before and after a token ("|") in C.
An example is shown below.
char *input[] = {"hello","I","am","|","a","cool","|","guy"}
//code
and the result is 3 arrays, containing
{"Hello","I","am"}
{"a","cool"}
{"guy"}
I tried strtok but that seems to split a string into pieces, rather than an array of strings into new, separate, sub-arrays of strings. I also do not know exactly how many "|" tokens will be present, and will need an unknown amount of new arrays (safe to say it'd be less than 10). They will be passed to execvp so having it as one string and just remembering where to start and stop looking will not work.
They will be passed to execvp
Assuming the strings include the program to be executed (the 1st parameter to execvp()) and the strings will be used in the order of appearance as per this pointer-array
char *input[] = {"hello","I","am","|","a","cool","|","guy"}
then a possible simple solution without any duplications might look like this:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
char * input[] = {"hello", "I", "am", "|",
"a", "cool", "|",
"guy", "|"}; /* note the additional trailing `"|"`. */
int main(void)
{
char ** pcurrent = input;
char ** pend = pcurrent + sizeof input / sizeof *input;
while (pcurrent < pend)
{
{
char ** ptmp = pcurrent;
while (ptmp < pend && **ptmp != '|')
{
++ptmp;
}
*ptmp = NULL;
}
{
pid_t pid = fork();
if ((pid_t) -1) == pid)
{
perror("fork() failed");
exit(EXIT_FAILURE);
}
if ((pid_t) 0) == pid) /* child */
{
execvp(pcurrent[0], pcurrent);
perror("execvp() failed");
exit(EXIT_FAILURE);
}
/* parent */
pcurrent = ptmp + 1;
}
} /* while (pcurrent < pend) */
} /* int main(void) */
You need manually to split the input array. And dynamically allocate a new place to store result. E.g. as:
#include <stdio.h>
#include <stdbool.h>
int main()
{
char *input[] = {"hello","I","am","|","a","cool","|","guy"};
int inputLength = sizeof(input)/sizeof(input[0]);
printf("inputLength - %d\n", inputLength);
const char ***result2DimArray = malloc(sizeof(char**) * inputLength);
int *result2DimArrayLengths = malloc(sizeof(int) * inputLength);
memset(result2DimArrayLengths, 0, sizeof(int) * inputLength);
const char **currentSection = 0;
int nextSectionNumber = 0;
for(int inputIndex = 0; inputIndex < inputLength; inputIndex++)
{
if(input[inputIndex][0] == '|')
{
currentSection = 0;
}
else
{
if(!currentSection)
{
currentSection = malloc(sizeof(char*) * inputLength);
result2DimArray[nextSectionNumber] = currentSection;
nextSectionNumber++;
}
*currentSection = input[inputIndex];
currentSection++;
result2DimArrayLengths[nextSectionNumber-1]++;
}
}
/*Checking the result*/
printf("total sections - %d\n", nextSectionNumber);
for(int i=0; i<nextSectionNumber;i++)
{
for(int j=0;j<result2DimArrayLengths[i];j++)
{
printf(result2DimArray[i][j]);
printf(", ");
}
puts("");
}
return 0;
}
Here is a solution which doesn't involve dynamic memory allocation.
Before going in to the details ...
I think it's useful when tackling a problem like this to think about how the "strings" are stored in memory. It might look something like in the attached picture. (The memory addresses are completely unrealistic - and there would be null terminators at the end of each string - but you get the idea).
As the picture shows, the vital information we need for each 'sub-array' can be stored in a <char **, int> pair. The char ** is the address of the first "string" in the sub-array; the int is the number of strings it contains.
We can use a struct string_array_t to store this information.
typedef struct {
// Pointer to first string in sub-array
char **p;
// Number of strings in sub-array
int count;
} string_array_t;
We allocate an array of these on the stack; thus no need for malloc() or free() - as long as we allocate enough sub-arrays.
string_array_t string_arrays[MAX_SUB_ARRAYS] = {0};
char *input[] = {"hello", "I", "am", "|", "a", "cool", "|", "guy"};
// Pointer to current sub-array
string_array_t *cur = NULL;
size_t n_sub_arrays = 1;
Initialize our counters and pointers:
int i = 0, j = 0, k = 0;
cur = &string_arrays[0];
size_t n_strings_total = sizeof(input) / sizeof(input[0]);
Then loop over the array.
for (i = 0; i < n_strings_total; i++) {
if (!strcmp(input[i], "|")) {
// Store total number of strings in this sub-array
cur->count = k;
k = 0;
// Switch to next sub-array
cur = &string_arrays[++j];
if (j >= MAX_SUB_ARRAYS) {
fprintf(stderr, "Not enough sub-arrays allocated ...\n");
break;
}
n_sub_arrays++;
continue;
}
if (k == 0) {
cur->p = &input[i];
}
k++;
}
cur->count = k;
Print the results.
printf("Found %zu sub arrays ...\n", n_sub_arrays);
for (i = 0; i < n_sub_arrays; i++) {
string_array_t *cur = &string_arrays[i];
for (j = 0; j < cur->count; j++) {
printf("%s ", *(cur->p++));
}
printf("\n");
}

Growing arrays. Refer to the elements by pointers, not indexes

Since the array address may change when memory is reallocated,
the main part of the program (in the body of the function main ()) should refer to the elements by
indexes, not pointers. Why?
Can you show an example of accessing items with pointers?
(Sorry for my English).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Nameval Nameval;
struct Nameval {
char *name;
int value;
};
struct NVtab {
int nval; /* current number of values */
int max; /* allocated number of values */
Nameval *nameval; /* array of name-value pairs */
};
enum {NVINIT = 1, NVGROW = 2};
/* addname: add new name and value to nvtab */
int addname(struct NVtab *nvtab, Nameval newname) {
Nameval *nvp;
if (nvtab->nameval == NULL) { /* first time */
nvtab->nameval = (Nameval *) malloc(NVINIT * sizeof(Nameval));
if (nvtab->nameval == NULL)
return -1;
nvtab->max = NVINIT;
nvtab->nval = 0;
} else if (nvtab->nval >= nvtab->max) { /* grow */
nvp = (Nameval *) realloc(nvtab->nameval,
(NVGROW*nvtab->max)*sizeof(Nameval));
if (nvp == NULL)
return -1;
nvtab->max *= NVGROW;
nvtab->nameval = nvp;
}
nvtab->nameval[nvtab->nval] = newname;
return nvtab->nval++;
}
int main(void) {
struct NVtab nvtab = {0, 0, NULL};
int curnum;
curnum = addname(&nvtab, (Nameval) {.name="Andy", .value=12});
printf("%d\n", curnum);
curnum = addname(&nvtab, (Nameval) {.name="Billy", .value=18});
printf("%d\n", curnum);
curnum = addname(&nvtab, (Nameval) {.name="Jack", .value=71});
printf("%d\n", curnum);
for (int i = 0; i < nvtab.nval; i++) {
printf("%s %d\n", nvtab.nameval[i].name,
nvtab.nameval[i].value);
}
}
For example, why can`t we show array like this:
for (int i = 0; i < nvtab.nval; i++)
printf("%s %d\n", nvtab.*(nameval+i).name, nvtab.*(nameval+i).value);
You are not supposed to assign a pointer calculated for a specific index to a variable with storage duration which could extend over an insert operation.
That pointer could become invalid, so the lesson behind that example is to always re-evaluate iterators on dynamic data structures.
E.g. what not to do:
auto *foo = &nvtab.nameval[i];
addname(&nvtab, (Nameval) {.name="Billy", .value=18});
printf("%s %d\n", foo->name, foo->value);
In the last line it can work or crash. Depending on whether realloc moved the allocation or resized in-place. Except that you can never know for sure until you execute it, as it isn't even fully deterministic.
This is not valid syntax:
nvtab. *(nameval+i).name
The member access operator . expects to be followed by the name of the member. What you want is:
(*(nvtab.nameval+i)).name

How to create an array consisting of the tokens created using the strtok function?

I am new with .ini files and thus this qn(which might seem silly) .I have created a .ini file and access it via my C program. The ini file looks like this:
[key]
title = A,H,D
The C program accesses it using:
LPCSTR ini ="C:\\conf.ini;
char var[100];
GetPrivateProfileString("key", "title", 0, var, 100, ini);
printf("%s", var);
char* buffer = strtok(var, ", ");
do{
printf("%s", buffer);
if (strcmp(buffer, "A")==0)
printf("Hello");
puts("");
}while ((buffer=strtok(NULL, ", "))!= NULL);
output looks as :
A H D F G IAHello
H
D
F
G
Now what I need to do is use these individual tokens again to form an array with indices within my C program. For example:
char x[A, H, D, F, G]
so that when I refer to the index 2, x[2] should give me 'D'. Could somebody suggest a way to do this. I have never used strtok before and thus very confused. Thank you in advance.
This question is quite similar to others regarding getting external information and storing it in an array.
The problem here is the amount of elements in your array to store.
You could use Link-lists, but for this example, I would scan the file, getting the total amount of items needed for the array - and then parse the file data again - storing the items in the array.
The first loop, goes through and counts the items to be store, as per your example posted. I will do the second loop just as an example - please note in my example you would of created nTotalItems and have counted the amount of items, storing that in nTotalItems ... I am assuming you want to store a string, not just a char...
Also please note this a draft example, done at work - only to show a method of storing the tokens into an array, therefore there is no error checking ec
// nTotalItems has already been calculated via the first loop...
char** strArray = malloc( nTotalItems * sizeof( char* ));
int nIndex = 0;
// re-setup buffer
buffer = strtok(var, ", ");
do {
// allocate the buffer for string and copy...
strArray[ nIndex ] = malloc( strlen( buffer ) + 1 );
strcpy( strArray[ nIndex ], buffer );
printf( "Array %d = '%s'\n", nIndex, strArray[ nIndex ] );
nIndex++;
} while ((buffer=strtok(NULL, ", "))!= NULL);
Just use an INI parser that supports arrays.
INI file:
[my_section]
title = A,H,D
C program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <confini.h>
#define MY_ARRAY_DELIMITER ','
struct configuration {
char ** title;
size_t title_length;
};
static char ** make_strarray (size_t * arrlen, const char * src, const size_t buffsize, IniFormat ini_format) {
*arrlen = ini_array_get_length(src, MY_ARRAY_DELIMITER, ini_format);
char ** const dest = *arrlen ? (char **) malloc(*arrlen * sizeof(char *) + buffsize) : NULL;
if (!dest) { return NULL; }
memcpy(dest + *arrlen, src, buffsize);
char * iter = (char *) (dest + *arrlen);
for (size_t idx = 0; idx < *arrlen; idx++) {
dest[idx] = ini_array_release(&iter, MY_ARRAY_DELIMITER, ini_format);
ini_string_parse(dest[idx], ini_format);
}
return dest;
}
static int ini_handler (IniDispatch * this, void * v_conf) {
struct configuration * conf = (struct configuration *) v_conf;
if (this->type == INI_KEY && ini_string_match_si("my_section", this->append_to, this->format)) {
if (ini_string_match_si("title", this->data, this->format)) {
/* Save memory (not strictly needed) */
this->v_len = ini_array_collapse(this->value, MY_ARRAY_DELIMITER, this->format);
/* Allocate a new array of strings */
if (conf->title) { free(conf->title); }
conf->title = make_strarray(&conf->title_length, this->value, this->v_len + 1, this->format);
if (!conf->title) { return 1; }
}
}
return 0;
}
static int conf_init (IniStatistics * statistics, void * v_conf) {
*((struct configuration *) v_conf) = (struct configuration) { NULL, 0 };
return 0;
}
int main () {
struct configuration my_conf;
/* Parse the INI file */
if (load_ini_path("C:\\conf.ini", INI_DEFAULT_FORMAT, conf_init, ini_handler, &my_conf)) {
fprintf(stderr, "Sorry, something went wrong :-(\n");
return 1;
}
/* Print the parsed data */
for (size_t idx = 0; idx < my_conf.title_length; idx++) {
printf("my_conf.title[%d] = %s\n", idx, my_conf.title[idx]);
}
/* Free the parsed data */
if (my_conf.title_length) {
free(my_conf.title);
}
return 0;
}
Output:
my_conf.title[0] = A
my_conf.title[1] = H
my_conf.title[2] = D

EXCEPTION_ACCESS_VIOLATION when using large array in C

this is another issue I'm stuck with. First of all I'm trying to read a level-4 matlab file which contains information exported from PicoScope 6, it reads four arrays from the file, A, Tstart, Tinterval and Length. Array number one is the largest by far, it contains 1000004 values, however the other three only contains one value each. When I exectue the code below it successfully reads the file, stores it into a multidimensional array but fails when I'm trying to use the array.
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <matio.h>
#include "ReadMatFile.h"
//macros
#define getLength(x) (sizeof(x) / sizeof(x[0]))
//variables
double *dataMatrix[4];
int innerSize[getLength(dataMatrix)];
//functions
jobjectArray convertToArray(JNIEnv *env, double **data, int length1D,
int *length2D);
JNIEXPORT jdoubleArray JNICALL Java_ReadMatFile_readMatFile(JNIEnv *env,
jobject object, jstring str) {
const char *fileName = (*env)->GetStringUTFChars(env, str, 0);
mat_t *matfp;
matvar_t *matvar;
matfp = Mat_Open(fileName, MAT_ACC_RDONLY | MAT_FT_MAT4);
if ( NULL == matfp) {
fprintf(stderr, "Error opening MAT file %s\n", fileName);
return NULL;
}
int i = 0;
while ( NULL != (matvar = Mat_VarReadNext(matfp))) {
double *data = (double*) (matvar->data);
dataMatrix[i] = data;
innerSize[i] = (int) matvar->nbytes / matvar->data_size;
Mat_VarFree(matvar);
matvar = NULL;
i++;
}
Mat_Close(matfp);
(*env)->ReleaseStringUTFChars(env, str, fileName);
int s;
for(s = 0; s < innerSize[0]; s++)
printf("A[%d] = %e\n", s, dataMatrix[0][s]); /* Fails here */
return NULL;
//return convertToArray(env, dataMatrix, getLength(dataMatrix) ,innerSize);
}
jobjectArray convertToArray(JNIEnv *env, double **data, int length1D,
int *length2D) {
jsize outerSize = (jsize) length1D;
jclass class = (*env)->FindClass(env, "[D");
jobjectArray outer = (*env)->NewObjectArray(env, outerSize, class, 0);
jsize i;
for (i = 0; i < outerSize; i++) {
jsize innerSize = (jsize) length2D[i];
jdoubleArray inner = (*env)->NewDoubleArray(env, innerSize);
(*env)->SetDoubleArrayRegion(env, inner, 0, innerSize, data[i]);
(*env)->SetObjectArrayElement(env, outer, i, inner);
(*env)->DeleteLocalRef(env, inner);
}
return outer;
}
What is the cause of this? It creates a minidump when I run this application. Is the array too large?
A fix for this and also a explanation of what is wrong would be much appreciated!
Thanks in advance folks.
I suspect your problem is in the following code:
double *data = (double*) (matvar->data);
dataMatrix[i] = data;
innerSize[i] = (int) matvar->nbytes / matvar->data_size;
Mat_VarFree(matvar); // whoopsie
If Mat_VarFree does what I think it does, matvar->data is no longer a valid pointer, meaning dataMatrix[i] is no longer a valid pointer, hence the crash.
I think what you intend to do is something more along the lines of
innerSize[i] = matvar->nbytes / matvar->data_size;
dataMatrix[i] = malloc( sizeof *dataMatrix[i] * innerSize[i] );
if ( dataMatrix[i] )
memcpy( dataMatrix[i], matvar->data, matvar->nbytes );
Mat_VarFree( matvar );
that is, create a local copy of the data in matvar->data and save it to your dataMatrix. In your original code, all you copied was a pointer value; you never created a separate copy of your data.

How to pass a pointer to array of structures into a function

pointers always get me in C programing.
I am having trouble, I want to pass a pointer to an array of structs into a function so it can modify the structs and then pass the members of the array can be used in other functions later. The problem is when I think I index the array and point it to the modified struct then I try to look at the members later they aren't the modified values. Here is some of my code
typedef struct
{
int rows;
int columns;
int *data;
} Mat;
int main(void)
{
Mat result, decoded_result;
int result_data[8] =
{ 0, 0, 0, 0, 0, 0, 0, 0 };
int decoded_data[4] =
{ 0, 0, 0, 0 };
result.columns = 1;
result.rows = 8;
result.data = &result_data[0];
decoded_result.columns = 1;
decoded_result.rows = 4;
decoded_result.data = &decoded_data[0];
Mat m1, m2, m3, m4, m5;
m1.rows = m2.rows = m3.rows = m4.rows = m5.rows = 4;
m1.columns = m2.columns = m3.columns = m4.columns = m5.columns = 1;
int md1[4], md2[4], md3[4], md4[4], md5[4];
m1.data = &md1[0], m2.data = &md2[0], m3.data = &md3[0], m4.data = &md4[0], m5.data =
&md5[0];
Mat mat_array[10] =
{ m1, m2, m3, m4, m5 };
decode_data(&result, &decoded_result, mat_array);
return 0;
}
int decode_data(Mat *result, Mat *decoded_result, Mat *mat_array)
{
int ii;
int size_of_EEPROM = 5;
//steps to decode data
for (ii = 0; ii < size_of_EEPROM; ii++)
{
decode(result, decoded_result); //decodes hamming 8,4, works
mat_array[ii] = *decoded_result; ///This is where the problem is
}
return 0;
}
Thanks in advance for the help with pointers :)
As Mat carries a pointer, simply assigning Mat a to Mat b won't work. At least not for the data referenced by Mat's member data.
What's needed to be done here is also called a Deep Copy. Deep coping would also create a copy of what is referenced by data.
Below is an example of how this could be done for Mat.
Note: As negative rows and columns are of no use you'd better declare Mat like this:
typedef struct
{
size_t rows;
size_t columns;
int * data;
} Mat;
(As size_t is defined to be unsigned this kind of declaration makes it unnecessary to test for negative values carried by the members rows and columns before allocating the new data when deep-coping as shown below)
#include <stdlib.h> /* for malloc(), size_t */
#include <string.h> /* for memcpy() */
#include <errno.h> /* for errno, ENOMEM, EINVAL */
...
/* Deep-copies src to dst. */
/* Returns 0 on success or -1 on error. Sets errno in the latter case. */
int mat_copy(Mat * dst, const Mat * src)
{
if ((!dst) || (!src))
{
errno = EINVAL;
return -1;
}
dst->rows = src->row;
dst->columns = src->columns
dst->data = NULL;
if (src->data)
{
size_t size = dst->rows * dst->columns * sizeof (*(dst->data));
dst->data = malloc(size);
if (!dst->data)
{
errno = ENOMEM;
return -1;
}
memcpy(dst->data, src->data, size);
}
return 0;
}
There is rule of three in C++ when using pointers. It says that if you need one of following then you need other two always. These three are Destructor/Copy Constructor/Assign Operator.
So what happen's in your scenario. When you write mat_array[ii] = *decoded_result it actually makes:
mat_array[ii].rows = decoded_result.rows;
mat_array[ii].columns = decoded_result.columns;
mat_array[ii].data = decoded_result.data // here just assign pointers, not the entire data.
In this case you have to make assignment operator to make actual copy of data.

Resources