Parsing command-line arguments in C?

后端 未结 12 691
眼角桃花
眼角桃花 2020-11-22 16:23

I\'m trying to write a program that can compare two files line by line, word by word, or character by character in C. It has to be able to read in command line options

相关标签:
12条回答
  • 2020-11-22 16:44

    There is a great general-purpose C library libUCW which includes neat command-line option parsing and config file loading.

    The library also comes with good documentation and includes some other useful stuff (fast IO, data structures, allocators, ...) but this can be used separately.

    Example libUCW option parser (from the library docs)

    #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;
    }
    
    0 讨论(0)
  • 2020-11-22 16:44

    Okay that's the start of long story - made short 'bort parsing a command line 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");
      }
    }
    

    Note that this version will also support combining arguments: So instead of writing /h /s -> /hs will also work.

    Sorry for being the n-th person posting here - however I wasn't really satisfied with all the stand-alone-versions I saw here. Well the lib ones are quit nice. So I would prefere libUCW option parser, Arg or Getopt over a home-made ones.

    Note you may change:

    *++argv[i] -> (++argv*)[0] longer less cryptic but still cryptic.

    Okay let's break it down: 1. argv[i]-> access i-th element in the argv-char pointer field

    1. ++*... -> will forward the argv-pointer by one char

    2. ... [0]-> will follow the pointer read the char

    3. ++(...) -> bracket are there so we'll increase the pointer and not the char value itself.

    So nice that In C## the pointers 'died' - long live the pointers !!!

    0 讨论(0)
  • 2020-11-22 16:45

    I wrote a tiny library that parses arguments similar to POpt, which I had several issues with, called XOpt. Uses GNU-style argument parsing and has a very similar interface to POpt.

    I use it from time to time with great success, and it works pretty much anywhere.

    0 讨论(0)
  • 2020-11-22 16:46

    Instructional template for parsing command line arguments 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;
    }
    
    0 讨论(0)
  • 2020-11-22 16:47
    #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;
    }
    
    0 讨论(0)
  • 2020-11-22 16:48

    I've found Gengetopt to be quite useful - you specify the options you want with a simple configuration file, and it generates a .c/.h pair that you simply include and link with your application. The generated code makes use of getopt_long, appears to handle most common sorts of command line parameters, and it can save a lot of time.

    A gengetopt input file might look something like this:

    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 
    

    Generating the code is easy and spits out cmdline.h and cmdline.c:

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

    The generated code is easily integrated:

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

    If you need to do any extra checking (such as ensuring flags are mutually exclusive), you can do this fairly easily with the data stored in the gengetopt_args_info struct.

    0 讨论(0)
提交回复
热议问题