Analyse des arguments de ligne de commande en C?

98

J'essaie d'écrire un programme capable de comparer deux fichiers ligne par ligne, mot par mot ou caractère par caractère en C. Il doit pouvoir lire dans les options de ligne de commande -l -w -i or --...

  • si l'option est -l, elle compare les fichiers ligne par ligne.
  • si l'option est -w, elle compare les fichiers mot par mot.
  • si les options sont - cela suppose automatiquement que l'argument suivant est le premier nom de fichier.
  • si l'option est -i, il les compare sans tenir compte de la casse.
  • par défaut, comparer les fichiers caractère par caractère.

Le nombre de fois où les options sont saisies n'a pas d'importance tant que -w et -l ne sont pas saisis en même temps et qu'il n'y a pas plus ou moins de 2 fichiers.

Je ne sais même pas par où commencer pour analyser les arguments de la ligne de commande. VEUILLEZ AIDER :(

C'est donc le code que j'ai trouvé pour tout. Je n'ai pas encore vérifié d'erreur, mais je me demandais si j'écrivais les choses de manière trop compliquée?

/*
 * 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;
}
user1251020
la source
Je ne sais pas trop comment utiliser getopt () ... Je ne l'ai pas encore appris dans ma classe.
user1251020
3
Alors, allez lire la page de manuel pour cela; ce n'est pas très complexe, et la page de manuel comprend probablement un exemple que vous pourrez expérimenter (et si votre page de manuel locale ne le fait pas, vous pouvez certainement trouver des exemples sur le Web).
Jonathan Leffler
1
C'est une librairie de haut niveau: argparse en c, très simple d'utilisation.
Cofyc
Pour des choses simples, vous pouvez simplement rouler le vôtre au lieu d'utiliser une bibliothèque. J'ai écrit un tutoriel de démarrage ici engineeringterminal.com/computer-science/tutorials
...

Réponses:

188

À ma connaissance, les trois méthodes les plus courantes pour analyser les arguments de ligne de commande en C sont:

  • Getopt ( #include <unistd.h>de la bibliothèque POSIX C), qui peut résoudre de simples tâches d' analyse d'arguments . Si vous êtes un peu familier avec bash, le getopt intégré de bash est basé sur Getopt de la libc GNU.
  • Argp ( #include <argp.h>de la bibliothèque GNU C), qui peut résoudre des tâches plus complexes et s'occupe de choses comme, par exemple:
    • -?, --helppour un message d'aide , y compris l'adresse e-mail
    • -V, --versionpour les informations de version
    • --usagepour le message d'utilisation
  • Faites-le vous - même , ce que je ne recommande pas pour les programmes qui seraient donnés à quelqu'un d'autre, car il y en a trop qui pourraient mal tourner ou diminuer la qualité. L'erreur courante consistant à oublier «-» pour arrêter l'analyse des options n'est qu'un exemple.

La documentation de la bibliothèque GNU C contient de jolis exemples pour Getopt et Argp.

Exemple d'utilisation 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.

    // ...
}

Exemple d'utilisation d' Argp

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

const char *argp_program_version = "programname programversion";
const char *argp_program_bug_address = "<[email protected]>";
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);

    // ...
}

Exemple pour le faire vous-même

#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.

    // ...
}   

Avis de non-responsabilité: Je suis nouveau sur Argp, l'exemple peut contenir des erreurs.

Christian Hujer
la source
9
Réponse vraiment complète, merci Christian (voté pour). Cependant, les utilisateurs de Mac doivent être conscients que l'approche argp n'est pas compatible avec plusieurs plates-formes. Comme je l'ai trouvé ici , Argp est une extension d'API glibc non standardisée. Il est disponible dans gnulib et peut donc être ajouté explicitement à un projet. Cependant, il est probablement plus simple pour les développeurs mac uniquement ou multiplateformes d'utiliser l'approche getopt.
thclark
1
Pour la version do it yourself, je n'aime pas que les options autorisent du texte supplémentaire par la suite, comme -wzzz analyse la même chose que -w, et aussi que les options doivent venir avant les arguments du fichier.
Jake
1
@Jake vous avez raison. Respect pour repérer ça. Je ne me souviens pas si j'ai remarqué cela quand je l'ai écrit. C'est encore un exemple parfait que le bricolage est si facile à se tromper et ne devrait donc pas être fait. Merci de l'avoir dit, je pourrais corriger l'exemple.
Christian Hujer
18

Utilisez getopt(), ou peut-être 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);
        }
    }
 }

Notez que vous devez déterminer les en-têtes à inclure (j'en fais 4 qui sont requis), et la façon dont j'ai écrit le op_modetype signifie que vous avez un problème dans la fonction process()- vous ne pouvez pas accéder à l'énumération là-bas. Il est préférable de déplacer l'énumération en dehors de la fonction; vous pourriez même faireop_mode une variable de portée fichier sans lien externe (une manière sophistiquée de dire static) pour éviter de la passer à la fonction. Ce code ne traite pas -comme synonyme d'entrée standard, un autre exercice pour le lecteur. Notez que getopt()prend automatiquement soin de --marquer la fin des options pour vous.

Je n'ai exécuté aucune version du typage ci-dessus après un compilateur; il pourrait y avoir des erreurs.


Pour un crédit supplémentaire, écrivez une fonction (bibliothèque):

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

qui encapsule la logique de traitement des options de nom de fichier après la getopt()boucle. Il doit gérer -comme entrée standard. Notez que l'utilisation de ceci indiquerait qu'il op_modedevrait s'agir d'une variable d'étendue de fichier statique. La filter()fonction prend argc, argv, optindet un pointeur vers la fonction de traitement. Il doit renvoyer 0 (EXIT_SUCCESS) s'il a pu ouvrir tous les fichiers et toutes les invocations de la fonction rapportée 0, sinon 1 (ou EXIT_FAILURE). Avoir une telle fonction simplifie l'écriture de programmes de «filtre» de style Unix qui lisent les fichiers spécifiés sur la ligne de commande ou l'entrée standard.

Jonathan Leffler
la source
Je n'aime pas que getopt () n'autorise pas les options après le premier fichier.
Jake
POSIX getopt()ne le fait pas; GNU le getopt()fait par défaut. Faites votre choix. Je ne suis pas intéressé par les options après le comportement des noms de fichiers, principalement parce qu'il n'est pas fiable sur toutes les plates-formes.
Jonathan Leffler
14

J'ai trouvé Gengetopt très utile - vous spécifiez les options que vous voulez avec un simple fichier de configuration, et il génère une paire .c / .h que vous incluez simplement et liez avec votre application. Le code généré utilise getopt_long, semble gérer les types les plus courants de paramètres de ligne de commande, et il peut gagner beaucoup de temps.

Un fichier d'entrée gengetopt peut ressembler à ceci:

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 

La génération du code est facile et crache cmdline.het cmdline.c:

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

Le code généré est facilement intégré:

#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 vous avez besoin de faire des vérifications supplémentaires (telles que vous assurer que les indicateurs sont mutuellement exclusifs), vous pouvez le faire assez facilement avec les données stockées dans la gengetopt_args_infostructure.

DavidA
la source
1 ++ sauf qu'il génère du code qui génère des avertissements :(
cat
Malheureusement oui. J'ai mis des exceptions dans mon fichier cmake.
davidA
Je vais probablement utiliser les pragmas GCC pour ignorer les avertissements pour ce fichier (horrible je sais)
cat
Notez que vous les perdrez évidemment si vous régénérez la source, vous voudrez peut-être les appliquer en tant que patch dans votre processus de construction. Franchement, j'ai trouvé plus facile de désactiver les avertissements sur ces fichiers spécifiques.
davidA
eh bien non, je veux dire mettre les pragmas autour du #include, pas dans le fichier généré lui-même. pour moi, désactiver les avertissements est verboten :-)
cat
6

Je suis très surpris que personne n'ait évoqué le package "opt" de James Theiler.

Vous pouvez trouver opt sur http://public.lanl.gov/jt/Software/

et un post flatteur avec quelques exemples de la façon dont c'est tellement plus simple que d'autres approches est ici:

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

Markgalassi
la source
2
@cat Qu'est-ce qui vous fait penser qu'il a fallu une mise à jour depuis? C'est tout simplement la mauvaise attitude à avoir à propos des logiciels.
Joshua Hedges
@JoshuaHedges À moins que je ne veuille maintenir le projet moi-même, je souhaite utiliser du code activement maintenu dans mon code activement maintenu. Il y a beaucoup de projets de 2006 qui sont activement maintenus, mais celui-ci est mort, et probablement avec des bugs. De plus, il y a 2 ans (presque exactement!), C'était il y a longtemps que j'ai écrit ceci: P
cat
1
opt n'est pas activement maintenu car il est complet et compact. Pour l'essentiel, je viens de le télécharger et j'ai essayé de le construire (gcc-7.3) et j'ai trouvé que la bibliothèque se construit et fonctionne, mais le test C ++ pourrait faire avec quelques travaux mineurs. iostream.h doit devenir iostream, et en utilisant l'espace de noms std; devrait être ajouté. Je vais le mentionner à James. Cela affecte uniquement le test de l'API C ++, pas le code lui-même.
markgalassi
4

Docopt a une implémentation C que je trouvais assez sympa: https://github.com/docopt/docopt.c

À partir d'un format normalisé de page de manuel décrivant les options de ligne de commande, docopt déduit et crée un analyseur d'arguments. Cela a commencé en python; la version python analyse littéralement juste la docstring et renvoie un dict. Faire cela en C demande un peu plus de travail, mais il est propre à utiliser et n'a pas de dépendances externes.

Gvoysey
la source
3

Il existe une excellente bibliothèque C à usage général libUCW qui inclut l' analyse des options de ligne de commande et le chargement du fichier de configuration .

La bibliothèque est également livrée avec une bonne documentation et comprend d'autres éléments utiles (E / S rapides, structures de données, allocateurs, ...) mais cela peut être utilisé séparément.

Exemple d'analyseur d'option libUCW (à partir de la documentation de la bibliothèque)

#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;
}
Tomáš Gavenčiak
la source
L'option positionnelle a un bug. s'il y a deux OPT_STRING, et un est positionnel, un non, il ne peut pas analyser.
NewBee
2

J'ai écrit une petite bibliothèque qui analyse des arguments similaires à POpt, avec laquelle j'ai eu plusieurs problèmes, appelée XOpt . Utilise l'analyse des arguments de style GNU et a une interface très similaire à POpt.

Je l'utilise de temps en temps avec beaucoup de succès, et cela fonctionne à peu près n'importe où.

Qix - MONICA A ÉTÉ MISTRE
la source
1

Si vous me le permettez, j'aimerais également vous suggérer de jeter un œil à une bibliothèque d'analyse d'options que j'ai écrite: dropt .

  • C'est une bibliothèque C (avec un wrapper C ++ si vous le souhaitez).
  • C'est léger.
  • Il est extensible (les types d'arguments personnalisés peuvent être facilement ajoutés et avoir un pied d'égalité avec les types d'arguments intégrés).
  • Il doit être très portable (il est écrit en C standard) sans dépendances (autres que la bibliothèque standard C).
  • Il a une licence très illimitée (zlib / libpng).

Une fonctionnalité qu'il offre que beaucoup d'autres n'offrent pas est la possibilité de remplacer les options précédentes. Par exemple, si vous avez un alias shell:

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

et que vous souhaitez utiliser barmais avec --flag1désactivé, cela vous permet de faire:

bar --flag1=0
Jamesdlin
la source
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;
}
Cosse
la source
4
Non; ce n'est absolument pas une bonne façon de le faire ... Utilisez l'une des fonctions d'analyse des arguments - getopt()ou getopt_long().
Jonathan Leffler
5
Cela ressemble à une triche, étant donné qu'il s'agit manifestement d'une question de devoir. De plus, l'OP a du mal à comprendre le concept de ce qu'est une chaîne et comment en lire certaines parties. Faire des getopts sur lui est une erreur.
Pod
C'est une question de devoirs. Je sais ce qu'est une corde. Je ne comprends tout simplement pas comment décomposer les arguments de la ligne de commande, car cela me semble déroutant lorsque vous pouvez saisir les options un certain nombre de fois, vous ne pouvez donc pas vraiment savoir où se trouvent les noms de fichiers. Peut-être que je réfléchis trop?
user1251020
0

Modèle pédagogique pour analyser les arguments de ligne de commande en C.

C:> nom_programme -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;
}
Java42
la source
1
... Je ne pense pas qu'il y ait une variable booléenne dans C ...?
user1251020
Mon environnement eclipse / windows est de type BOOL. Changez-le simplement pour taper int ou char et ajustez le code en conséquence.
Java42
1
C99 a un type _Boolà tout moment, et un en-tête <stdbool.h>qui définit boolcomme _Boolet trueet falseet __bool_true_false_are_defined, toutes les macros (qui, exceptionnellement, peuvent être indéfinies et redéfinies sans invoquer un comportement indéfini; cette licence est cependant étiquetée `` obsolescent ''). Donc, si vous avez un compilateur C99, vous pouvez utiliser <stdbool.h>et bool. Sinon, vous en écrivez un pour vous-même (ce n'est pas difficile) ou vous utilisez un équivalent natif.
Jonathan Leffler
1
@Wolfer Mon environnement C a le type BOOL (comme typedef int BOOL) et le type boolean (comme typedef unsigned char boolean) et aucune définition pour le type bool. Dans l'exemple, changez simplement pour taper int ou char et ajustez le code en conséquence.
Java42
3
Je ne suis pas d'accord avec cette approche. Utilisez une fonction de bibliothèque pour analyser les options.
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;
    }
Anonyme
la source
2
Vous devez expliquer votre code plutôt que de simplement le lancer et vous attendre à ce que tout le monde le comprenne. Ceci est un site pour apprendre non seulement le copier-coller.
Yokai
0

D'accord, c'est le début d'une longue histoire - en bref, l'analyse d'une ligne de commande 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");
  }
}

Notez que cette version prend également en charge la combinaison d'arguments: donc au lieu d'écrire / h / s -> / hs fonctionnera également.

Désolé d'être la n-ième personne à poster ici - mais je n'étais pas vraiment satisfait de toutes les versions autonomes que j'ai vues ici. Eh bien, les lib sont gentils. Je préférerais donc l' analyseur d'option libUCW , Arg ou Getopt à ceux faits maison.

Notez que vous pouvez changer:

*++argv[i]-> (++argv*)[0] plus longtemps moins cryptique mais toujours cryptique.

Bon, décomposons-le: 1. argv [i] -> accéder au i-ième élément dans le champ de pointeur argv-char

  1. ++ * ... -> fera suivre le pointeur argv d'un caractère

  2. ... [0] -> suivra le pointeur lisant le caractère

  3. ++ (...) -> bracket sont là donc nous allons augmenter le pointeur et non la valeur char elle-même.

Tellement sympa qu'en C ## les pointeurs «sont morts» - vive les pointeurs !!!

Nadu
la source