Skip to content

Instantly share code, notes, and snippets.

@ehetherington
Created August 14, 2020 03:58
Show Gist options
  • Save ehetherington/5f0c432bccc78189ef1b506b97b951c1 to your computer and use it in GitHub Desktop.
Save ehetherington/5f0c432bccc78189ef1b506b97b951c1 to your computer and use it in GitHub Desktop.
Parse lines from a string
/**
* @file buf_splitter.c
*
* @brief Split a string into a series of records.
*
* @details Parse lines from a string in modifiable memory. A control structure
* is initialized first. Then substrings separated by the specified
* record-separator are returned by repeated calls to bs_next(). The end of
* available strings is indicated by bs_next() returning NULL.
*
* The string is treated as a series of records separated by a record
* separator. That separator is typically a newline, but is specified in the
* call to bs_init().
*
* The string may be returned to its original condition by calling bs_restore().
*
* No buffers are allocated, so none need to be freed.
*
* @author Edward Hetherington
*/
#define _GNU_SOURCE /**< for rawmemchr() */
#include <stdlib.h>
#include <string.h>
#include "buf_splitter.h"
/**
* @fn void bs_init(struct buf_splitter *, char *, int)
*
* @brief Initialize a control structure to manage the process,
*
* @details Replace all the Record Separator chars with '\0'. This requires the
* string to be in modifiable memory.
*
* @param splitter The control structure to be initialized.
* @param buf The buffer to be managed. It will be modified, but may be
* restored to it's original state.
* @param rs The character to be used as the Record Separator. Typically newline.
*/
void bs_init(struct buf_splitter *splitter, char *buf, int rs) {
if ((splitter == NULL) || (buf == NULL)) { return; }
bzero(splitter, sizeof(struct buf_splitter));
splitter->buf = buf;
splitter->rs = rs;
splitter->current_start = buf;
splitter->buf_end = rawmemchr(buf, '\0');
splitter->len = splitter->buf_end - splitter->buf;
for (char *p = splitter->buf; p < splitter->buf_end; p++) {
if (*p == splitter->rs) {
*p = '\0';
}
}
}
/**
* @fn void bs_restore(struct buf_splitter *)
* @brief Restore the buffer to its original state.
* @details
* Replace all the '\0' chars (except the one at the end of the buffer)
* with the original Record Separator.
*
* @param splitter The previously initialized control structure.
*/
void bs_restore(struct buf_splitter *splitter) {
if ((splitter == NULL) || (splitter->buf == NULL)) { return; }
for (char *p = splitter->buf; p < splitter->buf_end; p++) {
if (*p == '\0') {
*p = splitter->rs;
}
}
}
/**
* @fn char *bs_next(struct buf_splitter *)
* @brief Fetch the next record.
* @return A pointer to the next string, or NULL if there are no more.
*/
char *bs_next(struct buf_splitter *splitter) {
if ((splitter == NULL) || (splitter->buf == NULL)) { return NULL; }
if (splitter->current_start >= splitter->buf_end) {
return NULL;
}
/* set up for the next string, and return the current one */
char *retval = splitter->current_start;
if (*retval == '\0') { // at end of string, start next char
splitter->current_start++;
} else if (splitter->current_start < splitter->buf_end) { /* any left ? */
/* buffer is null terminated */
char *next = memchr(splitter->current_start,
'\0', splitter->buf_end - splitter->current_start);
if (next == NULL) { /* no more */
splitter->current_start = splitter->buf_end;
} else {
splitter->current_start = next + 1;
}
}
return retval;
}
#ifndef _REMOVE_ME_
#include <stdio.h>
#include "buf_splitter.h"
/**
* @fn int main(void)
*/
int main(void) {
/* string in r/w memory */
char string[] = {
"hello world\n"
"this is line 2\n"
"this is line 3\n"
};
/* print the original buffer */
printf("Original:\n%s", string);
/* initialize the control structure */
struct buf_splitter splitter;
bs_init(&splitter, string, '\n');
/* print the strings */
char *line;
printf("\nOne line at a time:\n");
while ((line = bs_next(&splitter))!= NULL) {
printf("%s\n", line);
}
/* restore to original condition */
bs_restore(&splitter);
/* print the restored buffer */
printf("\nRestored:\n%s", string);
return 0;
}
#endif
#ifndef _BUF_SPLITTER_
#define _BUF_SPLITTER_
struct buf_splitter {
char *buf; /**< the buffer being dissected */
int rs; /**< character to be used as a record separator */
char *buf_end; /**< the end of the buffer */
int len; /**< the length of the buffer */
char *current_start; /**< pointer to the next record to return */
};
void bs_init(struct buf_splitter *splitter, char *buf, int rs);
char *bs_next(struct buf_splitter *splitter);
void bs_restore(struct buf_splitter *splitter);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment