Opdrachtregelargumenten ontleden in C

Ik probeer een programma te schrijven dat twee bestanden regel voor regel, woord voor woord of teken voor teken kan vergelijken in C. Het moet de opdrachtregelopties -l, -w, -iof --

  • als de optie -lis, worden de bestanden regel voor regel vergeleken.
  • als de optie -wis, worden de bestanden woord voor woord vergeleken.
  • als de optie --is, wordt er automatisch van uitgegaan dat het volgende argument de eerste bestandsnaam is.
  • als de optie -iis, worden ze op hoofdletterongevoelige wijze vergeleken.
  • standaard om de bestanden teken voor teken te vergelijken.

Het is niet de bedoeling dat het uitmaakt hoe vaak de opties worden ingevoerd, zolang -wen -lniet tegelijkertijd worden ingevoerd en er niet meer zijn of minder dan twee bestanden.

Ik weet niet eens waar ik moet beginnen met het ontleden van de opdrachtregelargumenten.

Dus dit is de code die ik voor alles heb bedacht. Ik heb het nog niet helemaal gecontroleerd, maar schrijf ik dingen op een te ingewikkelde manier?

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

Antwoord 1, autoriteit 100%

Voor zover ik weet, zijn de drie meest populaire manieren om opdrachtregelargumenten in C te ontleden:

  • Getopt(#include <unistd.h>uit de POSIX C-bibliotheek), die eenvoudige argumentparsing-taken kan oplossen. Als je een beetje bekend bent met bash, de ingebouwde getopt van bash is gebaseerd op Getopt van de GNU libc.
  • Argp (#include <argp.h>van de GNU C-bibliotheek), die meer complexe taken kan oplossen en zorgt voor van dingen zoals, bijvoorbeeld:
    • -?, --helpVOOR Help-bericht , inclusief E-mailadres
    • -V, --versionVOOR Versie-informatie
    • --usageVoor Gebruikersbericht
  • Doe het zelf , die ik niet aanbeveel voor programma’s die aan iemand anders zouden worden gegeven, want er is te veel die fout of een lagere kwaliteit kan gaan. De populaire fout om te vergeten over ‘-‘ om optie-parseren te stoppen is slechts een voorbeeld.

De GNU C-bibliotheekdocumentatie heeft enkele leuke voorbeelden voor Getopt en Argp.

Voorbeeld voor het gebruik van 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.
    // ...
}

Voorbeeld voor het gebruik van 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);
    // ...
}

Voorbeeld voor Zelf doen

#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 += optind;
    // *argv points to the remaining non-option arguments.
    // If *argv is NULL, there were no non-option arguments.
    // ...
}   

Disclaimer: ik ben nieuw bij Argp, het voorbeeld kan fouten bevatten.


Antwoord 2, autoriteit 9%

Ik heb Gengetoptheel nuttig gevonden – u specificeert de gewenste opties met een eenvoudig configuratiebestand, en het genereert een .c/.h-paar dat u eenvoudigweg opneemt en koppelt aan uw toepassing. De gegenereerde code maakt gebruik van getopt_long, lijkt de meest voorkomende soorten opdrachtregelparameters te verwerken en kan veel tijd besparen.

Een gengetopt-invoerbestand kan er ongeveer zo uitzien:

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 

Het genereren van de code is eenvoudig en spuugt cmdline.hen cmdline.cuit:

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

De gegenereerde code is eenvoudig te integreren:

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

Als u extra controle moet uitvoeren (zoals ervoor zorgen dat vlaggen elkaar uitsluiten), kunt u dit vrij eenvoudig doen met de gegevens die zijn opgeslagen in de gengetopt_args_info-struct.


Antwoord 3, autoriteit 8%

Gebruik getopt(), of misschien 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);
        }
    }
 }

Merk op dat u moet bepalen welke headers opnemen (ik maak het 4 die nodig zijn), en de manier waarop ik de op_modeTYPE schreef, betekent dat u een probleem hebt in de functie process()– u hebt daar geen toegang tot de opsomming. Het is het beste om de opsomming buiten de functie te verplaatsen; U kunt zelfs op_modeA File-Scope-variabele maken zonder externe koppeling (een mooie manier om te zeggen static) om te voorkomen dat u deze aan de functie kunt doorgeven. Deze code verwerkt niet -als een synoniem voor standaardinvoer, een andere oefening voor de lezer. Merk op dat getopt()automatisch voor -zorgt om het einde van de opties voor u te markeren.

Ik heb geen enkele versie van het typ boven een compiler uitgevoerd; Er kunnen er fouten in zijn.


Voor extra krediet, schrijf een (bibliotheek) functie:

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

die de logica inkapselt voor het verwerken van bestandsnaamopties na de getopt()-lus. Het zou -als standaard invoer moeten verwerken. Merk op dat het gebruik hiervan zou aangeven dat op_modeeen statische bestandsbereikvariabele zou moeten zijn. De functie filter()heeft argc, argv, optindnodig en een verwijzing naar de verwerkingsfunctie. Het zou 0 (EXIT_SUCCESS) moeten retourneren als het in staat was om alle bestanden en alle aanroepen van de functie gerapporteerd 0 te openen, anders 1 (of EXIT_FAILURE). Het hebben van een dergelijke functie vereenvoudigt het schrijven van Unix-achtige ‘filter’-programma’s die bestanden lezen die zijn opgegeven op de opdrachtregel of standaardinvoer.


Antwoord 4, autoriteit 3%

Je kunt het “opt”-pakketvan James Theiler gebruiken.

En een vleiend bericht met enkele voorbeelden van hoe het zo veel eenvoudiger is dan andere benaderingen is hier:

Opt 3.19 beoordeling en upgrades


Antwoord 5, autoriteit 3%

Docoptheeft een C-implementatie waarvan ik dacht dat die behoorlijk
leuk:

Vanuit een gestandaardiseerd man-pagina-formaat dat opdrachtregelopties beschrijft, leidt docopt af en maakt een argumentparser. Dit is begonnen in Python; de Python-versie parseert letterlijk gewoon de docstring en retourneert een dictaat. Om dit in C te doen, is wat meer werk nodig, maar het is schoon in gebruik en heeft geen externe afhankelijkheden.


Antwoord 6

Er is een geweldige C-bibliotheek voor algemeen gebruik, libUCW, die netjes ontleding van opdrachtregeloptiesen configuratiebestand laden.

De bibliotheek wordt ook geleverd met goede documentatie en bevat een aantal andere nuttige dingen (snelle I/O, datastructuren, allocators, …) maar dit kan afzonderlijk worden gebruikt.

Voorbeeld libUCW-optie-parser (uit de bibliotheekdocumenten)

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

Antwoord 7

Ik heb een kleine bibliotheek geschreven die argumenten parseert die lijken op POpt, waar ik verschillende problemen mee had, genaamd XOpt. Het gebruikt het ontleden van argumenten in GNU-stijl en heeft een interface die erg lijkt op POpt.

Ik gebruik het van tijd tot tijd met groot succes, en het werkt vrijwel overal.


Antwoord 8

Mijn eigen hoorn tooteren als ik mag, zou ik het ook willen voorstellen om een ​​kijkje te nemen naar een optie parserende bibliotheek die ik heb geschreven: Dropt .

  • Het is een C-bibliotheek (indien gewenst met een C++ -verpakking).
  • Het is lichtgewicht.
  • Het is uitgebreid (aangepaste argumentsoorten kunnen eenvoudig worden toegevoegd en gelijke voet met ingebouwde argumententypen).
  • Het moet erg draagbaar zijn (het is in standaard C) geschreven zonder afhankelijkheden (anders dan de C standaardbibliotheek).
  • Het heeft een zeer onberispelijke licentie (ZLIB / LIBPNG).

één functie die het biedt dat vele anderen niet het vermogen zijn om eerdere opties te overschrijven. Als u bijvoorbeeld een Shell-alias hebt:

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

en u wilt bargebruiken met --flag1Uitgeschakeld, hiermee kunt u doen:

bar --flag1=0

9

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

Antwoord 10

Instructiesjabloon voor het ontleden van opdrachtregelargumenten in C.

C:>programName -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;
}

Antwoord 11

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

12

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int is_arg(int ac, char **argv, char *arg) {
    if (ac < 2) {
        return 0;
    }
    for(int x=1; x < ac; x++) {
        if (0 == strcmp(argv[x], arg)) {
            return x;  // return position of arg
        }
    }
    return 0;  // arg not present
}
int main (int argc, char *argv[]) {
    int z = 0;
    if (argc < 2) {
        printf("no args present, aborting.\n");
        exit(1);
    }
    (z=is_arg(argc, argv, "bar")) ?     printf("TRUE %d\n", z) : printf("FALSE\n");
    (z=is_arg(argc, argv, "one bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n");
    (z=is_arg(argc, argv, "foo")) ?     printf("TRUE %d\n", z) : printf("FALSE\n");
    /* testing:
    run: ./getopt two bar "one bar" foo
    TRUE 2
    TRUE 3
    TRUE 4
    run: ./getopt two bar one bar foo
    TRUE 2
    FALSE
    TRUE 5
    */
    return 0;
}

Antwoord 13

Schaamteloze stekker. Ik ben de auteur van nog een andere opdrachtregel-parser genaamd clip. De parser is platformonafhankelijk en werkt met veel verschillende compilers. Ik heb het geprobeerd met GCC, clang, Turbo C, Open Watcom en zelfs Microsoft Visual C++.

Other episodes