Phân tích cú pháp các đối số dòng lệnh trong C?


98

Tôi đang cố gắng viết một chương trình có thể so sánh hai tệp từng dòng, từng chữ hoặc từng ký tự trong C. Nó phải có thể đọc trong các tùy chọn dòng lệnh -l -w -i or --...

  • nếu tùy chọn là -l, nó sẽ so sánh các tệp từng dòng một.
  • nếu tùy chọn là -w nó sẽ so sánh các tệp từng từ một.
  • nếu các tùy chọn là - nó tự động giả định rằng đối số tiếp theo là tên tệp đầu tiên.
  • nếu tùy chọn là -i, nó sẽ so sánh chúng theo cách không phân biệt chữ hoa chữ thường.
  • mặc định so sánh các tệp theo từng ký tự.

Không quan trọng bao nhiêu thời gian các tùy chọn được nhập miễn là -w và -l không được nhập cùng một lúc và không có nhiều hơn hoặc ít hơn 2 tệp.

Tôi thậm chí không biết bắt đầu từ đâu với việc phân tích cú pháp các đối số dòng lệnh. XIN VUI LÒNG GIÚP ĐỠ :(

Vì vậy, đây là mã mà tôi đã nghĩ ra cho mọi thứ. Tôi vẫn chưa kiểm tra được lỗi, nhưng tôi tự hỏi liệu mình có đang viết mọi thứ theo cách quá phức tạp không?

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

Tôi không chắc về cách sử dụng getopt () ... Tôi chưa học về nó trong lớp của mình.
user1251020 10/03/12

3
Vì vậy, hãy đi và đọc trang hướng dẫn sử dụng nó; nó không phức tạp lắm và trang hướng dẫn sử dụng có thể bao gồm một ví dụ để bạn thử nghiệm (và nếu trang người đàn ông địa phương của bạn không có, bạn chắc chắn có thể tìm thấy các ví dụ trên web).
Jonathan Leffler

1
Đây là một thư viện cấp cao: argparse in c, rất dễ sử dụng.
Cofyc


Đối với những thứ đơn giản, bạn có thể cuộn của riêng mình thay vì sử dụng thư viện. Tôi đã viết một hướng dẫn bắt đầu tại đây engineeringterminal.com/computer-science/tutorials/…
nalyd88 Ngày

Câu trả lời:


187

Theo hiểu biết của tôi, ba cách phổ biến nhất để phân tích cú pháp các đối số dòng lệnh trong C là:

  • Getopt ( #include <unistd.h>từ Thư viện C POSIX), có thể giải quyết các tác vụ phân tích cú pháp đối số đơn giản . Nếu bạn hơi quen thuộc với bash, getopt tích hợp sẵn của bash dựa trên Getopt từ GNU libc.
  • Argp ( #include <argp.h>từ Thư viện GNU C), có thể giải quyết các tác vụ phức tạp hơn và xử lý những thứ như, ví dụ:
    • -?, --helpcho thông báo trợ giúp , bao gồm địa chỉ email
    • -V, --versionđể biết thông tin phiên bản
    • --usagecho tin nhắn sử dụng
  • Tự mình làm điều đó , điều mà tôi không khuyến khích đối với các chương trình sẽ được cung cấp cho người khác, vì có quá nhiều thứ có thể dẫn đến sai hoặc chất lượng thấp hơn. Sai lầm phổ biến khi quên '-' để dừng phân tích cú pháp tùy chọn chỉ là một ví dụ.

Tài liệu của Thư viện GNU C có một số ví dụ hay cho Getopt và Argp.

Ví dụ cho việc sử dụng 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.

    // ...
}

Ví dụ cho việc sử dụng 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);

    // ...
}

Ví dụ cho việc tự làm

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

    // ...
}   

Tuyên bố từ chối trách nhiệm: Tôi mới sử dụng Argp, ví dụ này có thể có lỗi.


9
Câu trả lời thực sự thấu đáo, cảm ơn Christian (đã ủng hộ). Tuy nhiên, người dùng mac nên lưu ý rằng phương pháp argp không tương thích với nhiều nền tảng. Như tôi đã tìm thấy ở đây , Argp là một phần mở rộng API glibc không được tiêu chuẩn hóa. Nó có sẵn trong gnulib nên có thể được thêm vào dự án một cách rõ ràng. Tuy nhiên, có lẽ đơn giản hơn đối với các nhà phát triển chỉ mac hoặc đa nền tảng sử dụng phương pháp getopt.
thclark

1
Đối với phiên bản do it yourself, tôi không thích việc các tùy chọn cho phép thêm văn bản sau đó, chẳng hạn như -wzzz phân tích cú pháp giống như -w và các tùy chọn cũng phải đến trước các đối số tệp.
Jake

1
@Jake bạn đúng. Tôn trọng vì đã phát hiện ra điều đó. Tôi không nhớ liệu tôi có phát hiện ra điều đó khi tôi viết nó hay không. Đây một lần nữa là một ví dụ hoàn hảo cho thấy việc tự làm rất dễ sai và do đó không nên làm. Cảm ơn vì đã nói, tôi có thể sửa ví dụ.
Christian Hujer

18

Sử dụng getopt(), hoặc có lẽ 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);
        }
    }
 }

Lưu ý rằng bạn cần xác định tiêu đề nào cần bao gồm (tôi đặt nó là 4 tiêu đề bắt buộc), và cách tôi viết op_modeloại có nghĩa là bạn gặp sự cố trong hàm process()- bạn không thể truy cập bảng liệt kê ở đó. Tốt nhất là di chuyển kiểu liệt kê ra bên ngoài hàm; bạn thậm chí có thể tạo op_modemột biến phạm vi tệp mà không có liên kết bên ngoài (một cách nói hoa mỹ static) để tránh chuyển nó vào hàm. Mã này không xử lý -như một từ đồng nghĩa với đầu vào tiêu chuẩn, một bài tập khác cho người đọc. Lưu ý rằng getopt()sẽ tự động xử lý --để đánh dấu sự kết thúc của các tùy chọn cho bạn.

Tôi chưa chạy bất kỳ phiên bản nào của cách nhập ở trên qua trình biên dịch; có thể có sai lầm trong đó.


Để có thêm tín dụng, hãy viết một hàm (thư viện):

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

đóng gói logic để xử lý các tùy chọn tên tệp sau getopt()vòng lặp. Nó sẽ xử lý -như đầu vào tiêu chuẩn. Lưu ý rằng việc sử dụng điều này sẽ chỉ ra rằng đó op_modephải là một biến phạm vi tệp tĩnh. Các filter()chức năng mất argc, argv, optindvà một con trỏ đến các chức năng xử lý. Nó sẽ trả về 0 (EXIT_SUCCESS) nếu nó có thể mở tất cả các tệp và tất cả các lệnh gọi của hàm được báo cáo là 0, nếu không thì 1 (hoặc EXIT_FAILURE). Có một chức năng như vậy sẽ đơn giản hóa việc viết các chương trình 'bộ lọc' kiểu Unix để đọc các tệp được chỉ định trên dòng lệnh hoặc đầu vào chuẩn.


Tôi không thích rằng getopt () không cho phép các tùy chọn sau tệp đầu tiên.
Jake

POSIX getopt()không; GNU getopt()làm theo mặc định. Bạn chọn đi. Tôi không quan tâm đến các tùy chọn sau hành vi tên tệp, chủ yếu là vì nó không đáng tin cậy trên các nền tảng.
Jonathan Leffler

14

Tôi thấy Gengetopt khá hữu ích - bạn chỉ định các tùy chọn bạn muốn bằng một tệp cấu hình đơn giản và nó tạo ra một cặp .c / .h mà bạn chỉ cần bao gồm và liên kết với ứng dụng của mình. Mã được tạo sử dụng getopt_long, dường như để xử lý hầu hết các loại thông số dòng lệnh phổ biến và nó có thể tiết kiệm rất nhiều thời gian.

Tệp đầu vào gengetopt có thể trông giống như sau:

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 

Việc tạo mã rất dễ dàng và chia ra cmdline.hcmdline.c:

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

Mã được tạo được tích hợp dễ dàng:

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

Nếu bạn cần kiểm tra thêm (chẳng hạn như đảm bảo cờ loại trừ lẫn nhau), bạn có thể thực hiện việc này khá dễ dàng với dữ liệu được lưu trữ trong gengetopt_args_infocấu trúc.


1 ++ ngoại trừ việc nó tạo mã tạo ra cảnh báo :(
cat

Có rất tiếc. Tôi đặt ngoại lệ trong tệp cmake của mình.
davidA

Có lẽ tôi sẽ chỉ sử dụng GCC pragmas để bỏ qua các cảnh báo cho tệp đó (tôi biết thật tệ)
cat

Lưu ý rằng rõ ràng bạn sẽ mất chúng nếu bạn tạo lại nguồn, vì vậy bạn có thể muốn áp dụng chúng như một bản vá trong quá trình xây dựng của mình. Thành thật mà nói, tôi đã thấy dễ dàng hơn khi chỉ cần tắt cảnh báo trên các tệp cụ thể đó.
davidA

à không, ý tôi là đặt các pragmas xung quanh #includechứ không phải trong chính tệp được tạo. với tôi cách tắt cảnh báo được verboten :-)
mèo

6

Tôi rất ngạc nhiên không ai đưa ra gói "lựa chọn" của James Theiler.

Bạn có thể tìm thấy tùy chọn tại http://public.lanl.gov/jt/Software/

và một bài đăng tâng bốc với một số ví dụ về cách nó đơn giản hơn nhiều so với các cách tiếp cận khác ở đây:

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


2
@cat Điều gì khiến bạn nghĩ rằng nó cần được cập nhật kể từ đó? Đó chỉ đơn giản là thái độ sai lầm đối với phần mềm.
Joshua Hedges

@JoshuaHedges Trừ khi tôi muốn tự mình duy trì dự án, tôi muốn sử dụng mã được duy trì tích cực trong mã được duy trì tích cực của mình. Có rất nhiều các dự án từ năm 2006 mà đang tích cực duy trì, nhưng điều này đã chết, và có lẽ với lỗi trong Ngoài ra, 2 năm trước (gần như chính xác!) Là một thời gian dài trước đây mà tôi đã viết rằng:. P
mèo

1
opt không được duy trì tích cực vì nó hoàn chỉnh và nhỏ gọn. Đối với kick, tôi vừa tải xuống và cố gắng xây dựng nó (gcc-7.3) và nhận thấy rằng thư viện xây dựng và hoạt động, nhưng thử nghiệm C ++ có thể thực hiện với một số công việc nhỏ. iostream.h nên trở thành iostream và sử dụng không gian tên std; nên được thêm vào. Tôi sẽ đề cập nó với James. Điều này chỉ ảnh hưởng đến kiểm tra C ++ API, không ảnh hưởng đến bản thân mã.
markgalassi

4

Docopt có một triển khai C mà tôi nghĩ là khá tốt: https://github.com/docopt/docopt.c

Từ một định dạng chuẩn hóa man-trang mô tả các tùy chọn dòng lệnh, docopt cung cấp và tạo một trình phân tích cú pháp đối số. Điều này đã bắt đầu trong python; phiên bản python theo nghĩa đen chỉ phân tích cú pháp docstring và trả về một dict. Để thực hiện điều này trong C cần nhiều công việc hơn một chút, nhưng nó rất dễ sử dụng và không có phụ thuộc bên ngoài.


3

Có một thư viện C đa năng tuyệt vời libUCW bao gồm phân tích cú pháp tùy chọn dòng lệnh gọn gàng và tải tệp cấu hình .

Thư viện cũng đi kèm với tài liệu tốt và bao gồm một số công cụ hữu ích khác (IO nhanh, cấu trúc dữ liệu, trình phân bổ, ...) nhưng điều này có thể được sử dụng riêng.

Ví dụ về trình phân tích cú pháp tùy chọn libUCW (từ tài liệu thư viện)

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

tùy chọn vị trí có lỗi. nếu có hai OPT_STRING và một là vị trí, một không, nó không thể phân tích cú pháp.
NewBee

2

Tôi đã viết một thư viện nhỏ phân tích cú pháp các đối số tương tự như POpt, mà tôi gặp một số vấn đề, được gọi là XOpt . Sử dụng phân tích cú pháp đối số kiểu GNU và có giao diện rất giống với POpt.

Tôi sử dụng nó theo thời gian và rất thành công và nó hoạt động khá nhiều ở bất cứ đâu.


1

Ngắt cái sừng của chính mình nếu có thể, tôi cũng muốn khuyên bạn nên xem thư viện phân tích cú pháp tùy chọn mà tôi đã viết: dropt .

  • Đó là một thư viện C (với một trình bao bọc C ++ nếu muốn).
  • Nó nhẹ.
  • Nó có thể mở rộng (các loại đối số tùy chỉnh có thể được thêm dễ dàng và có giá trị ngang nhau với các loại đối số tích hợp sẵn).
  • Nó phải rất dễ di chuyển (nó được viết bằng C chuẩn) mà không có phụ thuộc nào (ngoài thư viện C chuẩn).
  • Nó có một giấy phép không bị ràng buộc (zlib / libpng).

Một tính năng mà nó cung cấp mà nhiều người khác không có là khả năng ghi đè các tùy chọn trước đó. Ví dụ: nếu bạn có bí danh shell:

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

và bạn muốn sử dụng barnhưng --flag1bị vô hiệu hóa, nó cho phép bạn làm:

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
Không; hoàn toàn không phải là một cách làm hay ... Sử dụng một trong các hàm phân tích cú pháp đối số - getopt()hoặc getopt_long().
Jonathan Leffler

5
Nghe giống như một trò gian lận, vì đây là một câu hỏi bài tập về nhà. Ngoài ra, OP đang gặp khó khăn trong việc hiểu khái niệm chuỗi là gì và cách đọc các phần của nó. Việc ủng hộ anh ta là một sai lầm.
Pod

Đó là một câu hỏi bài tập về nhà. Tôi biết chuỗi là gì. Tôi chỉ không hiểu cách chia nhỏ các đối số dòng lệnh vì nó có vẻ khó hiểu với tôi khi bạn có thể nhập các tùy chọn bất kỳ lúc nào, vì vậy bạn không thể thực sự tìm ra vị trí của tên tệp. Có lẽ tôi đã suy nghĩ quá nhiều về nó?
user1251020 Ngày

0

Mẫu hướng dẫn để phân tích cú pháp các đối số dòng lệnh trong 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;
}

1
... Tôi không nghĩ rằng có một biến boolean trong C ...?
user1251020

Môi trường nhật thực / cửa sổ của tôi có kiểu BOOL. Chỉ cần thay đổi nó thành kiểu int hoặc char và điều chỉnh mã cho phù hợp.
Java42

1
C99 luôn có một loại _Boolvà một tiêu đề <stdbool.h>xác định bool_Booltruefalse__bool_true_false_are_defined, tất cả các macro (đặc biệt, có thể không được xác định và xác định lại mà không cần gọi hành vi không xác định; tuy nhiên, giấy phép đó được gắn thẻ 'lỗi thời'). Vì vậy, nếu bạn có trình biên dịch C99, bạn có thể sử dụng <stdbool.h>bool. Nếu không, bạn có thể viết một cái cho chính mình (không khó) hoặc bạn sử dụng một bản gốc tương đương.
Jonathan Leffler

1
@Wolfer Môi trường C của tôi có kiểu BOOL (như typedef int BOOL) và kiểu boolean (dưới dạng typedef unsigned char boolean) và không có định nghĩa cho kiểu bool. Trong ví dụ này, chỉ cần thay đổi thành nhập int hoặc char và điều chỉnh mã cho phù hợp.
Java42

3
Tôi không đồng ý với cách tiếp cận này. Sử dụng một hàm thư viện để phân tích cú pháp các tùy chọn.
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
Bạn cần giải thích mã của mình hơn là chỉ ném nó lên và mong mọi người hiểu nó. Đây là một trang web để học không chỉ sao chép và dán.
Yokai

0

Được rồi, đó là phần bắt đầu của câu chuyện dài - được thực hiện ngắn gọn 'bort phân tích cú pháp một dòng lệnh trong 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");
  }
}

Lưu ý rằng phiên bản này cũng sẽ hỗ trợ kết hợp các lập luận: Vì vậy, thay vì viết / h / s -> / hs cũng sẽ hoạt động.

Xin lỗi vì là người thứ n đăng bài ở đây - tuy nhiên tôi không thực sự hài lòng với tất cả các phiên bản độc lập mà tôi thấy ở đây. Vâng, những người lib được bỏ tốt. Vì vậy, tôi sẽ thích phân tích cú pháp tùy chọn libUCW , Arg hoặc Getopt thay vì những thứ tự làm tại nhà.

Lưu ý bạn có thể thay đổi:

*++argv[i]-> (++argv*)[0] còn ít khó hiểu nhưng vẫn khó hiểu.

Được rồi, hãy chia nhỏ nó ra: 1. argv [i] -> truy cập phần tử thứ i trong trường con trỏ argv-char

  1. ++ * ... -> sẽ chuyển tiếp con trỏ argv bởi một ký tự

  2. ... [0] -> sẽ theo con trỏ đọc ký tự

  3. ++ (...) -> dấu ngoặc ở đó nên chúng ta sẽ tăng con trỏ chứ không phải giá trị char.

Tốt đến nỗi Trong C ## con trỏ đã 'chết' - con trỏ sống lâu !!!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.