Próbuję napisać program, który może porównywać dwa pliki wiersz po wierszu, słowo po słowie lub znak po znaku w C. Musi być w stanie czytać w opcjach wiersza poleceń -l -w -i or --
...
- jeśli opcją jest -l, porównuje pliki linia po linii.
- jeśli opcją jest -w, porównuje pliki słowo po słowie.
- jeśli opcje są - automatycznie zakłada, że następny argument jest pierwszą nazwą pliku.
- jeśli opcją jest -i, porównuje je bez uwzględniania wielkości liter.
- domyślnie porównuje pliki znak po znaku.
Nie powinno mieć znaczenia, ile razy opcje są wprowadzane, o ile -w i -l nie są wprowadzane w tym samym czasie i nie ma więcej lub mniej niż 2 plików.
Nie wiem nawet, od czego zacząć analizowanie argumentów wiersza poleceń. PROSZĘ POMÓŻ :(
Więc to jest kod, który wymyśliłem na wszystko. Nie sprawdziłem jeszcze błędów, ale zastanawiałem się, czy piszę w zbyt skomplikowany sposób?
/*
* 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
user1251020
źródło
źródło
Odpowiedzi:
O ile mi wiadomo, trzy najpopularniejsze sposoby analizowania argumentów wiersza poleceń w C to:
#include <unistd.h>
z biblioteki POSIX C), który może rozwiązywać proste zadania przetwarzania argumentów . Jeśli jesteś trochę zaznajomiony z bash, to wbudowane getopt w bash jest oparte na Getopt z GNU libc.#include <argp.h>
z biblioteki GNU C), który może rozwiązywać bardziej złożone zadania i zajmuje się takimi rzeczami, jak na przykład:-?
,--help
aby uzyskać pomoc , w tym adres e-mail-V
,--version
aby uzyskać informacje o wersji--usage
do wiadomości użytkowaniaDokumentacja biblioteki GNU C zawiera kilka fajnych przykładów dla Getopt i Argp.
Przykład użycia Getopt
Przykład użycia Argp
Przykład robienia tego samemu
Zastrzeżenie: Jestem nowy w firmie Argp, przykład może zawierać błędy.
źródło
Użyj
getopt()
, a możegetopt_long()
.Zwróć uwagę, że musisz określić, które nagłówki mają być uwzględnione (robię to 4, które są wymagane), a sposób, w jaki napisałem
op_mode
typ, oznacza, że masz problem z funkcjąprocess()
- nie możesz uzyskać dostępu do wyliczenia tam na dole. Najlepiej jest przenieść wyliczenie poza funkcję; możesz nawet utworzyćop_mode
zmienną o zasięgu plikowym bez powiązania zewnętrznego (fantazyjny sposób powiedzeniastatic
), aby uniknąć przekazywania jej do funkcji. Ten kod nie-
jest synonimem standardowego wejścia, innym ćwiczeniem dla czytelnika. Zauważ, żegetopt()
automatycznie dba o--
zaznaczenie końca opcji za Ciebie.Nie uruchomiłem żadnej wersji powyższego wpisywania poza kompilatorem; mogą być w nim błędy.
Aby uzyskać dodatkowe punkty, napisz funkcję (biblioteczną):
który hermetyzuje logikę przetwarzania opcji nazw plików po
getopt()
pętli. Powinien obsługiwać-
jako standardowe wejście. Zauważ, że użycie tego wskazywałoby, żeop_mode
powinna to być zmienna o zasięgu pliku statycznego.filter()
Funkcja przyjmujeargc
,argv
,optind
a wskaźnik do funkcji przetwarzania. Powinien zwrócić 0 (EXIT_SUCCESS), jeśli był w stanie otworzyć wszystkie pliki i wszystkie wywołania funkcji zgłoszone 0, w przeciwnym razie 1 (lub EXIT_FAILURE). Posiadanie takiej funkcji upraszcza pisanie programów filtrujących w stylu uniksowym, które czytają pliki określone w linii poleceń lub na standardowym wejściu.źródło
getopt()
nie; GNUgetopt()
robi to domyślnie. Wybieraj. Nie interesują mnie opcje po zachowaniu nazw plików, głównie dlatego, że nie są one niezawodne na różnych platformach.Zauważyłem, że Gengetopt jest całkiem przydatny - określasz żądane opcje w prostym pliku konfiguracyjnym, a on generuje parę .c / .h, którą po prostu dołączasz i łączysz z aplikacją. Wygenerowany kod korzysta z getopt_long, wydaje się obsługiwać większość typowych parametrów wiersza poleceń i może zaoszczędzić dużo czasu.
Plik wejściowy gengetopt może wyglądać mniej więcej tak:
Generowanie kodu jest łatwe i wypluwa
cmdline.h
icmdline.c
:Wygenerowany kod można łatwo zintegrować:
Jeśli potrzebujesz wykonać dodatkowe sprawdzenie (na przykład upewnienie się, że flagi wykluczają się wzajemnie), możesz to dość łatwo zrobić z danymi przechowywanymi w
gengetopt_args_info
strukturze.źródło
#include
, a nie w samym wygenerowanym pliku. dla mnie wyłączenie ostrzeżeń jest verboten :-)Jestem bardzo zaskoczony, że nikt nie wspomniał o pakiecie „opt” Jamesa Theilera.
Możesz znaleźć opt na http://public.lanl.gov/jt/Software/
i pochlebny post z kilkoma przykładami tego, jak to jest o wiele prostsze niż inne podejścia:
http://www.decompile.com/not_invented_here/opt/
źródło
Docopt ma implementację C, która moim zdaniem była całkiem niezła: https://github.com/docopt/docopt.c
Ze standardowego formatu strony podręcznika opisującego opcje wiersza poleceń docopt wnioskuje i tworzy parser argumentów. Zaczęło się to w Pythonie; wersja dla Pythona dosłownie po prostu analizuje docstring i zwraca dict. Aby to zrobić w C, wymaga trochę więcej pracy, ale jest czysty w użyciu i nie ma żadnych zewnętrznych zależności.
źródło
Istnieje wielka biblioteka libUCW ogólnego przeznaczenia, która zawiera zgrabną analizę opcji wiersza poleceń i ładowanie pliku konfiguracyjnego .
Biblioteka jest również dostarczana z dobrą dokumentacją i zawiera kilka innych przydatnych rzeczy (szybkie IO, struktury danych, alokatory, ...), ale można ich używać osobno.
Przykładowy parser opcji libUCW (z dokumentacji biblioteki)
źródło
Napisałem niewielką bibliotekę, która analizuje argumenty podobne do POpt, z którą miałem kilka problemów, o nazwie XOpt . Używa analizy argumentów w stylu GNU i ma bardzo podobny interfejs do POpt.
Używam go od czasu do czasu z dużym powodzeniem i działa praktycznie wszędzie.
źródło
Jeśli mogę, tootując swój własny klakson, chciałbym również zasugerować przyjrzenie się bibliotece opcji parsującej, którą napisałem: dropt .
Jedną z funkcji, którą oferuje, a której wielu innych nie ma, jest możliwość zastąpienia wcześniejszych opcji. Na przykład, jeśli masz alias powłoki:
i chcesz używać,
bar
ale z--flag1
wyłączoną funkcją, umożliwia:źródło
źródło
getopt()
lubgetopt_long()
.Szablon instrukcji do analizowania argumentów wiersza poleceń w C.
C:> nazwa programu -w - plikOne.txt plikTwo.txt
źródło
_Bool
i nagłówek,<stdbool.h>
który definiujebool
jako_Bool
itrue
ifalse
oraz__bool_true_false_are_defined
wszystkie makra (które wyjątkowo mogą być niezdefiniowane i przedefiniowane bez wywoływania niezdefiniowanego zachowania; ta licencja jest jednak oznaczona jako „przestarzała”). Jeśli więc masz kompilator C99, możesz użyć<stdbool.h>
ibool
. Jeśli nie, albo napiszesz go dla siebie (nie jest to trudne), albo użyjesz natywnego odpowiednika.źródło
Okay to początek długiej historii - zrobiłem krótko 'bort parsowanie wiersza poleceń w C ...
Zauważ, że ta wersja obsługuje również łączenie argumentów: Więc zamiast pisać / h / s -> / hs również będzie działać.
Przepraszam, że jestem n-tą osobą, która publikuje tutaj - jednak nie byłem zadowolony ze wszystkich wersji samodzielnych, które tu widziałem. Cóż, te lib są całkiem fajne. Więc wolałbym parser opcji libUCW , Arg lub Getopt niż te wykonane w domu.
Uwaga, możesz zmienić:
*++argv[i]
->(++argv*)[0]
już mniej tajemniczy, ale wciąż tajemniczy.Dobra, podzielmy to: 1. argv [i] -> uzyskaj dostęp do i-tego elementu w polu wskaźnika argv-char
++ * ... -> przekaże wskaźnik argv o jeden znak
... [0] -> będzie podążać za wskaźnikiem, odczytując znak
++ (...) -> nawias jest dostępny, więc zwiększymy wskaźnik, a nie samą wartość znaku.
Tak fajnie, że w C ## wskaźniki „umarły” - niech żyją wskaźniki !!!
źródło