Извлечение целых чисел из произвольного текста и сохранение их в динамическом массиве

Минимальные векторные динамические массивы, min_array.h. Это совсем не ново, но поскольку я использую много подобных функций в более крупных проектах, я надеялся получить комментарии по документации, особенно по именам и интерфейсу.

/** Macro for a type-specific minimal dynamic array: `MIN_ARRAY(name, type)`,
 where `name` is an identifier prefix that satisfies `C` naming conventions
 when mangled and `type` is defined tag-type associated therewith. When
 expanding the array, resizing may be necessary and incurs amortised cost; any
 pointers to this memory may become stale. */

#include <stdlib.h> /* size_t realloc free */
#include <string.h> /* memmove strcmp memcpy */
#include <errno.h>  /* errno */
#include <assert.h> /* assert */

#define MIN_ARRAY_IDLE { 0, 0, 0 }
#define MIN_ARRAY(name, type) 
struct name##_array { type *data; size_t size, capacity; }; 
/** Initialises `a` to idle. */ 
static void name##_array(struct name##_array *const a) 
    { assert(a); a->data = 0; a->capacity = a->size = 0; } 
/** Destroys `a` and returns it to idle. */ 
static void name##_array_(struct name##_array *const a) 
    { assert(a); free(a->data); name##_array(a); } 
/** Ensures `min_capacity` of `a`. @param[min_capacity] If zero, does nothing.
@return Success; otherwise, `errno` will be set.
@throws[ERANGE] Tried allocating more then can fit in `size_t` or `realloc`
doesn't follow POSIX. @throws[realloc, ERANGE] */ 
static int name##_array_reserve(struct name##_array *const a, 
    const size_t min_capacity) { 
    size_t c0; 
    type *data; 
    const size_t max_size = (size_t)-1 / sizeof *a->data; 
    assert(a); 
    if(a->data) { 
        if(min_capacity <= a->capacity) return 1; 
        c0 = a->capacity; 
    } else { /* Idle. */ 
        if(!min_capacity) return 1; 
        c0 = 1; 
    } 
    if(min_capacity > max_size) return errno = ERANGE, 0; 
    /* `c_n = a1.625^n`, approximation golden ratio `phi ~ 1.618`. */ 
    while(c0 < min_capacity) { 
        size_t c1 = c0 + (c0 >> 1) + (c0 >> 3) + 1; 
        if(c0 > c1) { c0 = max_size; break; } 
        c0 = c1; 
    } 
    if(!(data = realloc(a->data, sizeof *a->data * c0))) 
        { if(!errno) errno = ERANGE; return 0; } 
    a->data = data, a->capacity = c0; 
    return 1; 
} 
/** Makes sure that there are at least `buffer` contiguous, un-initialised,
 elements at the back of `a`.
 @return A pointer to the start of `buffer` elements, namely `a.data + a.size`.
 If `a` is idle and `buffer` is zero, a null pointer is returned, otherwise
 null indicates an error and `errno` will be set. @throws[realloc, ERANGE] */ 
static type *name##_array_buffer(struct name##_array *const a, 
    const size_t buffer) { 
    assert(a); 
    if(a->size > (size_t)-1 - buffer) 
        { errno = ERANGE; return 0; } /* Unlikely. */ 
    if(!name##_array_reserve(a, a->size + buffer)) return 0; 
    return a->data ? a->data + a->size : 0; 
} 
/** Adds `n` to the size of `a`; `n` must be smaller than or equal to the
 highest remaining buffer value set by <fn:<name>_array_buffer>. */ 
static void name##_array_emplace(struct name##_array *const a, 
    const size_t n) { 
    assert(a && a->capacity >= a->size && n <= a->capacity - a->size); 
    a->size += n; 
} 
/** @return Push back a new un-initialized datum of `a`.
@throws[realloc, ERANGE] */ 
static type *name##_array_new(struct name##_array *const a) { 
    type *const data = name##_array_buffer(a, 1); 
    return data ? name##_array_emplace(a, 1), data : 0; 
} 
/* It's perfectly valid that these functions are not used. */ 
static void name##_unused_coda(void); static void name##_unused(void) { 
    name##_array(0); name##_array_new(0); name##_unused_coda(); } 
static void name##_unused_coda(void) { name##_unused(); }

Использование min_array.h буферизовать поток и извлекать из него информацию; в данном случае числа. Это использует однопроходный режим и расширяет буфер по мере необходимости. С самого начала FILE будет скопирован в память, я использовал fread вместо того, чтобы идти построчно.

/** This is a test for `min_array.h`: takes an arbitrary stream, (Cntl-d stops
 in some implementations of interactive terminals,) and greedily extracts
 integers in the style of `strtol` until it gets to the end-of-file or a
 null-terminator. */

#include <stdlib.h> /* size_t EXIT_ strtol */
#include <stdio.h>  /* FILE fopen fclose fread [f]printf ferror perror */
#include <errno.h>  /* errno */
#include <ctype.h>  /* isdigit */
#include <limits.h> /* LONG_ INT_ _MIN _MAX */
#include <assert.h> /* assert */
#include "min_array.h"

struct num { size_t line; int num; };

MIN_ARRAY(char, char)
MIN_ARRAY(num, struct num)

/** Reads the contents of `fp` after the file pointer and copies them to
 `string`; adds a null-terminator.
 @return Success, otherwise `string` may have read a partial read and may not
 be terminated. @throws[fread, malloc] */
static int fp_to_str(FILE *const fp, struct char_array *const string) {
    const size_t granularity = 1024;
    size_t nread;
    char *cursor;
    assert(fp && string);
    do {
        if(!(cursor = char_array_buffer(string, granularity))) return 0;
        char_array_emplace(string, nread = fread(cursor, 1, granularity, fp));
    } while(nread == granularity); assert(nread < granularity);
    if(ferror(fp) || !(cursor = char_array_new(string))) return 0;
    *cursor="";
    return 1;
}

/** Stores the entire stream and then stores the extracted `int` numbers. */
int main(void) {
    int success = EXIT_FAILURE;
    struct char_array str = MIN_ARRAY_IDLE;
    struct num_array nums = MIN_ARRAY_IDLE;
    struct num *num;
    long big_num;
    char *a, *anum = 0;
    size_t i, line = 1 /* Unix: delimited by 'n'. */;
    errno = 0; /* In case we are running it as part of another editor. */
    if(!fp_to_str(stdin, &str)) goto catch;
    /* It would be conceivable use the length to continue past the first
     sentinel '', but not that complex. */
    fprintf(stderr, "Read:n<<<%s>>>n", str.data);
    for(a = str.data; a = strpbrk(a, "-0123456789n"); ) {
        if(*a == 'n') { line++; a++; continue; }
        if(*a == '-') { anum = a++; continue; }
        if(!anum || anum != a - 1) anum = a; /* Wasn't negative. */
        big_num = strtol(anum, &a, 0);
        if((!big_num || big_num == LONG_MIN || big_num == LONG_MAX) && errno)
            goto catch; /* Long conversion failed, (not all platforms.) */
        if(big_num < INT_MIN || big_num > INT_MAX)
            { errno = ERANGE; goto catch; }
        if(!(num = num_array_new(&nums))) goto catch;
        num->line = line;
        num->num  = (int)big_num;
    }
    fprintf(stderr, "Extracted:n");
    for(i = 0; i < nums.size; i++) printf("Line %lu: %dn",
        (unsigned long)nums.data[i].line, nums.data[i].num);
    success = EXIT_SUCCESS;
    goto finally;
catch:
    fprintf(stderr, "Line %lu ", (unsigned long)line), perror("stdin");
finally:
    char_array_(&str);
    num_array_(&nums);
    return success;
}

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *