Analizzare gli argomenti della riga di comando?

Ciao 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 oppure – “… se l’opzione è -l confronta i file riga per riga. se l’opzione è -w confronta i file parola per parola. se le opzioni sono – assume automaticamente che il prossimo argomento è il primo nome di file. e se l’opzione è -i la confronta in modo non sensibile al maiuscolo / minuscolo. Altrimenti, per impostazione predefinita, confronta i file carattere per carattere

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

Non so nemmeno da dove cominciare con l’analisi degli argomenti della riga di comando. PER FAVORE AIUTO 🙁

Quindi questo è il codice che mi è venuto in mente per tutto. Non ho ancora verificato l’errore, ma mi stavo chiedendo se sto scrivendo le cose in modo 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; } 

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

  • Getopt ( #include dalla libreria POSIX C), che può risolvere semplici compiti di analisi degli argomenti . Se hai un po ‘dimestichezza con bash, il getopt built-in di bash è basato su Getopt da GNU libc.
  • Argp ( #include dalla libreria GNU C), che può risolvere compiti più complessi e si occupa di cose come, ad esempio:
    • -? , --help per il messaggio di aiuto , incluso l’indirizzo email
    • -V , --version per informazioni --version versione
    • --usage Uso per messaggio di utilizzo
  • Fai da te , cosa che non raccomando per i programmi che sarebbero dati a qualcun altro, poiché c’è troppo che potrebbe andare storto o una qualità inferiore. L’errore popolare di dimenticare “-” per interrompere l’analisi delle opzioni è solo un esempio.

La documentazione della libreria GNU C ha alcuni buoni esempi per Getopt e Argp.

Esempio per l’utilizzo di Getopt

 #include  #include  #include  #include  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 ) 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  #include  const char *argp_program_version = "programname programversion"; const char *argp_program_bug_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 solo

 #include  #include  #include  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 di Argp, l'esempio potrebbe contenere errori.

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

Si noti che è necessario determinare quali intestazioni includere (lo faccio 4 che sono obbligatori), e il modo in cui ho scritto il tipo op_mode indica che si è verificato un problema nel process() function process() - non è ansible accedere all'enumerazione laggiù. È meglio spostare l'enumerazione al di fuori della funzione; si potrebbe persino rendere op_mode una variabile per il file scope senza un collegamento esterno (un modo elegante per dire static ) per evitare di passarlo alla funzione. Questo codice non gestisce - come sinonimo di input standard, un altro esercizio per il lettore. Nota che getopt() si occupa automaticamente di -- per segnare la fine delle opzioni per te.

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


Per ulteriore credito, 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 del file dopo il ciclo getopt() . Dovrebbe gestire - come input standard. Si noti che l'utilizzo di questo indica che op_mode dovrebbe essere una variabile di ambito di file statico. La funzione filter() accetta argc , argv , optind e un puntatore alla funzione di elaborazione. Dovrebbe restituire 0 (EXIT_SUCCESS) se fosse in grado di aprire tutti i file e tutte le invocazioni della funzione segnalate 0, altrimenti 1 (o EXIT_FAILURE). Avere tale funzione semplifica la scrittura di programmi 'filtro' in stile Unix che leggono i file specificati nella riga di comando o nello standard input.

Ho trovato che Gengetopt è molto utile: si specificano le opzioni desiderate con un semplice file di configurazione e si genera una coppia .c / .h che si include e si collega semplicemente con l’applicazione. Il codice generato fa uso di getopt_long, sembra gestire i tipi più comuni di parametri da riga di comando, e può far risparmiare molto tempo.

Un file di input gengetopt potrebbe essere qualcosa del genere:

 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 è semplice e sputa cmdline.h e cmdline.c :

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

Il codice generato è facilmente integrato:

 #include  #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 ulteriori verifiche (come garantire che i flag si escludano a vicenda), è ansible farlo abbastanza facilmente con i dati memorizzati nella struttura gengetopt_args_info .

Sono molto sorpreso che nessuno abbia presentato il pacchetto “opt” di James Theiler.

Puoi trovare optare 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/

C’è una grande libreria libUCW per scopi generici che include l’ opzione di parsing e il caricamento del file di configurazione .

La libreria viene fornita con una buona documentazione e include altre cose utili (I / O veloce, strutture dati, allocatori, …) ma può essere utilizzata separatamente.

Esempio di parser di opzioni libUCW (dai documenti della libreria)

 #include  #include  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, "\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; } 

Docopt ha un’implementazione C che pensavo fosse abbastanza carina: https://github.com/docopt/docopt.c

Da un formato standard man-page che descrive le opzioni della riga di comando, docopt si deduce e crea un parser di argomenti. Questo è iniziato in python; la versione python letteralmente analizza la docstring e restituisce un dettato. Fare questo in C richiede un po ‘più di lavoro, ma è pulito da usare e non ha dipendenze esterne.

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

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

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

Modello di istruzione 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; } 
  /* 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  #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; } 

Se mi piace il mio corno, vorrei anche suggerire di dare un’occhiata a una libreria di analisi delle opzioni 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 valore dei tipi di argomenti integrati).
  • Dovrebbe essere molto portabile (è scritto nello standard C) senza dipendenze (tranne la libreria standard C).
  • Ha una licenza molto restrittiva (zlib / libpng).

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

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

e vuoi usare la bar ma con --flag1 disabilitato, ti permette di fare:

 bar --flag1=0 

Okay, questo è l’inizio della lunga storia, fatta brevemente, parsing di una linea 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; iQuite 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 che ha postato qui, ma non sono stato davvero soddisfatto di tutte le versioni autonome che ho visto qui. Bene, quelli della libertà si sono lasciati andare bene. Quindi preferirei l' opzione parser libUCW , Arg o Getopt su quelli fatti in casa.

Nota che puoi cambiare:

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

Va bene analizziamolo: 1. argv [i] -> accede all'elemento i-esimo nel campo del puntatore argv-char

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

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

  3. ++ (...) -> parentesi ci sono così aumenteremo il puntatore e non il valore del char stesso.

Così bello che In C ## i puntatori "sono morti" - lunga vita ai suggerimenti !!!