¿Analizando argumentos de línea de comando en C?


98

Estoy tratando de escribir un programa que pueda comparar dos archivos línea por línea, palabra por palabra o carácter por carácter en C. Tiene que poder leer en las opciones de la línea de comandos -l -w -i or --...

  • si la opción es -l, compara los archivos línea por línea.
  • si la opción es -w, compara los archivos palabra por palabra.
  • si la opción es, automáticamente asume que el siguiente argumento es el primer nombre de archivo.
  • si la opción es -i, los compara sin diferenciar entre mayúsculas y minúsculas.
  • por defecto compara los archivos carácter a carácter.

No se supone que importe cuántas veces se ingresan las opciones siempre que -w y -l no se ingresen al mismo tiempo y no haya más o menos de 2 archivos.

Ni siquiera sé por dónde empezar a analizar los argumentos de la línea de comandos. POR FAVOR AYUDA :(

Así que este es el código que se me ocurrió para todo. Todavía no he comprobado el error, pero me preguntaba si estoy escribiendo cosas de una manera demasiado complicada.

/*
 * Functions to compare files.
 */
int compare_line();
int compare_word();
int compare_char();
int case_insens();

/*
 * Program to compare the information in two files and print message saying 
 * whether or not this was successful.
 */
int main(int argc, char* argv[])
{
/*Loop counter*/
  size_t i = 0;

  /*Variables for functions*/
  int caseIns = 0;
  int line = 0;
  int word = 0;

  /*File pointers*/
  FILE *fp1, *fp2;

  /*
   * Read through command-line arguments for options.
   */
  for (i = 1; i < argc; i++) {
    printf("argv[%u] = %s\n", i, argv[i]);
    if (argv[i][0] == '-') {
       if (argv[i][1] == 'i') 
       {
           caseIns = 1;
       }
       if (argv[i][1] == 'l')
       {
           line = 1;
       }
       if (argv[i][1] == 'w')
       {
           word = 1;
       }
       if (argv[i][1] == '-')
       {
           fp1 = argv[i][2];
           fp2 = argv[i][3];
       }
       else 
       {
           printf("Invalid option.");
           return 2;
       }
    } else {
       fp1(argv[i]);
       fp2(argv[i][1]);
    }
  }

  /*
   * Check that files can be opened.
   */
  if(((fp1 = fopen(fp1, "rb")) ==  NULL) || ((fp2 = fopen(fp2, "rb")) == NULL))
  {
      perror("fopen()");
      return 3;
  }
  else{
        if (caseIns == 1)
        {
            if(line == 1 && word == 1)
            {
                printf("That is invalid.");
                return 2;
            }
            if(line == 1 && word == 0)
            {
                if(compare_line(case_insens(fp1, fp2)) == 0)
                        return 0;
            }
            if(line == 0 && word == 1)
            {
                if(compare_word(case_insens(fp1, fp2)) == 0)
                    return 0;
            }
            else
            {
                if(compare_char(case_insens(fp1,fp2)) == 0)
                    return 0;
            }
        }
        else
        {
            if(line == 1 && word == 1)
            {
                printf("That is invalid.");
                return 2;
            }
            if(line == 1 && word == 0)
            {
                if(compare_line(fp1, fp2) == 0)
                    return 0;
            }
            if(line == 0 && word == 1)
            {
                if(compare_word(fp1, fp2) == 0)
                    return 0;
            }
            else
            {
                if(compare_char(fp1, fp2) == 0)
                    return 0;
            }
        }

  }
    return 1;
    if(((fp1 = fclose(fp1)) == NULL) || (((fp2 = fclose(fp2)) == NULL)))
        {
            perror("fclose()");
            return 3;
        }
        else
        {
            fp1 = fclose(fp1);
            fp2 = fclose(fp2);
        }
}

/*
 * Function to compare two files line-by-line.
 */
int compare_line(FILE *fp1, FILE *fp2)
{
    /*Buffer variables to store the lines in the file*/
    char buff1 [LINESIZE];
    char buff2 [LINESIZE];

    /*Check that neither is the end of file*/
    while((!feof(fp1)) && (!feof(fp2)))
    {
        /*Go through files line by line*/
        fgets(buff1, LINESIZE, fp1);
        fgets(buff2, LINESIZE, fp2);
    }
    /*Compare files line by line*/
    if(strcmp(buff1, buff2) == 0)
    {
        printf("Files are equal.\n");
        return 0;
    }
    printf("Files are not equal.\n");
    return 1;
}   

/*
 * Function to compare two files word-by-word.
 */
int compare_word(FILE *fp1, FILE *fp2)
{
    /*File pointers*/
    FILE *fp1, *fp2;

    /*Arrays to store words*/
    char fp1words[LINESIZE];
    char fp2words[LINESIZE];

    if(strtok(fp1, " ") == NULL || strtok(fp2, " ") == NULL)
    {
        printf("File is empty. Cannot compare.\n");
        return 0;
    }
    else
    {
        fp1words = strtok(fp1, " ");
        fp2words = strtok(fp2, " ");

        if(fp1words == fp2words)
        {
            fputs(fp1words);
            fputs(fp2words);
            printf("Files are equal.\n");
            return 0;
        }
    }
    return 1;
}

/*
 * Function to compare two files character by character.
 */
int compare_char(FILE *fp1,FILE *fp2)
{
    /*Variables to store the characters from both files*/
    int c;
    int d;

    /*Buffer variables to store chars*/
    char buff1 [LINESIZE];
    char buff2 [LINESIZE];

    while(((c = fgetc(fp1))!= EOF) && (((d = fgetc(fp2))!=EOF)))
    {
        if(c == d)
        {
            if((fscanf(fp1, "%c", buff1)) == (fscanf(fp2, "%c", buff2)))
            {
                printf("Files have equivalent characters.\n");
                return 1;
                break;
            }
        }

    }
        return 0;
}

/*
 * Function to compare two files in a case-insensitive manner.
 */
int case_insens(FILE *fp1, FILE *fp2, size_t n)
{
    /*Pointers for files.*/
    FILE *fp1, *fp2;

    /*Variable to go through files.*/
    size_t i = 0;

    /*Arrays to store file information.*/
    char fp1store[LINESIZE];
    char fp2store[LINESIZE];

    while(!feof(fp1) && !feof(fp2))
    {
         for(i = 0; i < n; i++)
         {
                fscanf(fp1, "%s", fp1store);
                fscanf(fp2, "%s", fp2store);

                fp1store = tolower(fp1store);
                fp2store = tolower(fp2store);

                return 1;
         }
    }
    return 0;
}

No estoy muy seguro de cómo usar getopt () ... aún no lo he aprendido en mi clase.
user1251020

3
Entonces, vaya y lea la página del manual; no es muy complejo, y la página de manual probablemente incluye un ejemplo para que experimente (y si su página de manual local no lo hace, ciertamente puede encontrar ejemplos en la web).
Jonathan Leffler

1
Esta es una biblioteca de alto nivel: argparse en c, muy fácil de usar.
Cofyc


Para cosas simples, puede simplemente rodar las suyas en lugar de usar una biblioteca. Escribí un tutorial de introducción aquí engineeringterminal.com/computer-science/tutorials/…
nalyd88

Respuestas:


188

Que yo sepa, las tres formas más populares de analizar los argumentos de la línea de comandos en C son:

  • Getopt ( #include <unistd.h>de la biblioteca POSIX C), que puede resolver tareas simples de análisis de argumentos . Si está un poco familiarizado con bash, el getopt incorporado de bash se basa en Getopt de la libc de GNU.
  • Argp ( #include <argp.h>de la biblioteca GNU C), que puede resolver tareas más complejas y se ocupa de cosas como, por ejemplo:
    • -?, --helppara recibir mensajes de ayuda , incluida la dirección de correo electrónico
    • -V, --versionpara obtener información sobre la versión
    • --usagepara mensaje de uso
  • Hágalo usted mismo , que no recomiendo para programas que se le darían a otra persona, ya que hay demasiadas cosas que podrían salir mal o bajar la calidad. El error común de olvidarse de '-' para detener el análisis de opciones es solo un ejemplo.

La documentación de la biblioteca GNU C tiene algunos buenos ejemplos para Getopt y Argp.

Ejemplo de uso de Getopt

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    bool isCaseInsensitive = false;
    int opt;
    enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;

    while ((opt = getopt(argc, argv, "ilw")) != -1) {
        switch (opt) {
        case 'i': isCaseInsensitive = true; break;
        case 'l': mode = LINE_MODE; break;
        case 'w': mode = WORD_MODE; break;
        default:
            fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    // Now optind (declared extern int by <unistd.h>) is the index of the first non-option argument.
    // If it is >= argc, there were no non-option arguments.

    // ...
}

Ejemplo para usar Argp

#include <argp.h>
#include <stdbool.h>

const char *argp_program_version = "programname programversion";
const char *argp_program_bug_address = "<your@email.address>";
static char doc[] = "Your program description.";
static char args_doc[] = "[FILENAME]...";
static struct argp_option options[] = { 
    { "line", 'l', 0, 0, "Compare lines instead of characters."},
    { "word", 'w', 0, 0, "Compare words instead of characters."},
    { "nocase", 'i', 0, 0, "Compare case insensitive instead of case sensitive."},
    { 0 } 
};

struct arguments {
    enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode;
    bool isCaseInsensitive;
};

static error_t parse_opt(int key, char *arg, struct argp_state *state) {
    struct arguments *arguments = state->input;
    switch (key) {
    case 'l': arguments->mode = LINE_MODE; break;
    case 'w': arguments->mode = WORD_MODE; break;
    case 'i': arguments->isCaseInsensitive = true; break;
    case ARGP_KEY_ARG: return 0;
    default: return ARGP_ERR_UNKNOWN;
    }   
    return 0;
}

static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };

int main(int argc, char *argv[])
{
    struct arguments arguments;

    arguments.mode = CHARACTER_MODE;
    arguments.isCaseInsensitive = false;

    argp_parse(&argp, argc, argv, 0, 0, &arguments);

    // ...
}

Ejemplo para hacerlo usted mismo

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{   
    bool isCaseInsensitive = false;
    enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE;
    size_t optind;
    for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) {
        switch (argv[optind][1]) {
        case 'i': isCaseInsensitive = true; break;
        case 'l': mode = LINE_MODE; break;
        case 'w': mode = WORD_MODE; break;
        default:
            fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]);
            exit(EXIT_FAILURE);
        }   
    }   

    // *argv points to the remaining non-option arguments.
    // If *argv is NULL, there were no non-option arguments.

    // ...
}   

Descargo de responsabilidad: soy nuevo en Argp, el ejemplo puede contener errores.


9
Respuesta realmente completa, gracias Christian (upvoted). Sin embargo, los usuarios de mac deben tener en cuenta que el enfoque argp no es compatible con varias plataformas. Como encontré aquí , Argp es una extensión de API glibc no estandarizada. Está disponible en gnulib, por lo que se puede agregar a un proyecto de forma explícita. Sin embargo, probablemente sea más sencillo para los desarrolladores de solo mac o multiplataforma utilizar el enfoque getopt.
thclark

1
Para la versión hágalo usted mismo, no me gusta que las opciones permitan texto adicional después, como -wzzz analiza lo mismo que -w, y también que las opciones tienen que venir antes de los argumentos del archivo.
Jake

1
@Jake tienes razón. Respeto por ver eso. No recuerdo si me di cuenta de eso cuando lo escribí. De nuevo, es un ejemplo perfecto de que el bricolaje es tan fácil de equivocarse y, por lo tanto, no debería hacerse. Gracias por decirme, podría arreglar el ejemplo.
Christian Hujer

18

Utilice getopt(), o quizás getopt_long().

int iflag = 0;
enum { WORD_MODE, LINE_MODE } op_mode = WORD_MODE;  // Default set
int opt;

while ((opt = getopt(argc, argv, "ilw") != -1)
{
    switch (opt)
    {
    case 'i':
        iflag = 1;
        break;
    case 'l':
        op_mode = LINE_MODE;
        break;
    case 'w':
        op_mode = WORD_MODE;
        break;
    default:
        fprintf(stderr, "Usage: %s [-ilw] [file ...]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
}

/* Process file names or stdin */
if (optind >= argc)
    process(stdin, "(standard input)", op_mode);
else
{
    int i;
    for (i = optind; i < argc; i++)
    {
        FILE *fp = fopen(argv[i], "r");
        if (fp == 0)
            fprintf(stderr, "%s: failed to open %s (%d %s)\n",
                    argv[0], argv[i], errno, strerror(errno));
        else
        {
            process(fp, argv[i], op_mode);
            fclose(fp);
        }
    }
 }

Tenga en cuenta que debe determinar qué encabezados incluir (lo hago 4 que son obligatorios), y la forma en que escribí el op_modetipo significa que tiene un problema en la función process(): no puede acceder a la enumeración allí. Es mejor mover la enumeración fuera de la función; incluso podría crear op_modeuna variable de alcance de archivo sin vinculación externa (una forma elegante de decirlo static) para evitar pasarla a la función. Este código no se maneja -como sinónimo de entrada estándar, otro ejercicio para el lector. Tenga en cuenta que getopt()automáticamente se encarga de --marcar el final de las opciones por usted.

No he ejecutado ninguna versión de la escritura anterior más allá de un compilador; podría haber errores en él.


Para obtener crédito adicional, escriba una función (biblioteca):

int filter(int argc, char **argv, int idx, int (*function)(FILE *fp, const char *fn));

que encapsula la lógica para procesar las opciones de nombre de archivo después del getopt()ciclo. Debe manejarse -como entrada estándar. Tenga en cuenta que usar esto indicaría que op_modedebería ser una variable de alcance de archivo estático. La filter()función toma argc, argv, optindy un puntero a la función de procesamiento. Debería devolver 0 (EXIT_SUCCESS) si pudo abrir todos los archivos y todas las invocaciones de la función reportadas 0, de lo contrario 1 (o EXIT_FAILURE). Tener una función de este tipo simplifica la escritura de programas de 'filtro' estilo Unix que leen archivos especificados en la línea de comando o en la entrada estándar.


No me gusta que getopt () no permita opciones después del primer archivo.
Jake

POSIX getopt()no lo hace; GNU lo getopt()hace por defecto. Elige tu opción. No estoy interesado en las opciones después del comportamiento de los nombres de archivo, principalmente porque no es confiable en todas las plataformas.
Jonathan Leffler

14

Encontré que Gengetopt es bastante útil: usted especifica las opciones que desea con un archivo de configuración simple y genera un par .c / .h que simplemente incluye y vincula con su aplicación. El código generado hace uso de getopt_long, parece manejar los tipos más comunes de parámetros de línea de comando y puede ahorrar mucho tiempo.

Un archivo de entrada de gengetopt podría verse así:

version "0.1"
package "myApp"
purpose "Does something useful."

# Options
option "filename" f "Input filename" string required
option "verbose" v "Increase program verbosity" flag off
option "id" i "Data ID" int required
option "value" r "Data value" multiple(1-) int optional 

Generar el código es fácil y escupe cmdline.hy cmdline.c:

$ gengetopt --input=myApp.cmdline --include-getopt

El código generado se integra fácilmente:

#include <stdio.h>
#include "cmdline.h"

int main(int argc, char ** argv) {
  struct gengetopt_args_info ai;
  if (cmdline_parser(argc, argv, &ai) != 0) {
    exit(1);
  }
  printf("ai.filename_arg: %s\n", ai.filename_arg);
  printf("ai.verbose_flag: %d\n", ai.verbose_flag);
  printf("ai.id_arg: %d\n", ai.id_arg);
  int i;
  for (i = 0; i < ai.value_given; ++i) {
    printf("ai.value_arg[%d]: %d\n", i, ai.value_arg[i]);
  }
}

Si necesita hacer alguna verificación adicional (como asegurarse de que los indicadores sean mutuamente excluyentes), puede hacerlo con bastante facilidad con los datos almacenados en la gengetopt_args_infoestructura.


1 ++ excepto que genera código que genera advertencias :(
cat

Sí, lamentablemente. Pongo excepciones en mi archivo cmake.
davidA

Probablemente solo use los pragmas de GCC para ignorar las advertencias para ese archivo (horrible, lo sé)
cat

Tenga en cuenta que, obviamente, los perderá si regenera la fuente, por lo que es posible que desee aplicarlos como un parche en su proceso de construcción. Francamente, me resultó más fácil desactivar las advertencias en esos archivos específicos.
davidA

bueno no, me refiero a poner los pragmas alrededor del #include, no en el archivo generado en sí. para mí, apagar las advertencias está prohibido :-)
cat

6

Estoy muy sorprendido de que nadie haya mencionado el paquete "opt" de James Theiler.

Puede encontrar optar en http://public.lanl.gov/jt/Software/

y una publicación halagadora con algunos ejemplos de cómo es mucho más simple que otros enfoques está aquí:

http://www.decompile.com/not_invented_here/opt/


2
@cat ¿Qué te hace pensar que es necesaria una actualización desde entonces? Esa es simplemente la actitud incorrecta que debemos tener sobre el software.
Joshua Hedges

@JoshuaHedges A menos que quiera mantener el proyecto yo mismo, quiero usar código mantenido activamente en mi código mantenido activamente. Hay muchos proyectos de 2006 que se mantienen activamente, pero este murió, y probablemente con errores. Además, hace 2 años (¡casi exactamente!) Fue hace mucho tiempo que escribí eso: P
cat

1
opt no se mantiene activamente porque es completo y compacto. Por diversión, lo descargué e intenté compilarlo (gcc-7.3) y descubrí que la biblioteca se compila y funciona, pero la prueba de C ++ podría funcionar con un trabajo menor. iostream.h debería convertirse en iostream y usar el espacio de nombres std; debería ser añadido. Se lo mencionaré a James. Esto solo afecta a la prueba de la API de C ++, no al código en sí.
Markgalassi

4

Docopt tiene una implementación de C que pensé que era bastante buena: https://github.com/docopt/docopt.c

A partir de un formato estandarizado de página de manual que describe las opciones de la línea de comandos, docopt infiere y crea un analizador de argumentos. Esto comenzó en Python; la versión de Python literalmente simplemente analiza la cadena de documentos y devuelve un dict. Hacer esto en C requiere un poco más de trabajo, pero es limpio de usar y no tiene dependencias externas.


3

Hay una gran biblioteca de C de propósito general libUCW que incluye un ordenado análisis de opciones de línea de comandos y carga de archivos de configuración .

La biblioteca también viene con buena documentación e incluye algunas otras cosas útiles (E / S rápidas, estructuras de datos, asignadores, ...) pero esto se puede usar por separado.

Ejemplo de analizador de opciones libUCW (de los documentos de la biblioteca)

#include <ucw/lib.h>
#include <ucw/opt.h>

int english;
int sugar;
int verbose;
char *tea_name;

static struct opt_section options = {
  OPT_ITEMS {
    OPT_HELP("A simple tea boiling console."),
    OPT_HELP("Usage: teapot [options] name-of-the-tea"),
    OPT_HELP(""),
    OPT_HELP("Options:"),
    OPT_HELP_OPTION,
    OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
    OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
    OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
    OPT_STRING(OPT_POSITIONAL(1), NULL, tea_name, OPT_REQUIRED, ""),
    OPT_END
  }
};

int main(int argc, char **argv)
{
  opt_parse(&options, argv+1);
  return 0;
}

La opción posicional tiene un error. si hay dos OPT_STRING, y uno es posicional, el otro no, no se puede analizar.
NewBee

2

Escribí una pequeña biblioteca que analiza argumentos similares a POpt, con los que tuve varios problemas, llamada XOpt . Utiliza análisis de argumentos al estilo GNU y tiene una interfaz muy similar a POpt.

Lo uso de vez en cuando con gran éxito y funciona prácticamente en cualquier lugar.


1

Tocando mi propio cuerno si puedo, también me gustaría sugerir que eche un vistazo a una biblioteca de análisis de opciones que he escrito: dropt .

  • Es una biblioteca C (con un contenedor C ++ si lo desea).
  • Es ligero.
  • Es extensible (los tipos de argumentos personalizados se pueden agregar fácilmente y tienen el mismo nivel que los tipos de argumentos integrados).
  • Debería ser muy portátil (está escrito en el estándar C) sin dependencias (aparte de la biblioteca estándar de C).
  • Tiene una licencia muy irrestricta (zlib / libpng).

Una característica que ofrece y que muchos otros no ofrecen es la capacidad de anular opciones anteriores. Por ejemplo, si tiene un alias de shell:

alias bar="foo --flag1 --flag2 --flag3"

y desea usar barpero con --flag1deshabilitado, le permite hacer:

bar --flag1=0

0
#include <stdio.h>

int main(int argc, char **argv)
{
    size_t i;
    size_t filename_i = -1;

    for (i = 0; i < argc; i++)
    {
        char const *option =  argv[i];
        if (option[0] == '-')
        {
            printf("I am a flagged option");
            switch (option[1])
            {
                case 'a':
                    /*someting*/
                    break;
                case 'b':
                    break;
                case '-':
                    /* "--" -- the next argument will be a file.*/
                    filename_i = i;
                    i = i + 1;
                    break;
                default:
                    printf("flag not recognised %s", option);
                    break;
            }
        }
        else
        {   
            printf("I am a positional argument");
        }

        /* At this point, if -- was specified, then filename_i contains the index
         into argv that contains the filename. If -- was not specified, then filename_i will be -1*/
     }
  return 0;
}

4
No; absolutamente no es una buena forma de hacerlo ... Utilice una de las funciones de análisis de argumentos - getopt()o getopt_long().
Jonathan Leffler

5
Suena como una trampa, dado que esta es una pregunta de tarea descaradamente. Además, el OP está teniendo dificultades para comprender el concepto de qué es una cadena y cómo leer partes de ella. Engañarlo es un error.
Pod

Es una cuestión de tarea. Sé lo que es una cuerda. Simplemente no entiendo cómo desglosar los argumentos de la línea de comando porque me parece confuso cuando puede ingresar las opciones cualquier cantidad de veces, por lo que realmente no puede averiguar dónde están los nombres de archivo. ¿Quizás lo estoy pensando demasiado?
user1251020

0

Plantilla instructiva para analizar argumentos de línea de comando en C.

C:> nombre del programa -w - fileOne.txt fileTwo.txt

BOOL argLine = FALSE;
BOOL argWord = FALSE;
BOOL argChar = FALSE;
char * fileName1 = NULL;
char * fileName2 = NULL;

int main(int argc, char * argv[]) {
    int i;
    printf("Argument count=%d\n",argc);
    for (i = 0; i < argc; i++) {
        printf("Argument %s\n",argv[i]);
        if (strcmp(argv[i],"-l")==0) {
            argLine = TRUE;
            printf("    argLine=TRUE\n");
        }
        else if (strcmp(argv[i],"-w")==0) {
            argWord = TRUE;
            printf("    argWord=TRUE\n");
        }
        else if (strcmp(argv[i],"-c")==0) {
            argChar = TRUE;
            printf("    argChar=TRUE\n");
        }
        else if (strcmp(argv[i],"--")==0) {
            if (i+1 <= argc) {
                fileName1 = argv[++i];
                printf("    fileName1=%s\n",fileName1);
            }
            if (i+1 <= argc) {
                fileName2 = argv[++i];
                printf("    fileName2=%s\n",fileName2);
            }
        }
    }
    return 0;
}

1
... No creo que haya una variable booleana en C ...?
user1251020

Mi entorno de eclipse / windows tiene el tipo BOOL. Simplemente cámbielo para escribir int o char y ajuste el código en consecuencia.
Java42

1
C99 tiene un tipo _Boolen todo momento, y un encabezado <stdbool.h>que define boolcomo _Booly truey falsey __bool_true_false_are_defined, todas las macros (que, excepcionalmente, pueden estar indefinidas y redefinidas sin invocar un comportamiento indefinido; esa licencia, sin embargo, está etiquetada como 'obsoleta'). Entonces, si tiene un compilador C99, puede usar <stdbool.h>y bool. Si no es así, escribe uno para ti (no es difícil) o utilizas un equivalente nativo.
Jonathan Leffler

1
@Wolfer Mi entorno C tiene el tipo BOOL (como typedef int BOOL) y el tipo boolean (como typedef unsigned char boolean) y no tiene definición para el tipo bool. En el ejemplo, simplemente cambie para escribir int o char y ajuste el código en consecuencia.
Java42

3
No estoy de acuerdo con este enfoque. Utilice una función de biblioteca para analizar opciones.
Jonathan Leffler

0
    /*
      Here's a rough one not relying on any libraries.
      Example:
      -wi | -iw //word case insensitive
      -li | -il //line case insensitive
      -- file  //specify the first filename (you could just get the files
      as positional arguments in the else statement instead)
      PS: don't mind the #define's, they're just pasting code :D
    */
    #ifndef OPT_H
    #define OPT_H

    //specify option requires argument
    #define require \
      optarg = opt_pointer + 1; \
      if (*optarg == '\0') \
      { \
        if (++optind == argc) \
          goto opt_err_arg; \
        else \
          optarg = argv[optind]; \
      } \
      opt_pointer = opt_null_terminator;

    //start processing argv
    #define opt \
    int   optind                 = 1; \
    char *opt_pointer            = argv[1]; \
    char *optarg                 = NULL; \
    char  opt_null_terminator[2] = {'\0','\0'}; \
    if (0) \
    { \
      opt_err_arg: \
        fprintf(stderr,"option %c requires argument.\n",*opt_pointer); \
        return 1; \
      opt_err_opt: \
        fprintf(stderr,"option %c is invalid.\n",*opt_pointer); \
        return 1; \
    } \
    for (; optind < argc; opt_pointer = argv[++optind]) \
      if (*opt_pointer++ == '-') \
      { \
        for (;;++opt_pointer) \
          switch (*opt_pointer) \
          {

    //stop processing argv
    #define done \
          default: \
            if (*opt_pointer != '\0') \
              goto opt_err_opt; \
            else \
              goto opt_next; \
            break; \
          } \
        opt_next:; \
      }
    #endif //opt.h

    #include <stdio.h>
    #include "opt.h"
    int
    main (int argc, char **argv)
    {
      #define by_character 0
      #define by_word      1
      #define by_line      2
      int cmp = by_character;
      int case_insensitive = 0;
      opt
      case 'h':
        puts ("HELP!");
        break;
      case 'v':
        puts ("fileCMP Version 1.0");
        break;
      case 'i':
        case_insensitive = 1;
        break;
      case 'w':
        cmp = by_word;
        break;
      case 'l':
        cmp = by_line;
        break;
      case '-':required
        printf("first filename: %s\n", optarg);
        break;
      done
      else printf ("Positional Argument %s\n", argv[optind]);
      return 0;
    }

2
Necesita explicar su código en lugar de simplemente lanzarlo y esperar que todos lo entiendan. Este es un sitio para aprender no solo a copiar y pegar.
Yokai

0

De acuerdo, ese es el comienzo de una larga historia: abreviado 'bort analizando una línea de comando en C ...

/**
* Helper function to parse the command line
* @param argc Argument Counter
* @param argv Argument Vector
* @param prog Program Instance Reference to fill with options
*/
bool parseCommandLine(int argc, char* argv[], DuplicateFileHardLinker* prog) {
  bool pathAdded = false;

  // iterate over all arguments...
  for ( int i = 1; i<argc; i++ ) {

    // is argv a command line option ?
    if ( argv[i][0] == '-' || argv[i][0] == '/' ) {

// ~~~~~~ Optionally Cut that part vvvvvvvvvvvvv for sake of simplicity ~~~~~~~
      // check for longer options
            if ( stricmp( &argv[i][1], "NoFileName"  ) == 0
              ||  strcmp( &argv[i][1], "q1"          ) == 0 ) {

        boNoFileNameLog = true;
      } else if ( strcmp( &argv[i][1], "HowAreYou?"    ) == 0 ) {
          logInfo( "SECRET FOUND: Well - wow I'm glad ya ask me.");
      } else {

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Now here comes the main thing:
//
        // check for one char options
        while ( char option = *++argv[i] ) {

          switch ( option ) {
          case '?':
            // Show program usage

            logInfo(L"Options:");
            logInfo(L"  /q\t>Quite mode");
            logInfo(L"  /v\t>Verbose mode");
            logInfo(L"  /d\t>Debug mode");
            return false;

            // Log options
          case 'q':
            setLogLevel(LOG_ERROR);
            break;

          case 'v':
            setLogLevel(LOG_VERBOSE);
            break;

          case 'd':
            setLogLevel(LOG_DEBUG);
            break;

          default:
            logError(L"'%s' is an illegal command line option!"
                      "  Use /? to see valid options!", option);
            return false;
          } // switch one-char-option
        } //while one-char-options
      }  //else one vs longer options
    } // if isArgAnOption

// 
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^  So that's it! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// What follows now is are some usefull extras...
//
    else {


      // the command line options seems to be a path...
      WCHAR tmpPath[MAX_PATH_LENGTH];
      mbstowcs(tmpPath, argv[i], sizeof(tmpPath));

      // check if the path is existing!
      //...

      prog->addPath(tmpPath); //Comment or remove to get a working example
      pathAdded = true;
    }
  }

  // check for parameters
  if ( !pathAdded ) {
    logError("You need to specify at least one folder to process!\n"
             "Use /? to see valid options!");
    return false;
  }

  return true;
}



int main(int argc, char* argv[]) {

  try {
    // parse the command line
    if ( !parseCommandLine(argc, argv, prog) ) {
      return 1; 
    }

// I know that sample is just to show how the nicely parse commandline Arguments
// So Please excuse more nice useful C-glatter that follows now...
  }
  catch ( LPCWSTR err ) {
    DWORD dwError = GetLastError();
    if ( wcslen(err) > 0 ) {
      if ( dwError != 0 ) {
        logError(dwError, err);
      }
      else {
        logError(err);
      }
    }
    return 2;
  }
}

#define LOG_ERROR               1
#define LOG_INFO                0
#define LOG_VERBOSE             -1
#define LOG_DEBUG               -2

/** Logging Level for the console output */
int logLevel = LOG_INFO;

void logError(LPCWSTR message, ...) {
  va_list argp;
  fwprintf(stderr, L"ERROR: ");
  va_start(argp, message);
  vfwprintf(stderr, message, argp);
  va_end(argp);
  fwprintf(stderr, L"\n");
}


void logInfo(LPCWSTR message, ...) {
  if ( logLevel <= LOG_INFO ) {
    va_list argp;
    va_start(argp, message);
    vwprintf(message, argp);
    va_end(argp);
    wprintf(L"\n");
  }
}

Tenga en cuenta que esta versión también admitirá la combinación de argumentos: así que en lugar de escribir / h / s -> / hs también funcionará.

Perdón por ser la enésima persona que publica aquí; sin embargo, no estaba realmente satisfecho con todas las versiones independientes que vi aquí. Bueno, los lib son bastante agradables. Así que preferiría el analizador de opciones libUCW , Arg o Getopt sobre los caseros.

Tenga en cuenta que puede cambiar:

*++argv[i]-> (++argv*)[0] más largo menos críptico pero aún críptico.

Bien, vamos a desglosarlo: 1. argv [i] -> acceda al elemento i-ésimo en el campo de puntero argv-char

  1. ++ * ... -> reenviará el puntero argv por un carácter

  2. ... [0] -> seguirá el puntero leer el carácter

  3. ++ (...) -> corchetes están ahí, así que aumentaremos el puntero y no el valor del carácter en sí.

Tan agradable que en C ## los punteros 'murieron' - ¡¡¡Viva los punteros !!!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.