I'm attempting a variation to parse/tokenize a progmem string as in this forum post. I've been puttering with this and making no headway.
strpbrk() works and produces the correct output:
// tokenize string in progmem - search delimiter with strpbrk()
/* Description: Locate character in program space string.
char * strpbrk ( const char * s,
const char * accept
)
The strpbrk() function locates the first occurrence in the string s of any
of the characters in the flash string accept. This function is similar to
strpbrk() except that accept is a pointer to a string in program space.
Returns
The strpbrk() function returns a pointer to the character in s that matches
one of the characters in accept, or NULL if no such character is found.
The terminating zero is not considered as a part of string: if one or
both args are empty, the result will NULL.
*/
// https://forum.arduino.cc/index.php?topic=663437.0
char testData[] = "01:234:56:78-90:ab:cd:efg:hi-jklm-zztop";
const char delimiters[] = ":-";
const byte segmentSize = 5 ; // Segments longer than this will be skipped
char scratchPad[segmentSize + 1]; // A buffer to pull the progmem bytes into for printing/processing
// declare three pointers to the progmem string
char* nextSegmentPtr = testData; // points to first character of segment
char* delimiterPtr = testData; // points to detected delimiter character, or eos NULL
char* endOfData = testData + strlen(testData); // points to last character in string
void setup() {
Serial.begin(115200);
// Serial.println(__FILE__);
byte segmentLen; // number of characters in current segment
while (1) {
delimiterPtr = strpbrk(nextSegmentPtr, delimiters); // Locate target character in progmem string.
segmentLen = delimiterPtr - nextSegmentPtr;
if (delimiterPtr == nullptr) { // Hit end of string
segmentLen = endOfData - nextSegmentPtr;
}
if (segmentLen <= segmentSize) {
memcpy(scratchPad, nextSegmentPtr, segmentLen);
scratchPad[segmentLen] = '\0'; // Append terminator to extracted characters.
Serial.print(F("segmentLen "));
Serial.print(segmentLen);
Serial.print(F(" : "));
Serial.println(scratchPad);
}
else {
Serial.print(F("segment len "));
Serial.print(segmentLen);
Serial.println(F( " skipped"));
}
if (delimiterPtr == nullptr) { // ----- Exit while loop here -----
break;
}
nextSegmentPtr = nextSegmentPtr + segmentLen + 1;
}// end while
Serial.println(F("\n*** done ***"));
} // end setup
void loop() {
}
Output:
segmentLen 2 : 01
segmentLen 3 : 234
segmentLen 2 : 56
segmentLen 2 : 78
segmentLen 2 : 90
segmentLen 2 : ab
segmentLen 2 : cd
segmentLen 3 : efg
segmentLen 2 : hi
segmentLen 4 : jklm
segmentLen 5 : zztop*** done ***
The progmem version, strpbrk_P, although compiling and running without error produces only what is shown below.
// tokenize string in progmem - search delimiter with strpbrk_P()
/* Description: Locate character in program space string.
char * strpbrk_P ( const char * s,
const char * accept
)
The strpbrk_P() function locates the first occurrence in the string s of any
of the characters in the flash string accept. This function is similar to
strpbrk() except that accept is a pointer to a string in program space.
Returns
The strpbrk_P() function returns a pointer to the character in s that matches
one of the characters in accept, or NULL if no such character is found.
The terminating zero is not considered as a part of string: if one or
both args are empty, the result will NULL.
*/
// https://forum.arduino.cc/index.php?topic=663437.0
//--------------------------------------------
const char testData[] PROGMEM = "01:234:56:78-90:ab:cd:efg:hi-jklm-zztop";
const char delimiters[] PROGMEM = ":-";
const byte segmentSize = 5 ; // Segments longer than this will be skipped
char scratchPad[segmentSize + 1]; // A buffer to pull the progmem bytes into for printing/processing
// declare three pointers to the progmem string
PGM_P nextSegmentPtr = testData; // points to first character of segment
PGM_P delimiterPtr = testData; // points to detected delimiter character, or eos NULL
PGM_P endOfData = testData + strlen(testData); // points to last character in string
void setup() {
Serial.begin(115200);
// Serial.println(__FILE__);
byte segmentLen; // number of characters in current segment
while (1) {
delimiterPtr = strpbrk_P(nextSegmentPtr, delimiters); // Locate target character in progmem string.
segmentLen = delimiterPtr - nextSegmentPtr;
if (delimiterPtr == nullptr) { // Hit end of string
segmentLen = endOfData - nextSegmentPtr;
}
if (segmentLen <= segmentSize) {
memcpy_P(scratchPad, nextSegmentPtr, segmentLen);
scratchPad[segmentLen] = '\0'; // Append terminator to extracted characters.
Serial.print(F("segmentLen "));
Serial.print(segmentLen);
Serial.print(F(" : "));
Serial.println(scratchPad);
}
else {
Serial.print(F("segment len "));
Serial.print(segmentLen);
Serial.println(F( " skipped"));
}
if (delimiterPtr == nullptr) { // ----- Exit while loop here -----
break;
}
nextSegmentPtr = nextSegmentPtr + segmentLen + 1;
}// end while
Serial.println(F("\n*** done ***"));
} // end setup
void loop() {
}
segment len 39 skipped
*** done ***
From where I sit the programs differ only in the parts that address progmem. I have yet another nearly identical version which uses strchr_P to do the same thing as the strpbrk() version and it does produce correct output. It's not here 'cuz it put me over the posting size limit. I can add it in another post if necessary.
Anyhow, I think it's a forest and trees situation.
Open my eyes?