Parsing argumen baris perintah di C?

98

Saya mencoba menulis sebuah program yang dapat membandingkan dua file baris demi baris, kata demi kata, atau karakter demi karakter dalam C. Program itu harus dapat membaca dalam opsi baris perintah -l -w -i or --...

  • jika opsi -l ini membandingkan file baris demi baris.
  • jika opsinya adalah -w itu membandingkan file kata demi kata.
  • jika opsinya adalah - secara otomatis mengasumsikan bahwa arg berikutnya adalah nama file pertama.
  • jika opsinya adalah -i maka akan membandingkannya dengan cara yang tidak membedakan huruf besar / kecil.
  • default untuk membandingkan karakter file dengan karakter.

Tidak masalah berapa kali opsi dimasukkan selama -w dan -l tidak dimasukkan pada saat yang sama dan tidak lebih atau kurang dari 2 file.

Saya bahkan tidak tahu harus mulai dari mana dengan mengurai argumen baris perintah. TOLONG BANTU :(

Jadi ini adalah kode yang saya buat untuk semuanya. Saya belum memeriksanya dengan kesalahan, tetapi saya bertanya-tanya apakah saya menulis sesuatu dengan cara yang terlalu rumit?

/*
 * 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;
}
pengguna1251020
sumber
Saya tidak begitu yakin bagaimana menggunakan getopt () ... Saya belum mempelajarinya di kelas saya.
pengguna1251020
3
Jadi, buka dan baca halaman manual untuk itu; ini tidak terlalu rumit, dan halaman manual mungkin menyertakan sebuah contoh untuk Anda coba (dan jika halaman manual lokal Anda tidak, Anda pasti dapat menemukan contoh di web).
Jonathan Leffler
1
Ini adalah pustaka tingkat tinggi: argparse di c, sangat mudah digunakan.
Cofyc
Untuk hal-hal sederhana Anda bisa menggulung sendiri alih-alih menggunakan perpustakaan. Saya menulis tutorial memulai di sini engineeringterminal.com/computer-science/tutorials/…
nalyd88

Jawaban:

188

Sepengetahuan saya, tiga cara paling populer untuk mengurai argumen baris perintah di C adalah:

  • Getopt ( #include <unistd.h>dari Pustaka C POSIX), yang dapat menyelesaikan tugas penguraian argumen sederhana . Jika Anda sedikit terbiasa dengan bash, getopt bawaan dari bash didasarkan pada Getopt dari GNU libc.
  • Argp ( #include <argp.h>dari GNU C Library), yang dapat menyelesaikan tugas yang lebih kompleks dan menangani hal-hal seperti, misalnya:
    • -?, --helpuntuk pesan bantuan , termasuk alamat email
    • -V, --versionuntuk informasi versi
    • --usageuntuk pesan penggunaan
  • Melakukannya sendiri , yang tidak saya rekomendasikan untuk program yang akan diberikan kepada orang lain, karena terlalu banyak program yang bisa salah atau kualitasnya lebih rendah. Kesalahan populer dari melupakan '-' untuk menghentikan penguraian opsi hanyalah salah satu contoh.

Dokumentasi GNU C Library memiliki beberapa contoh bagus untuk Getopt dan Argp.

Contoh penggunaan 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.

    // ...
}

Contoh penggunaan 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);

    // ...
}

Contoh untuk Melakukannya Sendiri

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

    // ...
}   

Penafian: Saya baru mengenal Argp, contohnya mungkin mengandung kesalahan.

Christian Hujer
sumber
9
Jawaban yang benar-benar menyeluruh, terima kasih Christian (suara positif). Namun, pengguna mac harus menyadari bahwa pendekatan argp tidak kompatibel lintas platform. Seperti yang saya temukan di sini , Argp adalah ekstensi API glibc non-standar. Ini tersedia di gnulib sehingga dapat ditambahkan ke proyek secara eksplisit. Namun, mungkin lebih mudah bagi pengembang khusus mac atau lintas platform untuk menggunakan pendekatan getopt.
thclark
1
Untuk versi do it yourself, saya tidak suka bahwa opsi mengizinkan teks tambahan setelahnya, seperti -wzzz mem-parsing sama dengan -w, dan juga bahwa opsi harus ada sebelum argumen file.
Jake
1
@Jake kamu benar. Hormat untuk melihat itu. Saya tidak ingat apakah saya melihatnya ketika saya menulisnya. Sekali lagi ini adalah contoh sempurna bahwa DIY sangat mudah salah dan karenanya tidak boleh dilakukan. Terima kasih sudah memberi tahu, saya mungkin memperbaiki contoh.
Christian Hujer
18

Gunakan getopt(), atau mungkin 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);
        }
    }
 }

Perhatikan bahwa Anda perlu menentukan header mana yang akan disertakan (saya membuatnya 4 yang diperlukan), dan cara saya menulis op_modejenisnya berarti Anda memiliki masalah dalam fungsinya process()- Anda tidak dapat mengakses pencacahan di sana. Yang terbaik adalah memindahkan pencacahan di luar fungsi; Anda bahkan dapat membuat op_modevariabel cakupan file tanpa tautan eksternal (cara yang bagus untuk mengatakannya static) untuk menghindari meneruskannya ke fungsi. Kode ini tidak menangani -sebagai sinonim untuk input standar, latihan lain untuk pembaca. Perhatikan bahwa getopt()secara otomatis menangani --untuk menandai akhir opsi untuk Anda.

Saya belum menjalankan versi apa pun dari pengetikan di atas melewati kompiler; mungkin ada kesalahan di dalamnya.


Untuk kredit ekstra, tulis fungsi (perpustakaan):

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

yang merangkum logika untuk memproses opsi nama file setelah getopt()pengulangan. Ini harus ditangani -sebagai input standar. Perhatikan bahwa menggunakan ini akan menunjukkan bahwa op_modeseharusnya variabel cakupan file statis. The filter()fungsi mengambil argc, argv, optinddan pointer ke fungsi pengolahan. Ini harus mengembalikan 0 (EXIT_SUCCESS) jika ia dapat membuka semua file dan semua pemanggilan fungsi yang dilaporkan 0, jika tidak 1 (atau EXIT_FAILURE). Memiliki fungsi seperti itu menyederhanakan penulisan program 'filter' gaya Unix yang membaca file yang ditentukan pada baris perintah atau input standar.

Jonathan Leffler
sumber
Saya tidak suka getopt () tidak mengizinkan opsi setelah file pertama.
Jake
POSIX getopt()tidak; GNU getopt()melakukannya secara default. Ambil pilihanmu. Saya tidak tertarik pada opsi setelah perilaku nama file, terutama karena tidak dapat diandalkan di seluruh platform.
Jonathan Leffler
14

Saya telah menemukan Gengetopt cukup berguna - Anda menentukan opsi yang Anda inginkan dengan file konfigurasi sederhana, dan itu menghasilkan pasangan .c / .h yang cukup Anda sertakan dan tautkan dengan aplikasi Anda. Kode yang dihasilkan menggunakan getopt_long, tampaknya menangani jenis parameter baris perintah yang paling umum, dan dapat menghemat banyak waktu.

File masukan gengetopt mungkin terlihat seperti ini:

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 

Membuat kode itu mudah dan mudah cmdline.hdan cmdline.c:

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

Kode yang dihasilkan mudah diintegrasikan:

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

Jika Anda perlu melakukan pemeriksaan tambahan (seperti memastikan flag saling eksklusif), Anda dapat melakukannya dengan cukup mudah dengan data yang disimpan di gengetopt_args_infostruct.

davidA
sumber
1 ++ kecuali ia menghasilkan kode yang menghasilkan peringatan :(
cat
Ya, sayangnya. Saya menaruh pengecualian di file cmake saya.
davidA
Saya mungkin hanya akan menggunakan pragma GCC untuk mengabaikan peringatan untuk file itu (mengerikan saya tahu)
cat
Perhatikan bahwa Anda jelas akan kehilangan mereka jika Anda membuat ulang sumber, jadi Anda mungkin ingin menerapkannya sebagai tambalan dalam proses pembangunan Anda. Sejujurnya saya merasa lebih mudah untuk mematikan peringatan pada file-file tertentu itu.
davidA
baik tidak, maksud saya meletakkan pragma di sekitar #include, bukan di file yang dihasilkan itu sendiri. bagi saya mematikan peringatan adalah verboten :-)
kucing
6

Saya sangat terkejut tidak ada yang menyinggung paket "opt" James Theiler.

Anda dapat menemukan opt di http://public.lanl.gov/jt/Software/

dan posting yang bagus dengan beberapa contoh bagaimana itu jauh lebih sederhana daripada pendekatan lain di sini:

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

markgalassi.dll
sumber
2
@cat Apa yang membuat Anda berpikir perlu pembaruan sejak saat itu? Itu hanyalah sikap yang salah tentang perangkat lunak.
Joshua Hedges
@JoshuaHedges Kecuali saya ingin memelihara proyek sendiri, saya ingin menggunakan kode yang dipelihara secara aktif dalam kode yang dipelihara secara aktif. Ada banyak proyek dari tahun 2006 yang secara aktif dipelihara, tetapi yang satu ini mati, dan mungkin dengan bug di dalamnya. Juga, 2 tahun yang lalu (hampir persis!) Sudah lama sekali saya menulis bahwa: P
cat
1
opt tidak aktif dipertahankan karena lengkap dan kompak. Sebagai permulaan, saya baru saja mengunduhnya dan mencoba membangunnya (gcc-7.3) dan menemukan bahwa perpustakaan dibangun dan berfungsi, tetapi tes C ++ dapat dilakukan dengan beberapa pekerjaan kecil. iostream.h harus menjadi iostream, dan menggunakan namespace std; harus ditambahkan. Saya akan menyebutkannya kepada James. Ini hanya memengaruhi pengujian C ++ API, bukan kode itu sendiri.
markgalassi
4

Docopt memiliki implementasi C yang menurut saya cukup bagus: https://github.com/docopt/docopt.c

Dari format standar halaman manual yang menjelaskan opsi baris perintah, docopt menyimpulkan dan membuat parser argumen. Ini dimulai dengan python; versi python secara harfiah hanya mem-parsing docstring dan mengembalikan sebuah dict. Untuk melakukan ini di C membutuhkan sedikit lebih banyak pekerjaan, tetapi bersih untuk digunakan dan tidak memiliki ketergantungan eksternal.

gvoysey.dll
sumber
3

Ada pustaka C libUCW serbaguna yang mencakup penguraian opsi baris perintah yang rapi dan pemuatan file konfigurasi .

Pustaka juga dilengkapi dengan dokumentasi yang baik dan menyertakan beberapa hal berguna lainnya (IO cepat, struktur data, pengalokasi, ...) tetapi ini dapat digunakan secara terpisah.

Contoh parser opsi libUCW (dari dokumen perpustakaan)

#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;
}
Tomáš Gavenčiak
sumber
opsi posisi memiliki bug. jika ada dua OPT_STRING, dan satu posisional, satu tidak, tidak dapat mengurai.
NewBee
2

Saya menulis perpustakaan kecil yang mem-parsing argumen yang mirip dengan POpt, yang memiliki beberapa masalah dengan saya, yang disebut XOpt . Menggunakan penguraian argumen gaya GNU dan memiliki antarmuka yang sangat mirip dengan POpt.

Saya menggunakannya dari waktu ke waktu dengan sukses besar, dan berhasil hampir di mana saja.

Qix - MONICA SALAH
sumber
1

Sambil mengomel sendiri jika boleh, saya juga ingin menyarankan untuk melihat opsi parsing library yang telah saya tulis: dropt .

  • Ini adalah pustaka C (dengan pembungkus C ++ jika diinginkan).
  • Ringan.
  • Ini dapat diperluas (tipe argumen kustom dapat dengan mudah ditambahkan dan memiliki pijakan yang sama dengan tipe argumen built-in).
  • Ini harus sangat portabel (ditulis dalam C standar) tanpa ketergantungan (selain pustaka standar C).
  • Ini memiliki lisensi yang sangat tidak terbatas (zlib / libpng).

Salah satu fitur yang ditawarkannya yang tidak ditawarkan oleh banyak orang lain adalah kemampuan untuk mengganti opsi sebelumnya. Misalnya, jika Anda memiliki alias shell:

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

dan Anda ingin menggunakan bartetapi dengan --flag1nonaktif, ini memungkinkan Anda untuk melakukan:

bar --flag1=0
jamesdlin.dll
sumber
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;
}
Polong
sumber
4
Tidak; benar-benar bukan cara yang baik untuk melakukannya ... Gunakan salah satu fungsi penguraian argumen - getopt()atau getopt_long().
Jonathan Leffler
5
Kedengarannya seperti curang, mengingat ini adalah pertanyaan pekerjaan rumah. Selain itu, OP mengalami kesulitan memahami konsep string dan cara membaca bagian-bagiannya. Membohongi dia adalah kesalahan.
Pod
Ini adalah pertanyaan pekerjaan rumah. Saya tahu apa itu string. Saya hanya tidak mengerti cara memecah argumen baris perintah karena tampaknya membingungkan bagi saya ketika Anda dapat memasukkan opsi beberapa kali, jadi Anda tidak dapat benar-benar mengetahui di mana nama file itu. Mungkin aku terlalu memikirkannya?
pengguna1251020
0

Template instruksional untuk mengurai argumen baris perintah di 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;
}
Java42
sumber
1
... Saya tidak berpikir ada variabel boolean di C ...?
pengguna1251020
Lingkungan eclipse / windows saya memiliki tipe BOOL. Cukup ubah untuk mengetik int atau char dan sesuaikan kode yang sesuai.
Java42
1
C99 memiliki tipe _Boolsetiap saat, dan header <stdbool.h>yang mendefinisikan boolsebagai _Booldan truedan falsedan __bool_true_false_are_defined, semua macro (yang, sangat, dapat terdefinisi dan didefinisikan ulang tanpa melibatkan perilaku undefined, lisensi yang, bagaimanapun, tagged 'usang'). Jadi, jika Anda memiliki kompiler C99, Anda dapat menggunakan <stdbool.h>dan bool. Jika tidak, Anda dapat menuliskannya untuk diri Anda sendiri (tidak sulit) atau Anda menggunakan padanan asli.
Jonathan Leffler
1
@Wolfer My C environment memiliki tipe BOOL (sebagai typedef int BOOL) dan tipe boolean (sebagai typedef unsigned char boolean) dan tidak ada definisi untuk tipe bool. Dalam contoh, cukup ubah untuk mengetik int atau char dan sesuaikan kode yang sesuai.
Java42
3
Saya tidak setuju dengan pendekatan ini. Gunakan fungsi perpustakaan untuk mengurai opsi.
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;
    }
Anonim
sumber
2
Anda perlu menjelaskan kode Anda daripada hanya memunculkannya dan mengharapkan semua orang memahaminya. Ini adalah situs untuk belajar tidak hanya menyalin dan menempel.
Yokai
0

Oke itulah awal dari cerita panjang - dibuat pendek 'bort parsing baris perintah di 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");
  }
}

Perhatikan bahwa versi ini juga akan mendukung penggabungan argumen: Jadi daripada menulis / h / s -> / hs juga akan berfungsi.

Maaf karena menjadi orang ke-n yang memposting di sini - namun saya tidak terlalu puas dengan semua versi mandiri yang saya lihat di sini. Yang lib cukup bagus. Jadi saya lebih suka parser opsi libUCW , Arg atau Getopt daripada yang dibuat sendiri.

Perhatikan bahwa Anda dapat mengubah:

*++argv[i]-> (++argv*)[0] lagi lebih sedikit samar tapi masih samar.

Oke mari kita hancurkan: 1. argv [i] -> akses elemen ke-i di bidang pointer argv-char

  1. ++ * ... -> akan meneruskan argv-pointer dengan satu karakter

  2. ... [0] -> akan mengikuti pointer membaca karakter

  3. ++ (...) -> braket ada sehingga kami akan meningkatkan penunjuk dan bukan nilai karakter itu sendiri.

Begitu bagusnya di C ## petunjuknya 'mati' - panjang umur petunjuknya !!!

Nadu
sumber