I'm making a system that reads sensor value from Arduino Uno through SoftwareSerial and publishes it via MQTT. However, the problem I'm facing I think is more general, I must admit I am new to c.
I'm reading the data, and splitting it into two const* variables that are defined on the top of my program.
When I read back the saved "data" and "topic" variable that I have parsed from the Serial connection, I only get garbage output, and usually a crash that restarts the device.
It prints them successfully inside the read-from-serial function, but it can't be correctly read later. Can it have something to do with how the data is saved? Can I explicitly allocate some memory for the variables?
I'm using a ESP8266 (ESP07) chip with lowered baud rate and proper voltage supply. It seems to be running well and stable.
#include <StringSplitter.h>
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include <time.h>
//const char* ssid = "xxxx";
//const char* password = "xxxx";
const char* ssid = "xxxx";
const char* password = "xxxx";
const char* mqttServer = "xxxx;
const int mqttPort = xxxx;
const char* mqttUser = "xxxx";
const char* mqttPassword = "xxxx";
int timezone = 1;
int dst = 0;
The data is stored here:
char* data;
char* topic;
boolean newData = false;
boolean unpublishedData = false;
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(19200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
configTime(timezone * 3600, dst * 0, "pool.ntp.org", "time.nist.gov");
client.setServer(mqttServer, mqttPort);
client.setCallback(callback);
while (!client.connected()) {
Serial.println("Connecting to MQTT...");
if (client.connect("ESP8266Client", mqttUser, mqttPassword )) {
Serial.println("connected");
} else {
Serial.print("failed with state ");
Serial.print(client.state());
delay(2000);
}
// wait and determine if we have a valid time from the network.
time_t now = time(nullptr);
Serial.print("Waiting for network time.");
while (now <= 1500000000) {
Serial.print(".");
delay(300); // allow a few seconds to connect to network time.
now = time(nullptr);
}
}
Serial.println("");
time_t now = time(nullptr);
Serial.println(ctime(&now));
String datatext = "val: ";
String timetext = ", time: ";
String dataToBeSent = "test";
String timeToBeSent = ctime(&now);
String publishString = datatext + dataToBeSent + timetext + timeToBeSent;
Serial.println("Attempting to publish: " + publishString);
client.publish("trykk/sensor0", (char*) publishString.c_str());
client.subscribe("trykk/sensor0");
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived in topic: ");
Serial.println(topic);
Serial.print("Message:");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
Serial.println("-----------------------");
}
void loop() {
client.loop();
recvWithStartEndMarkers();
showNewData();
publishReceived();
}
void publishReceived() {
if (unpublishedData) {
Serial.println("Hello from inside the publisher loop!");
time_t now = time(nullptr);
char* timeNow = ctime(&now);
It fails here, reading the data:
char publishText[30]; //TODO: make it JSON
strcpy( publishText, data );
strcat( publishText, " " );
strcat( publishText, timeNow );
Serial.print("publishText: ");
Serial.println(publishText);
Serial.print("topic: ");
Serial.println(topic);
client.publish(topic, publishText);
client.subscribe(topic);
unpublishedData = false;
} else if (!data) {
Serial.println("No data saved to array.");
} else if (!topic) {
Serial.println("No topic saved to array.");
}
}
void recvWithStartEndMarkers() {
int numChars = 32;
char receivedChars[numChars];
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
if (Serial.available() > 0) {
Serial.println("Hello from inside the receive loop!");
delay(100);
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
Serial.println("Reading from data line.");
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
Serial.println("Found the end marker.");
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
unpublishedData = true;
This part prints the values correctly back to me:
//Split the string
Serial.print("ESP debug: read: ");
Serial.println(receivedChars);
const char s[2] = ":";
*data = strtok(receivedChars, s);
Serial.print(data);
Serial.print(" ");
*topic = strtok(NULL, s);
Serial.println(topic);
}
}
else if (rc == startMarker) {
recvInProgress = true;
Serial.println("Found start marker");
}
}
}
}
//This is gutted as it gave me problems reading the variables
void showNewData() {
if (newData == true) {
Serial.print("This just in ... ");
Serial.print("Topic: ");
Serial.print("stuff");
Serial.print(", data: ");
Serial.println("more stuff");
newData = false;
}
}
From your code :
char* data;
...
*data = strtok(receivedChars, s);
strtok return a char* but you do *data = strtok(...) while data is itself a (non initialized) char *, this is non consistent, and you have a first 'chance' to have a crash because you write at a random address.
If you do not have the crash and your program can continue data is not modified by itself and stay uninitialized.
In
strcpy( publishText, data );
...
Serial.print(data);
When you use data as a char* doing Serial.print(data); and strcpy( publishText, data ); you read from a random (and certainly invalid) address, producing your crash.
To correct just replace *data = strtok(receivedChars, s); by data = strtok(receivedChars, s);
After fixing the assignment of strtok's result to data as shown in bruno's answer there is another bug that can lead to a crash.
Your function loop() calls recvWithStartEndMarkers() first, then publishReceived().
void loop() {
client.loop();
recvWithStartEndMarkers();
showNewData();
publishReceived();
}
In function recvWithStartEndMarkers you read some data into a local array receivedChars, feed this into strtok and write a pointer returned from strtok to a global variable data.
void recvWithStartEndMarkers() {
int numChars = 32;
char receivedChars[numChars]; /* this is a local variable with automatic storage */
/* ... */
while (Serial.available() > 0 && newData == false) {
/* ... */
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
/* ... */
receivedChars[ndx] = '\0'; // terminate the string
/* Now there is a terminated string in the local variable */
/* ... */
//Split the string
/* ... */
const char s[2] = ":";
data = strtok(receivedChars, s); /* strtok modifies the input in receivedChars and returns a pointer to parts of this array. */
/* ... */
}
After leaving the function the memory that was receivedChars is no longer valid. This means data will point to this invalid memory on the stack.
Later you want to access the global variable data in a function publishReceived(). Accessing this memory is is unspecified behavior. You may still get the data, you may get something else or your program may crash.
void publishReceived() {
/* ... */
char publishText[30]; //TODO: make it JSON
strcpy( publishText, data ); /* This will try to copy whatever is now in the memory that was part of receivedChars inside recvWithStartEndMarkers() but may now contain something else, e.g. local data of function publishReceived(). */
/* ... */
To fix this you could use strdup in recvWithStartEndMarkers():
data = strtok(receivedChars, s);
if(data != NULL) data = strdup(data);
Then you have to free(data) somewhere when you no longer need the data or before calling recvWithStartEndMarkers() again.
Or make data an array and use strncpy in recvWithStartEndMarkers().
Related
I'm having a difficult time understanding how the size of my datatypes relates to parsing serial information through an Arduino mega2560. I'm trying to input large integers through the serial monitor, such as the integer 8000000, but negative numbers or numbers significantly smaller are being returned.
The following code is a demo I modified. A user must proceed and conclude serial inputs between less than and greater than signs (<'NUMBER>):
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use by strtok() function
// variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;
boolean newData = false;
//============
void setup() {
Serial.begin(9600);
// Serial.println("This demo expects 3 pieces of data - text, an integer and a floating point value");
// Serial.println("Enter data in this style <text,12,24.7> ");
Serial.println();
}
//============
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() replaces the commas with \0
parseData();
showParsedData();
newData = false;
}
}
//============
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
//============
void parseData() {
// split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
// strtokIndx = strtok(tempChars,","); // get the first part - the string
// strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
strtokIndx = strtok(tempChars, ","); // this continues where the previous call left off
integerFromPC = atoi(strtokIndx); // convert this part to an integer
// strtokIndx = strtok(NULL, ",");
// floatFromPC = atof(strtokIndx); // convert this part to a float
}
//============
void showParsedData() {
Serial.print("Message ");
Serial.println(messageFromPC);
Serial.print("Integer ");
Serial.println(integerFromPC);
Serial.print("Float ");
Serial.println(floatFromPC);
}
I suspect my problem relates to the datatypes of my variables.
Hello I am developing a program for the Raspberry in C (the in-progress project can be found here).
I noted there are some errors in the task1 function so I created an equivalent program in my Desktop (running Ubuntu) to find the error, where the task1 was readapted as below:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stub.h"
#include "globals.h"
#include "utils.h"
#include <pthread.h>
#define INPIN 25
void takePic(char picname[24]);
//This thread reads the PIR sensor output
void task1()
{
unsigned char val_read = 0;
static unsigned char alarm_on = FALSE;
static const unsigned int maxNumPics=10;
static char folderPath[] = "/home/usr/Documents/alarmSys_rasp/alarm_new/pics/";
char fileNameTodelete[73];
fileNameTodelete[0] = '\0';
strcat(fileNameTodelete, folderPath);
//INITIALIZING
pinMode(INPIN, INPUT);
//create folder where to save pics
createFolder(folderPath);
char* names[24];
char picname[24];
int res = 0;
picname[0] = '\0';
static unsigned int numPicsInFolder;
//delete if more than 10 files
while((numPicsInFolder = filesByName(names, folderPath))>maxNumPics)
{
fileNameTodelete[0] = '\0';
strcat(fileNameTodelete, folderPath);
strcat(fileNameTodelete, names[0]);
printf("%s\n", fileNameTodelete);
remove(fileNameTodelete);
}
static unsigned int nexEl;
nexEl = numPicsInFolder % maxNumPics;
printf("Entering while\n");
while(1)
{
//static const unsigned int del = 300;
val_read = digitalRead(INPIN);
if (val_read && (!alarm_on)) //motion detected
{
printf("\nDetected movement\n");
if (numPicsInFolder >= maxNumPics)
{
printf("\nMax num pics\n");
fileNameTodelete[0] = '\0';
strcat(fileNameTodelete, folderPath);
strcat(fileNameTodelete, names[nexEl]);
printFiles(names, numPicsInFolder);
printf("File to be deleted %d: %s, ", nexEl, names[nexEl]);
//printf("%s\n", fileNameTodelete);
if ((res = remove(fileNameTodelete))!=0)
{
printf("Error deleting file: %d\n", res);
}
}
else
{
printf("\nNot reached max num pics\n");
numPicsInFolder++;
}
//update buffer
takePic(picname);
printf("value returned by takePic: %s\n", picname);
//names[nexEl] = picname;
strcpy(names[nexEl], picname); //ERROR HERE
printFiles(names, numPicsInFolder);
printf("curr element %d: %s\n",nexEl, names[nexEl]);
nexEl++;
nexEl %= maxNumPics;
printf("\nDetected movement: alarm tripped\n\n");
alarm_on = TRUE;
/*Give some time before another pic*/
}
else if (alarm_on && !val_read)
{
alarm_on = FALSE;
printf("\nAlarm backed off\n\n");
}
}
}
void takePic(char picname[24])
{
/*Build string to take picture*/
int err;
//finalcmd is very long
char finalcmd[150];
finalcmd[0] = '\0';
getDateStr(picname);
char cmd1[] = "touch /home/usr/Documents/alarmSys_rasp/alarm_new/pics/";
char cmdlast[] = "";
strcat(finalcmd, cmd1);
strcat(picname, ".jpg");
strcat(finalcmd, picname);
strcat(finalcmd, cmdlast);
system(finalcmd);
if ((err=remove("/var/www/html/*.jpg"))!=0)
{
printf("Error deleting /var/www/html/*.jpg, maybe not existing\n" );
}
//system(finalcmd_ln);
//pthread_mutex_lock(&g_new_pic_m);
g_new_pic_flag = TRUE;
printf("\nPicture taken\n\n");
}
DESCRIPTION
The main function calls the task1 function defined in the file task1.c. The function creates a file in the folder ./pics/ every time the condition (val_read && (!alarm_on)) is verified (in the simulation this condition is satisfied every 2 loops). The function allows only 10 files in the folder. If there are already 10, it deletes the oldest one and creates the new file by calling the function takePic.
The name of files are stored in a array of strings char* names[24]; and the variable nexEl points to the element of this array having the name of the oldest file so that it is replaced with the name of the new file just created.
PROBLEM
The problem is the following: the array char* names[24] is correctly populated at the first iteration but already in the second iteration some elements are overwritten. The problem arises when the folder has the maximum number of files (10) maybe on the update of the array.
It seems the calls to printf overwrite some of its elements so that for example one of them contains the string "Error deleting /var/www/html/*.jpg, maybe not existing\n" printed inside the funtion takePic.
What am I missing or doing wrong with the management of arrays of strings?
UTILITIES FUNCTIONS
To be complete here are shortly described and reported other functions used in the program.
The function getDateStr builds a string representing the current date in the format yyyy_mm_dd_hh_mm_ss.
The function filesByName builds an array of strings where each string is the name of a file in the folder ./ ordered from the last file created to the newest.
The function printFiles prints the previous array.
void getDateStr(char str[20])
{
char year[5], common[3];
time_t t = time(NULL);
struct tm tm = *localtime(&t);
str[0]='\0';
sprintf(year, "%04d", tm.tm_year+1900);
strcat(str, year);
sprintf(common, "_%02d", tm.tm_mon + 1);
strcat(str, common);
sprintf(common, "_%02d", tm.tm_mday);
strcat(str, common);
sprintf(common, "_%02d", tm.tm_hour);
strcat(str, common);
sprintf(common, "_%02d", tm.tm_min);
strcat(str, common);
sprintf(common, "_%02d", tm.tm_sec);
strcat(str, common);
//printf("%s\n", str);
}
unsigned int countFiles(char* dir)
{
unsigned int file_count = 0;
DIR * dirp;
struct dirent * entry;
dirp = opendir(dir); /* There should be error handling after this */
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_type == DT_REG) { /* If the entry is a regular file */
file_count++;
}
}
return file_count;
}
void printFiles(char* names[24], unsigned int file_count)
{
for (int i=0; i<file_count; i++)
{
printf("%s\n", names[i]);
}
}
unsigned int filesByName(char* names[24], char* dir)
{
unsigned int file_count = 0;
DIR * dirp;
struct dirent * entry;
dirp = opendir(dir); /* There should be error handling after this */
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_type == DT_REG) { /* If the entry is a regular file */
//strncpy(names[file_count], entry->d_name,20);
//names[file_count] = malloc(24*sizeof(char));
names[file_count] = entry->d_name;
file_count++;
}
}
closedir(dirp);
char temp[24];
if (file_count>0)
{
for (int i=0; i<file_count-1; i++)
{
for (int j=i; j<file_count; j++)
{
if (strcmp(names[i], names[j])>0)
{
strncpy(temp, names[i],24);
strncpy(names[i], names[j],24);
strncpy(names[j], temp, 24);
}
}
}
}
return file_count;
}
For the simulation I created also the following function (digitalRead is actually a function of the wiringPi C library for the Raspberry):
int digitalRead(int INPIN)
{
static int res = 0;
res = !res;
return res;
}
In task1, you have char *names[24]. This is an array of char pointers.
In filesByName, you do
names[file_count] = entry->d_name;
but should be doing
names[file_count] = strdup(entry->d_name);
because you can't guarantee that d_name persists or is unique after the function returns or even within the loop. You were already close with the commented out malloc call.
Because you call filesByName [possibly] multiple times, it needs to check for names[file_count] being non-null so it can do a free on it [to free the old/stale value from a previous invocation] before doing the strdup to prevent a memory leak.
Likewise, in task1,
strcpy(names[nexEl], picname); //ERROR HERE
will have similar problems and should be replaced with:
if (names[nexEl] != NULL)
free(names[nexEl]);
names[nexEl] = strdup(picname);
There may be other places that need similar adjustments. And, note that in task1, names should be pre-inited will NULL
Another way to solve this is to change the definition of names [everywhere] from:
char *names[24];
to:
char names[24][256];
This avoids some of the malloc/free actions.
getDateStr() uses a char buffer that is always too small. Perhaps other problems exists too.
void getDateStr(char str[20]) {
char year[5], common[3];
....
sprintf(common, "_%02d", tm.tm_mon + 1); // BAD, common[] needs at least 4
Alternative with more error checking
char *getDateStr(char *str, size_t sz) {
if (str == NULL || sz < 1) {
return NULL;
}
str[0] = '\0';
time_t t = time(NULL);
struct tm *tm_ptr = localtime(&t);
if (tm_ptr == NULL) {
return NULL;
}
struct tm tm = *tm_ptr;
int cnt = snprintf(year, sz, "%04d_%02d_%02d_%02d_%02d_%02d",
tm.tm_year+1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
if (cnt < 0 || cnt >= sz) {
return NULL;
}
return str;
}
So I'm trying to combine two strings together but I'm getting str is a read only value on the last line of the second while loop. Is their anyone can I do this without changing the function header?
Also String is a struct I created that has a *char called str.
String * String_Combine(String * tar, const String * src) {
//end of string
while (* tar->str != '\0') {
tar->str++;
}
//int i = 0;
//copy string
while (*source->str != '\0') {
*tar->str = *src->str;
*tar->str++;
*src->str++;
// i++;
}
return tar;
}
Copy the pointer before modifying it. I guess modifying tar->str may also be harmful because it will destroy the information that where the string starts.
String * String_Combine(String * tar, const String * src) {
char * tar_str = tar->str, * src_str = src->str;
//end of string
while (* tar_str != '\0') {
tar_str++;
}
//int i = 0;
//copy string
while (*src_str != '\0') { /* assuming that "source" is a typo and it should be "src" */
*tar_str = *src_str; /* with hope that there is enough writable buffer allocated */
tar_str++;
src_str++;
// i++;
}
//terminate string
*tar_str = '\0';
return tar;
}
Two things:
Make sure you allocate enough memory for tar->strto hold both tar->str and src->str
Store a pointer tar/src->str locally and iterate thought them so you wouldn't loose the pointer to original str;
I wrote a test case for you to understand it easily ;)
#include <iostream>
#include <string.h>
struct String {
char* str;
};
using namespace std;
String * String_Combine(String * tar, const String * src) {
// take a local copy of strs
char* tar_str = tar->str;
char* src_str = src->str;
//end of string
while (* tar_str != '\0') {
tar_str++;
}
//int i = 0;
//copy src string to tar string
while (*src_str != '\0') {
*tar_str = *src_str;
*tar_str++;
*src_str++;
// i++;
}
return tar;
}
int main()
{
String* src = (String*) malloc(sizeof(String));
src->str = new char[20];
strcpy(src->str, "World!");
String* tar = (String*) malloc(sizeof(String));
tar->str = new char[20];
strcpy(tar->str, "Hello ");
String* result = String_Combine(tar,src);
cout << result->str << endl;
return 0;
}
I sending a string like this:
$13,-14,283,4,-4,17,6,-240,-180#
But is not showing up because the buffer is 'overloading', how can I receive the whole string or how can I clear it after each byte read?
// get a character string
char *getsU2(char *s, int len) {
char *p = s; // copy the buffer pointer
do {
*s = getU2(); // get a new character
if (( *s=='\r') || (*s=='\n')) // end of line...
break; // end the loop s++;
// increment the buffer pointer
len--;
} while (len>1); // until buffer is full
*s = '\0'; // null terminate the string
return p; // return buffer pointer
}
// get a character string
char *getsU2(char *s, int len) {
char *p = s; // copy the buffer pointer
do {
*s = getU2(); // get a new character
if (( *s=='\r') || (*s=='\n')) // end of line...
break; // end the loop
s++;
// increment the buffer pointer
len--;
} while (len>1); // until buffer is full
*s = '\0'; // null terminate the string
return p; // return buffer pointer
}
char getU2(void) {
if(U2STAbits.OERR == 1)
{ U2STAbits.OERR = 0; }
while (!U2STAbits.URXDA); // wait for new character to arrive return U2RXREG;
// read character from the receive buffer }
getsU2(buffer,sizeof(buffer));
Try using the UART receive interrupt. Code below is for a PIC24H; modify appropriately.
In your init function:
IFS0bits.U1RXIF = 0; // clear rx interrupt flag
IFS0bits.U1TXIF = 0; // clear tx interrupt flag
IEC0bits.U1RXIE = 1; // enable Rx interrupts
IPC2bits.U1RXIP = 1;
IEC0bits.U1TXIE = 1; // enable tx interrupts
IPC3bits.U1TXIP = 1;
Create an interrupt handler that places the bytes into a buffer or queue:
void __attribute__((__interrupt__, auto_psv)) _U1RXInterrupt(void)
{
char bReceived;
// Receive Data Ready
// there is a 4 byte hardware Rx fifo, so we must be sure to get all read bytes
while (U1STAbits.URXDA)
{
bReceived = U1RXREG;
// only usethe data if there was no error
if ((U1STAbits.PERR == 0) && (U1STAbits.FERR == 0))
{
// Put your data into a queue
FIFOPut(bReceived);
}
}
IFS0bits.U1RXIF = 0; // clear rx interrupt flag
}
Your queue code is along these lines:
#define FIFO_SIZE 64
char pbBuffer[FIFO_SIZE];
char *pbPut;
char *pbGet;
void FIFOInit(void)
{
pbPut = pbBuffer;
pbGet = pbBuffer;
}
void FIFOPut(char bInput)
{
*pbPut = bInput;
pbPut++;
if (pbPut >= (pbBuffer + FIFO_SIZE))
pbPut = pbBuffer;
}
char FIFOGet(void)
{
char bReturn;
bReturn = *pbGet;
pbGet++;
if (pbGet>= (pbBuffer + FIFO_SIZE))
pbGet= pbBuffer;
}
Obviously, one should beef up the FIFO functions to prevent overflow, return errors on an empty queue, etc.
I have a string like that:
4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;
It is separated into sections by ";" and each section can have one or more key/value pairs like 5=1 and so on, as you can see.
I want to parse it in pure C and I started working with strtok as I am showing in code here:
const wuint8 section_delimiter[] = ";";
const wuint8 field_delimiter[] = ",";
const wuint8 value_delimiter[] = "=";
printf("%s\n",data->msg);
token = strtok(data->msg,section_delimiter);
while(token != NULL) {
indicator = atoi(token);
printf("indicator: %d\n", indicator);
switch(indicator) {
case TYPE_1: {
printf("type: %d\n",TYPE_1);
wuint16 i, headerType, headerSubType;
for(i = 1; i < TP_MAX; i++) {
if(i == atoi(token)) {
token = strtok(NULL,value_delimiter);
headerType = i;
headerSubType = atoi(token);
break;
}
}
break;
}
case TYPE_2: {
printf("type: %d\n",TYPE_3);
break;
}
case TYPE_3: {
printf("type: %d\n",TYPE_3);
break;
}
case TYPE_4: {
printf("type: %d\n",TYPE_4);
break;
}
I am not sure how to do that correctly.
It also gets complicated, because not every string has the same structure, sometimes only one or two sections can be present. E.g.: 3;4=3;1=0,2=2,3=1,4=1,5=1;
Is there a how to do that showing the best and most convenient way?
strtok can't, AFAICR, be used in nested loops like this due to the global state it manages itself.
I suggest parsing each semicolon-delimited part out first, then handling them sequentially - or just implement something akin to strtok for your semicolon case yourself, then happily use strtok in the inner loop.
Using strcspn(). Fixed buffers, results go into global variables. data[] buffer is altered (and thus needs to be writable). YMMV
/*
It is separated into sections by ";" and each section can have one or more
key/value pairs like 5=1 and so on, as you can see. I want to parse it in
pure C and I started working with strtok as I am showing in code here:
*/
char data[] = "4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;" ;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct header {
int l;
int r;
} headers[123];
unsigned nheader;
int indicator;
char rest [123];
int tokenise(char * buff);
unsigned tokenise2(struct header *dst, char * buff);
/****************/
int tokenise(char * buff)
{
char *ptrs[14];
unsigned nptr;
unsigned len, pos;
ptrs[nptr=0] = NULL;
for (len = pos=0; buff[pos]; pos += len ) {
len = strcspn(buff+pos, ";");
ptrs[nptr++] = buff+pos;
ptrs[nptr] = NULL;
if (!buff[pos+len] ) break;
buff[pos+len] = 0;
len +=1;
}
if ( nptr> 0 && ptrs[0]) indicator = atoi(ptrs[0]); else indicator = -1;
if ( nptr> 1 && ptrs[1]) nheader = tokenise2 (headers, ptrs[1] ); else nheader = 0;
if ( nptr> 2 && ptrs[2]) nheader += tokenise2 (headers+nheader, ptrs[2] ); else nheader += 0;
if ( nptr> 3 && ptrs[3]) strcpy (rest, ptrs[3]); else rest[0] = 0;
return 0; /* or something useful ... */
}
unsigned tokenise2(struct header *target, char * buff)
{
char *ptrs[123];
unsigned nptr, iptr;
unsigned len, pos;
ptrs[nptr=0] = NULL;
for (len = pos=0; buff[pos]; pos += len ) {
len = strcspn(buff+pos, "," );
ptrs[nptr++] = buff+pos;
ptrs[nptr] = NULL;
if (!buff[pos+len] ) break;
buff[pos+len] = 0;
len +=1;
}
for ( iptr=0; iptr < nptr; iptr++) {
if (! ptrs[iptr] ) break;
len = strcspn(ptrs[iptr], "=" );
if (!len) break;
target[iptr].l = atoi (ptrs[iptr] );
target[iptr].r = atoi (ptrs[iptr]+len+1 );
}
return iptr; /* something useful ... */
}
int main(void)
{
int rc;
unsigned idx;
fprintf(stderr, "Org=[%s]\n", data );
rc = tokenise(data);
printf("Indicator=%d\n", indicator );
for (idx=0; idx < nheader; idx++) {
printf("%u: %d=%d\n", idx, headers[idx].l , headers[idx].r );
}
printf("Rest=%s\n", rest );
return 0;
}