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;
}
c
command-line-arguments
usuário1251020
fonte
fonte
Respostas:
Pelo que sei, as três maneiras mais populares de analisar argumentos de linha de comando em C são:
#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.#include <argp.h>
da GNU C Library), que pode resolver tarefas mais complexas e cuidar de coisas como, por exemplo:-?
,--help
para mensagem de ajuda , incluindo endereço de e-mail-V
,--version
para informações de versão--usage
para mensagem de usoA documentação da Biblioteca GNU C tem alguns bons exemplos para Getopt e Argp.
Exemplo para usar Getopt
Exemplo para usar Argp
Exemplo para fazer você mesmo
Isenção de responsabilidade: eu sou novo no Argp, o exemplo pode conter erros.
fonte
Use
getopt()
, ou talvezgetopt_long()
.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_mode
tipo significa que você tem um problema na funçãoprocess()
- você não pode acessar a enumeração lá embaixo. É melhor mover a enumeração para fora da função; você pode até criarop_mode
uma variável de escopo de arquivo sem ligação externa (uma maneira elegante de dizerstatic
) 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 quegetopt()
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):
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 queop_mode
deve ser uma variável de escopo de arquivo estático. Afilter()
função levaargc
,argv
,optind
e 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.fonte
getopt()
não; GNUgetopt()
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.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:
Gerar o código é fácil e rápido
cmdline.h
ecmdline.c
:O código gerado é facilmente integrado:
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_info
estrutura.fonte
#include
, não no próprio arquivo gerado. para mim, desligar avisos é proibido :-)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/
fonte
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.
fonte
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)
fonte
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.
fonte
Tomando minha própria buzina, se possível, também gostaria de sugerir que você dê uma olhada em uma biblioteca de análise de opções que escrevi: dropt .
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:
e você deseja usar,
bar
mas com--flag1
desativado, permite que você faça:fonte
fonte
getopt()
ougetopt_long()
.Modelo de instrução para analisar argumentos de linha de comando em C.
C:> programName -w - fileOne.txt fileTwo.txt
fonte
_Bool
em todos os momentos e um cabeçalho<stdbool.h>
que definebool
como_Bool
etrue
efalse
e__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>
ebool
. Caso contrário, você pode escrever um para você mesmo (não é difícil) ou usar um equivalente nativo.fonte
Ok, esse é o início de uma longa história - resumida ao analisar uma linha de comando em C ...
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
++ * ... -> irá encaminhar o ponteiro argv em um caractere
... [0] -> seguirá o ponteiro ler o caractere
++ (...) -> 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 !!!
fonte