Analisi degli argomenti della riga di comando in C?


98

Sto cercando di scrivere un programma in grado di confrontare due file riga per riga, parola per parola o carattere per carattere in C. Deve essere in grado di leggere le opzioni della riga di comando -l -w -i or --...

  • se l'opzione è -l confronta i file riga per riga.
  • se l'opzione è -w confronta i file parola per parola.
  • se l'opzione è - assume automaticamente che l'argomento successivo sia il primo nome di file.
  • se l'opzione è -i, le confronta senza distinzione tra maiuscole e minuscole.
  • il valore predefinito confronta i file carattere per carattere.

Non dovrebbe importare quante volte le opzioni vengono inserite fintanto che -w e -l non vengono immessi contemporaneamente e non ci sono più o meno di 2 file.

Non so nemmeno da dove iniziare con l'analisi degli argomenti della riga di comando. PER FAVORE AIUTO :(

Quindi questo è il codice che ho escogitato per tutto. Non ho ancora verificato l'errore, ma mi chiedevo se sto scrivendo le cose in modo troppo complicato?

/*
 * 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;
}

Non sono abbastanza sicuro di come usare getopt () ... non l'ho ancora imparato nella mia classe.
user1251020

3
Quindi, vai a leggere la pagina di manuale per questo; non è molto complesso, e la pagina di manuale probabilmente include un esempio con cui sperimentare (e se la tua pagina man locale non lo fa, puoi sicuramente trovare esempi sul web).
Jonathan Leffler

1
Questa è una libreria di alto livello: argparse in c, molto facile da usare.
Cofyc


Per cose semplici potresti semplicemente rotolare il tuo invece di usare una libreria. Ho scritto un tutorial per iniziare qui engineeringterminal.com/computer-science/tutorials/…
nalyd88

Risposte:


188

Per quanto ne so, i tre modi più popolari per analizzare gli argomenti della riga di comando in C sono:

  • Getopt ( #include <unistd.h>dalla libreria POSIX C), che può risolvere semplici parsing di argomenti attività di . Se hai familiarità con bash, il built-in getopt di bash è basato su Getopt dalla libc GNU.
  • Argp ( #include <argp.h>dalla libreria GNU C), che può risolvere compiti più complessi e si occupa di cose come, ad esempio:
    • -?, --helpper il messaggio di aiuto , incluso l'indirizzo e-mail
    • -V, --versionper informazioni sulla versione
    • --usageper messaggio di utilizzo
  • Farlo da soli , cosa che non consiglio per programmi che verrebbero dati a qualcun altro, poiché ci sono troppe cose che potrebbero andare storte o di qualità inferiore. Il famoso errore di dimenticare "-" per interrompere l'analisi delle opzioni è solo un esempio.

La documentazione della libreria GNU C contiene alcuni bei esempi per Getopt e Argp.

Esempio per l'utilizzo di 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.

    // ...
}

Esempio per l'utilizzo di 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);

    // ...
}

Esempio per farlo da soli

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

    // ...
}   

Dichiarazione di non responsabilità: sono nuovo su Argp, l'esempio potrebbe contenere errori.


9
Risposta davvero esauriente, grazie Christian (voto positivo). Tuttavia, gli utenti Mac dovrebbero essere consapevoli del fatto che l'approccio argp non è compatibile multipiattaforma. Come ho trovato qui , Argp è un'estensione API glibc non standardizzata. È disponibile in gnulib, quindi può essere aggiunto esplicitamente a un progetto. Tuttavia, è probabilmente più semplice per gli sviluppatori solo Mac o multipiattaforma utilizzare l'approccio getopt.
thclark

1
Per la versione fai da te, non mi piace che le opzioni consentano il testo aggiuntivo in seguito, come -wzzz analizza lo stesso di -w, e anche che le opzioni debbano venire prima degli argomenti del file.
Jake

1
@ Jake hai ragione. Rispetto per averlo notato. Non ricordo se l'ho notato quando l'ho scritto. È di nuovo un esempio perfetto del fatto che il fai-da-te è così facile da sbagliare e quindi non dovrebbe essere fatto. Grazie per averlo detto, potrei correggere l'esempio.
Christian Hujer

18

Usa getopt(), o forse 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);
        }
    }
 }

Nota che devi determinare quali intestazioni includere (io ne faccio 4 richieste) e il modo in cui ho scritto il op_modetipo significa che hai un problema nella funzione process(): non puoi accedere all'enumerazione laggiù. È meglio spostare l'enumerazione fuori dalla funzione; potresti persino creare op_modeuna variabile di ambito del file senza collegamento esterno (un modo elegante per dire static) per evitare di passarla alla funzione. Questo codice non tratta -come sinonimo di input standard, un altro esercizio per il lettore. Nota che getopt()si prende automaticamente cura di-- contrassegnare la fine delle opzioni per te.

Non ho eseguito alcuna versione della digitazione sopra un compilatore; potrebbero esserci degli errori.


Per un credito extra, scrivi una funzione (libreria):

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

che incapsula la logica per l'elaborazione delle opzioni del nome file dopo il getopt()ciclo. Dovrebbe essere gestito -come input standard. Si noti che l'utilizzo di questo indicherebbe che op_modedovrebbe essere una variabile di ambito di file statico. La filter()funzione prende argc, argv, optinde un puntatore alla funzione di elaborazione. Dovrebbe restituire 0 (EXIT_SUCCESS) se è stato in grado di aprire tutti i file e tutte le chiamate della funzione riportavano 0, altrimenti 1 (o EXIT_FAILURE). Avere una tale funzione semplifica la scrittura di programmi di "filtro" in stile Unix che leggono i file specificati sulla riga di comando o sullo standard input.


Non mi piace che getopt () non consenta opzioni dopo il primo file.
Jake

POSIX getopt()no; GNU lo getopt()fa per impostazione predefinita. Fai la tua scelta. Non mi piacciono le opzioni dopo il comportamento dei nomi dei file, principalmente perché non è affidabile su tutte le piattaforme.
Jonathan Leffler

14

Ho trovato Gengetopt molto utile: specifichi le opzioni che desideri con un semplice file di configurazione e genera una coppia .c / .h che includi e colleghi semplicemente alla tua applicazione. Il codice generato utilizza getopt_long, sembra gestire i tipi più comuni di parametri della riga di comando e può far risparmiare molto tempo.

Un file di input gengetopt potrebbe essere simile a questo:

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 generazione del codice è facile e sputa cmdline.he cmdline.c:

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

Il codice generato è facilmente integrabile:

#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]);
  }
}

Se è necessario eseguire controlli aggiuntivi (come assicurarsi che i flag si escludano a vicenda), è possibile farlo abbastanza facilmente con i dati memorizzati nella gengetopt_args_infostruttura.


1 ++ tranne per il fatto che genera codice che genera avvisi :(
cat

Sì purtroppo. Ho inserito delle eccezioni nel mio file cmake.
davidA

Probabilmente userò i pragmi GCC per ignorare gli avvisi per quel file (orribile lo so)
gatto

Nota che ovviamente li perderai se rigenererai il sorgente, quindi potresti volerli applicare come patch nel tuo processo di compilazione. Francamente ho trovato più semplice disattivare gli avvisi su quei file specifici.
davidA

beh no, intendo mettere i pragmi attorno a #include, non nel file generato stesso. per me disattivare gli avvisi è verboten :-)
gatto

6

Sono molto sorpreso che nessuno abbia parlato del pacchetto "opt" di James Theiler.

Puoi trovare opt su http://public.lanl.gov/jt/Software/

e un post lusinghiero con alcuni esempi di come sia molto più semplice di altri approcci è qui:

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


2
@cat Cosa ti fa pensare che sia necessario un aggiornamento da allora? Questo è semplicemente l'atteggiamento sbagliato da avere nei confronti del software.
Joshua Hedges

@JoshuaHedges A meno che non voglia mantenere il progetto da solo, desidero utilizzare codice gestito attivamente nel mio codice gestito attivamente. Ci sono molti progetti del 2006 che vengono mantenuti attivamente, ma questo è morto, e probabilmente con dei bug. Inoltre, 2 anni fa (quasi esattamente!) È stato molto tempo fa che ho scritto che: P
cat

1
opt non viene mantenuto attivamente perché è completo e compatto. Per i calci l'ho appena scaricato e ho provato a costruirlo (gcc-7.3) e ho scoperto che la libreria si compila e funziona, ma il test C ++ potrebbe fare con qualche lavoro minore. iostream.h dovrebbe diventare iostream e usare lo spazio dei nomi std; dovrebbe essere aggiunto. Ne parlerò a James. Ciò riguarda solo il test API C ++, non il codice stesso.
markgalassi

4

Docopt ha un'implementazione in C che mi è sembrata abbastanza carina: https://github.com/docopt/docopt.c

Da un formato standardizzato della pagina di manuale che descrive le opzioni della riga di comando, docopt deduce e crea un parser di argomenti. Questo è iniziato in Python; la versione python analizza letteralmente solo la docstring e restituisce un dict. Fare questo in C richiede un po 'più di lavoro, ma è pulito da usare e non ha dipendenze esterne.


3

Esiste una fantastica libreria C generica libUCW che include l' analisi delle opzioni della riga di comando e il caricamento dei file di configurazione .

La libreria viene fornita con una buona documentazione e include altre cose utili (IO veloce, strutture dati, allocatori, ...) ma possono essere usate separatamente.

Esempio di parser dell'opzione libUCW (dalla documentazione della libreria)

#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;
}

l'opzione posizionale ha un bug. se ci sono due OPT_STRING, e uno è di posizione, uno no, non può analizzare.
NewBee

2

Ho scritto una piccola libreria che analizza argomenti simili a POpt, con cui ho avuto diversi problemi, chiamata XOpt . Utilizza l'analisi degli argomenti in stile GNU e ha un'interfaccia molto simile a POpt.

Lo uso di tanto in tanto con grande successo e funziona praticamente ovunque.


1

Suonando il mio corno se posso, vorrei anche suggerire di dare un'occhiata a un'opzione che analizza la libreria che ho scritto: dropt .

  • È una libreria C (con un wrapper C ++ se lo si desidera).
  • È leggero.
  • È estensibile (i tipi di argomenti personalizzati possono essere facilmente aggiunti e hanno lo stesso fondamento con i tipi di argomenti incorporati).
  • Dovrebbe essere molto portabile (è scritto nello standard C) senza dipendenze (a parte la libreria standard C).
  • Ha una licenza molto non restrittiva (zlib / libpng).

Una caratteristica che offre che molti altri non offrono è la possibilità di sostituire le opzioni precedenti. Ad esempio, se hai un alias di shell:

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

e che vuoi usare barma con --flag1disabilitato, ti permette di fare:

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; non è assolutamente un buon modo per farlo ... Usa una delle funzioni di parsing degli argomenti - getopt()o getopt_long().
Jonathan Leffler

5
Sembra un inganno, dato che questa è palesemente una domanda da fare a casa. Inoltre, l'OP ha difficoltà a comprendere il concetto di cosa sia una stringa e come leggerne parti. Farselo addosso è un errore.
Pod

È una domanda per i compiti. So cos'è una stringa. Semplicemente non capisco come scomporre gli argomenti della riga di comando perché mi sembra confuso quando puoi inserire le opzioni un numero qualsiasi di volte, quindi non puoi davvero capire dove sono i nomi dei file. Forse ci sto pensando troppo?
user1251020

0

Modello di istruzioni per l'analisi degli argomenti della riga di comando in C.

C:> nomeprogramma -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
... Non penso che ci sia una variabile booleana in C ...?
user1251020

Il mio ambiente eclipse / windows ha il tipo BOOL. Basta cambiarlo per digitare int o char e regolare il codice di conseguenza.
Java42

1
C99 ha un tipo _Boolin ogni momento e un'intestazione <stdbool.h>che definisce boolcome _Boole trueed falsee __bool_true_false_are_defined, tutte le macro (che, eccezionalmente, può essere indefinito e ridefiniti senza invocare comportamento indefinito, che licenza è, tuttavia, Tagged 'obsoleta'). Quindi, se hai un compilatore C99, puoi usare <stdbool.h>e bool. In caso contrario, ne scrivi uno per te stesso (non è difficile) o usi un equivalente nativo.
Jonathan Leffler

1
@Wolfer Il mio ambiente C ha il tipo BOOL (come typedef int BOOL) e il tipo boolean (come typedef unsigned char boolean) e nessuna definizione per il tipo bool. Nell'esempio, cambia semplicemente per digitare int o char e regola il codice di conseguenza.
Java42

3
Non sono d'accordo con questo approccio. Usa una funzione di libreria per analizzare le opzioni.
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
Devi spiegare il tuo codice piuttosto che vomitarlo e aspettarti che tutti lo capiscano. Questo è un sito per imparare non solo a copiare e incollare.
Yokai

0

Va bene, questo è l'inizio di una lunga storia - abbreviato durante l'analisi di una riga di comando in 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");
  }
}

Nota che questa versione supporterà anche la combinazione di argomenti: quindi invece di scrivere / h / s -> / hs funzionerà anche.

Scusa per essere l'ennesima persona a postare qui - tuttavia non ero molto soddisfatto di tutte le versioni stand-alone che ho visto qui. Ebbene, quelli lib sono abbastanza belli. Quindi preferirei il parser dell'opzione libUCW , Arg o Getopt a quelli fatti in casa.

Nota che puoi cambiare:

*++argv[i]-> (++argv*)[0] più a lungo meno criptico ma comunque criptico.

Ok, analizziamolo: 1. argv [i] -> accedi all'elemento i-esimo nel campo del puntatore argv-char

  1. ++ * ... -> inoltrerà il puntatore argv di un carattere

  2. ... [0] -> seguirà il puntatore a leggere il carattere

  3. ++ (...) -> le parentesi sono lì, quindi aumenteremo il puntatore e non il valore del carattere stesso.

Così bello che in C ## i puntatori "morivano" - lunga vita ai puntatori !!!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.