Pregunta

Tengo un demonio que lee un archivo de configuración con el fin de saber dónde escribir algo. En el archivo de configuración, una línea como ésta existe:

output = /tmp/foo/%d/%s/output

O, puede tener este aspecto:

output = /tmp/foo/%s/output/%d

... o simplemente como esto:

output = /tmp/foo/%s/output

... o por último:

output = /tmp/output

tengo esa línea como CFG-> pathfmt dentro de mi programa. Lo que estoy tratando de hacer ahora es subir con una cierta forma inteligente de usarla.

Un poco más explicación, el camino puede contener hasta dos componentes a ser formateada. % D se expandirá como un ID de trabajo (int),% s como un nombre de trabajo (cadena). El usuario puede querer usar uno, ambos o ninguno en el archivo de configuración. Necesito saber lo que quieren y en qué orden antes de que finalmente pasan a snprintf (). Puedo tipo de reducirlo, pero sigo queriendo hablar con strtok () y que parece fea.

Quiero dar a los usuarios de este tipo de flexibilidad, sin embargo me estoy perdido en busca de una manera sensata y portátil para ponerlo en práctica. También estoy en una pérdida total y completa de cómo comenzar a buscar esto.

Yo sería muy feliz si:

  • Alguien me podría ayudar a reducir el número de frase de búsqueda para encontrar buenos ejemplos
  • Alguien podría enviar un enlace a algún proyecto OSS implementación de este
  • Alguien podría publicar algunos pseudo código

No quiero que el código escrito para mí, sólo estoy realmente atascado en lo que (creo) debe ser algo muy simple y necesita un poco de ayuda de tomar el primer bocado. Realmente siento como si estuviera sobre el pensamiento y con vistas a lo obvio.

El resultado final debe ser una función booleana como esto:

bool output_sugar(const char *fmt, int jobid, const char *jobname, struct job *j);

Sería luego llamar snprintf () (sensatez) en j-> OUTPATH, volviendo falso si algún tipo de basura (es decir,% seguido por algo que no s, d or%) está en la línea de configuración (o su nula). Los chequeos son fáciles, sólo estoy teniendo un poco de tiempo para conseguir el número (y el orden) de argumentos para dar formato correcto.

Gracias de antemano. Además, no dude de editar este título si tiene la reputación de hacerlo, como ya he dicho, no estoy muy seguro de cómo hacer la pregunta en una sola línea. Creo que lo que necesito es un analizador , pero se siente incómodo usando un analizador léxico completo soplado / analizador para manejar una cadena sencilla.

¿Fue útil?

Solución

Sí, se necesita un analizador de algún tipo. No tiene que ser compleja, sin embargo:

void format_filename(const char *fmt, int jobid, const char *jobname,
                     char *buffer, size_t buflen)
{
    char *end = buffer + buflen - 1;
    const char *src = fmt;
    char *dst = buffer;
    char c;
    assert(buffer != 0 && fmt != 0 && buflen != 0 && jobname != 0);
    while ((c = *src++) != '\0')
    {
        if (dst >= end)
            err_exit("buffer overflow in %s(): format = %s\n",
                     __func__, fmt);
        else if (c != '%')
            *dst++ = c;
        else if ((c = *src++) == '\0' || c == '%')
        {
            *dst++ = '%';
            if (c == '\0')
                break;
        }
        else if (c == 's')
        {
            size_t len = strlen(jobname);
            if (len > end - dst)
                err_exit("buffer overflow on jobname in %s(): format = %s\n",
                         __func__, fmt);
            else
            {
                strcpy(dst, jobname);
                dst += len;
            }
        }
        else if (c == 'd')
        {
             int nchars = snprintf(dst, end - dst, "%d", jobid);
             if (nchars < 0 || nchars >= end - dst)
                 err_exit("format error on jobid in %s(); format = %s\n",
                          __func__, fmt);
             dst += nchars;
        }
        else
            err_exit("invalid format character %d in %s(): format = %s\n",
                     c, __func__, fmt);
    }
    *dst = '\0';
}

código Probado. Tenga en cuenta que es compatible con la notación '%%' para permitir al usuario para incrustar un solo '%' en la salida. Además, se trata a un único '%' en el extremo de la cadena como válida y equivalente a '%%'. Se llama err_exit () en caso de error; se puede elegir estrategias alternativas de error que se adapte a su sistema. Yo simplemente asumen que ha incluido <assert.h>, <stdio.h> y <string.h> y la cabecera de la función err_exit() (variadic).


Código de ensayo ...

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>

static void err_exit(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    exit(1);
}

... entonces format_filename() que el anterior, entonces ...

#define DIM(x) (sizeof(x)/sizeof(*(x)))

static const char *format[] =
{
    "/tmp/%d/name/%s",
    "/tmp/%s/number/%d",
    "/tmp/%s.%d%%",
    "/tmp/%",
};

int main(void)
{
    char buffer[64];
    size_t i;

    for (i = 0; i < DIM(format); i++)
    {
        format_filename(format[i], 1234, "job-name", buffer, sizeof(buffer));
        printf("fmt = %-20s; name = %s\n", format[i], buffer);
    }

    return(0);
}

Otros consejos

Uso strtok es un propenso a errores. Puede tratar las variables como un mini idioma mediante la lex (FL) y yacc. No es sencillo tutorial aquí

%{
#include <stdio.h>
%}

%%
%d                      printf("%04d",jobid);
%s                      printf("%s",stripspaces(dirname));
%%

Hice un derivador de ODBC que le permiten hacer cosas como dbprintf ( "inserta en valores bla% s% D% T% Y", cosas de aquí ...); Pero fue hace muchos años y me mordió y analiza la cadena de formato usando strtok.

Si el número de opciones es pequeño y que de otra manera no quieren / necesitan la flexibilidad y la complejidad de un programa de análisis, se puede, simplemente, buscar para cada potencial reemplazo subcadena utilizando strstr ().

Si usted tiene sólo dos opciones, puede crear tolerablemente a si estructura de cuatro ramificado / persona (sólo A, solamente B, ambos con A antes que B, ambos con B antes de A) en la que llamar sprintf () con los argumentos correctamente ordenados. De lo contrario, hacer múltiples llamadas sprintf (), cada uno de los cuales reemplaza sólo el primer reemplazo-marcador en la cadena de formato. (Esto implica la construcción de una lista de los que se necesitan reemplazos y ordenarlos en la apariencia de orden ...)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top