Analisando argumentos de linha de comando em C?

98

Estou tentando escrever um programa que pode comparar dois arquivos linha por linha, palavra por palavra ou caractere por caractere em C. Tem que ser capaz de ler as opções de linha de comando -l -w -i or --...

  • se a opção for -l, ele compara os arquivos linha por linha.
  • se a opção for -w, ele compara os arquivos palavra por palavra.
  • se as opções forem - ele assume automaticamente que o próximo argumento é o primeiro nome do arquivo.
  • se a opção for -i, ele os compara de uma maneira que não diferencia maiúsculas de minúsculas.
  • o padrão é comparar os arquivos caractere por caractere.

Não importa quantas vezes as opções são inseridas, desde que -w e -l não sejam inseridos ao mesmo tempo e não haja mais ou menos que 2 arquivos.

Eu nem sei por onde começar analisando os argumentos da linha de comando. POR FAVOR AJUDE :(

Portanto, este é o código que criei para tudo. Ainda não verifiquei o erro, mas gostaria de saber se estou escrevendo as coisas de uma maneira complicada demais.

/*
 * 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;
}
usuário1251020
fonte
Não tenho certeza de como usar getopt () ... Não aprendi sobre isso ainda em minha aula.
user1251020
3
Então, vá e leia a página de manual para ele; não é muito complexo, e a página de manual provavelmente inclui um exemplo para você experimentar (e se sua página de manual local não incluir, você certamente encontrará exemplos na web).
Jonathan Leffler
1
Esta é uma biblioteca de alto nível: argparse in c, muito fácil de usar.
Cofyc de
Para coisas simples, você pode apenas criar seu próprio em vez de usar uma biblioteca. Escrevi um tutorial de
introdução

Respostas:

187

Pelo que sei, as três maneiras mais populares de analisar argumentos de linha de comando em C são:

  • Getopt ( #include <unistd.h>da Biblioteca POSIX C), que pode resolver tarefas simples de análise de argumentos . Se você estiver um pouco familiarizado com o bash, o getopt embutido do bash é baseado no Getopt do GNU libc.
  • Argp ( #include <argp.h>da GNU C Library), que pode resolver tarefas mais complexas e cuidar de coisas como, por exemplo:
    • -?, --helppara mensagem de ajuda , incluindo endereço de e-mail
    • -V, --versionpara informações de versão
    • --usagepara mensagem de uso
  • Faça você mesmo , o que eu não recomendo para programas que seriam dados a outra pessoa, pois há muitas coisas que podem dar errado ou ter qualidade inferior. O erro comum de esquecer '-' para interromper a análise de opções é apenas um exemplo.

A documentação da Biblioteca GNU C tem alguns bons exemplos para Getopt e Argp.

Exemplo para usar 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.

    // ...
}

Exemplo para usar 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);

    // ...
}

Exemplo para fazer você mesmo

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

    // ...
}   

Isenção de responsabilidade: eu sou novo no Argp, o exemplo pode conter erros.

Christian Hujer
fonte
9
Resposta realmente completa, obrigado Christian (voto positivo). No entanto, os usuários do mac devem estar cientes de que a abordagem argp não é compatível com várias plataformas. Como descobri aqui , Argp é uma extensão de API glibc não padronizada. Ele está disponível no gnulib, portanto, pode ser adicionado a um projeto explicitamente. No entanto, é provavelmente mais simples para desenvolvedores somente Mac ou de plataforma cruzada usar a abordagem getopt.
thclark
1
Para a versão faça você mesmo, não gosto que as opções permitam texto extra depois, como -wzzz analisa o mesmo que -w, e também que as opções têm que vir antes dos argumentos do arquivo.
Jake
1
@Jake você está certo. Respeito por perceber isso. Não me lembro se percebi isso quando o escrevi. É novamente um exemplo perfeito de que DIY é tão fácil de errar e, portanto, não deve ser feito. Obrigado por contar, posso corrigir o exemplo.
Christian Hujer
18

Use getopt(), ou talvez 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);
        }
    }
 }

Observe que você precisa determinar quais cabeçalhos incluir (eu faço 4 que são obrigatórios), e a maneira como escrevi o op_modetipo significa que você tem um problema na função process()- você não pode acessar a enumeração lá embaixo. É melhor mover a enumeração para fora da função; você pode até criar op_modeuma variável de escopo de arquivo sem ligação externa (uma maneira elegante de dizer static) para evitar passá-la para a função. Este código não é -considerado sinônimo de entrada padrão, outro exercício para o leitor. Observe que getopt()automaticamente se encarrega de --marcar o fim das opções para você.

Não executei nenhuma versão da digitação acima em um compilador; pode haver erros nele.


Para obter crédito extra, escreva uma função (biblioteca):

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

que encapsula a lógica para processar opções de nome de arquivo após o getopt()loop. Deve ser tratada -como entrada padrão. Observe que usar isso indica que op_modedeve ser uma variável de escopo de arquivo estático. A filter()função leva argc, argv, optinde um ponteiro para a função de processamento. Deve retornar 0 (EXIT_SUCCESS) se foi capaz de abrir todos os arquivos e todas as invocações da função relataram 0, caso contrário, 1 (ou EXIT_FAILURE). Ter tal função simplifica a escrita de programas de 'filtro' no estilo Unix que leem arquivos especificados na linha de comando ou entrada padrão.

Jonathan Leffler
fonte
Não gosto que getopt () não permita opções após o primeiro arquivo.
Jake
POSIX getopt()não; GNU getopt()faz por padrão. Faça sua escolha. Não estou interessado nas opções após o comportamento dos nomes de arquivo, principalmente porque não é confiável entre plataformas.
Jonathan Leffler
14

Achei o Gengetopt bastante útil - você especifica as opções que deseja com um arquivo de configuração simples e ele gera um par .c / .h que você simplesmente inclui e vincula ao seu aplicativo. O código gerado usa getopt_long, parece lidar com os tipos mais comuns de parâmetros de linha de comando e pode economizar muito tempo.

Um arquivo de entrada gengetopt pode ter a seguinte aparência:

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 

Gerar o código é fácil e rápido cmdline.he cmdline.c:

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

O código gerado é facilmente integrado:

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

Se você precisar fazer qualquer verificação extra (como garantir que os sinalizadores sejam mutuamente exclusivos), você pode fazer isso facilmente com os dados armazenados na gengetopt_args_infoestrutura.

davidA
fonte
1 ++ exceto que gera código que gera avisos :(
cat
Sim Infelizmente. Eu coloquei exceções no meu arquivo cmake.
davidA,
Provavelmente usarei os pragmas do GCC para ignorar os avisos desse arquivo (horrível, eu sei)
cat,
Observe que você obviamente os perderá se regenerar a fonte, então você pode querer aplicá-los como um patch em seu processo de construção. Francamente, achei mais fácil simplesmente desligar os avisos nesses arquivos específicos.
davidA,
bem, não, quero dizer colocar os pragmas em torno do #include, não no próprio arquivo gerado. para mim, desligar avisos é proibido :-)
cat,
6

Estou muito surpreso por ninguém ter mencionado o pacote "opt" de James Theiler.

Você pode encontrar opt em http://public.lanl.gov/jt/Software/

e uma postagem lisonjeira com alguns exemplos de como isso é muito mais simples do que outras abordagens está aqui:

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

Markgalassi
fonte
2
@cat O que te faz pensar que é necessária uma atualização desde então? Essa é simplesmente a atitude errada a se ter em relação ao software.
Joshua Hedges
@JoshuaHedges A menos que eu queira manter o projeto por conta própria, quero usar código mantido ativamente em meu código mantido ativamente. Há muitos projetos de 2006 que são mantidos ativamente, mas este morreu, e provavelmente com bugs. Além disso, 2 anos atrás (quase exatamente!) Foi há muito tempo que eu escrevi isso: P
cat
1
opt não é mantido ativamente porque é completo e compacto. Para jogar, acabei de baixá-lo e tentei compilá-lo (gcc-7.3) e descobri que a biblioteca é compilada e funciona, mas o teste C ++ pode fazer com alguns pequenos trabalhos. iostream.h deve se tornar iostream, e usando o namespace std; deve ser adicionado. Vou mencionar isso para James. Isso afeta apenas o teste da API C ++, não o código em si.
markgalassi
4

Docopt tem uma implementação C que eu achei muito boa: https://github.com/docopt/docopt.c

A partir de um formato padronizado de página de manual que descreve opções de linha de comando, docopt infere e cria um analisador de argumento. Isso começou em python; a versão python literalmente apenas analisa a docstring e retorna um dict. Fazer isso em C dá um pouco mais de trabalho, mas é fácil de usar e não tem dependências externas.

gvoysey
fonte
3

Há uma ótima biblioteca C de uso geral, libUCW, que inclui análise de opções de linha de comando e carregamento de arquivo de configuração .

A biblioteca também vem com uma boa documentação e inclui algumas outras coisas úteis (IO rápido, estruturas de dados, alocadores, ...) mas isso pode ser usado separadamente.

Exemplo de analisador de opção libUCW (da documentação da biblioteca)

#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
fonte
a opção posicional tem bug. se houver dois OPT_STRING, e um for posicional, outro não, não será possível analisar.
NewBee
2

Escrevi uma pequena biblioteca que analisa argumentos semelhantes ao POpt, com o qual tive vários problemas, chamada XOpt . Usa análise de argumentos no estilo GNU e tem uma interface muito semelhante ao POpt.

Eu o uso de vez em quando com grande sucesso e funciona em praticamente qualquer lugar.

Qix - MONICA ERA MISTURADA
fonte
1

Tomando minha própria buzina, se possível, também gostaria de sugerir que você uma olhada em uma biblioteca de análise de opções que escrevi: dropt .

  • É uma biblioteca C (com um wrapper C ++ se desejado).
  • É leve.
  • É extensível (tipos de argumentos personalizados podem ser facilmente adicionados e têm igualdade com os tipos de argumentos integrados).
  • Deve ser muito portátil (é escrito em C padrão) sem dependências (além da biblioteca padrão C).
  • Possui uma licença muito restritiva (zlib / libpng).

Um recurso que ele oferece que muitos outros não oferecem é a capacidade de substituir opções anteriores. Por exemplo, se você tiver um alias de shell:

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

e você deseja usar, barmas com --flag1desativado, permite que você faça:

bar --flag1=0
Jamesdlin
fonte
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;
}
Pod
fonte
4
Não; absolutamente não é uma boa maneira de fazer isso ... Use uma das funções de análise de argumento - getopt()ou getopt_long().
Jonathan Leffler
5
Parece uma trapaça, visto que essa é uma pergunta descaradamente de dever de casa. Além disso, o OP está tendo dificuldade em entender o conceito do que é uma string e como ler partes dela. Impor getopts nele é um erro.
Pod
É uma questão de lição de casa. Eu sei o que é uma corda. Só não entendo como decompor os argumentos da linha de comando porque me parece confuso quando você pode inserir as opções várias vezes, de modo que não é possível descobrir realmente onde estão os nomes dos arquivos. Talvez eu esteja pensando demais nisso?
usuário1251020
0

Modelo de instrução para analisar argumentos de linha de comando em 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
fonte
1
... Não acho que haja uma variável booleana em C ...?
usuário1251020
Meu ambiente eclipse / windows tem o tipo BOOL. Basta alterá-lo para digitar int ou char e ajustar o código de acordo.
Java42
1
C99 tem um tipo _Boolem todos os momentos e um cabeçalho <stdbool.h>que define boolcomo _Boole truee falsee __bool_true_false_are_defined, todas as macros (que, excepcionalmente, podem ser indefinidas e redefinidas sem invocar um comportamento indefinido; essa licença é, no entanto, marcada como 'obsoleta'). Portanto, se você tiver um compilador C99, poderá usar <stdbool.h>e bool. Caso contrário, você pode escrever um para você mesmo (não é difícil) ou usar um equivalente nativo.
Jonathan Leffler
1
@Wolfer Meu ambiente C tem tipo BOOL (como typedef int BOOL) e tipo booleano (como typedef unsigned char boolean) e nenhuma definição para o tipo bool. No exemplo, simplesmente mude para digitar int ou char e ajuste o código de acordo.
Java42 de
3
Eu discordo dessa abordagem. Use uma função de biblioteca para analisar opções.
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;
    }
Anônimo
fonte
2
Você precisa explicar seu código em vez de apenas vomitá-lo e esperar que todos o entendam. Este é um site para aprender não apenas copiar e colar.
Yokai,
0

Ok, esse é o início de uma longa história - resumida ao analisar uma linha de comando em 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");
  }
}

Observe que esta versão também suporta a combinação de argumentos: Portanto, em vez de escrever / h / s -> / hs , também funcionará.

Desculpe por ser a enésima pessoa postando aqui - no entanto, não fiquei muito satisfeito com todas as versões autônomas que vi aqui. Bem, os lib são bem legais. Portanto, eu preferiria o analisador de opções libUCW , Arg ou Getopt em vez de um feito em casa.

Observe que você pode mudar:

*++argv[i]-> (++argv*)[0] mais menos enigmático, mas ainda enigmático.

Ok, vamos decompô-lo: 1. argv [i] -> acesse o i-ésimo elemento no campo do ponteiro argv-char

  1. ++ * ... -> irá encaminhar o ponteiro argv em um caractere

  2. ... [0] -> seguirá o ponteiro ler o caractere

  3. ++ (...) -> colchetes estão lá, então vamos aumentar o ponteiro e não o valor char em si.

Tão bom que em C ## os ponteiros 'morreram' - viva os ponteiros !!!

Nadu
fonte