Uruchamianie programu z argumentami początkowymi.

Odpowiedz Nowy wątek
2017-11-12 18:53
0

Witam,
mam do napisania program w C, który w zależności od wybranej opcji (wpisanej jako parametr) ma wykonywać inną rzecz. Program ma mieć 4 opcje oznaczone jako "-i" (zamiana stdin z plikiem podanym jako kolejny argument, czyli rozumiem, że ma otwierać dany plik, pobierać znaki i wyświetlać je na ekran), "-o" (tak samo jak -i, tylko zamiast pobierać znaki, wpisujemy coś z klawiatury i zapisujemy do podanego w argumencie pliku), "-O" (podobnie jak -o ale tutaj dopisujemy, zamiast nadpisywać), "-q" (ignorowanie komunikatów o błędach). Czyli wywołanie programu w konsoli to przykładowo ./nazwa_programu -i plik1 -o plik2, albo np. ./nazwa_programu -i -O plik. No i udało mi się napisać coś takiego:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main(int argc, char *argv[]){
    char s;
    FILE *plik;
    int i=1;
    printf("%d\n",argc);
    while(i<argc){
        if(strcmp(argv[i],"-i")==0){
            plik = fopen(argv[i+1],"r");
            while(fscanf(plik,"%c",&s)!=EOF){
                printf("%c",s);
            }
        fclose(plik);
        }
        if(strcmp(argv[i],"-o")==0){
            plik = fopen(argv[i+1],"w");
            while(s!='\n'){
                scanf("%c",&s);
                fprintf(plik,"%c",s);   
            }
        fclose(plik);
        }
        if(strcmp(argv[i],"-O")==0){
            plik = fopen(argv[i+1],"a");
            while(s!='\n'){
                scanf("%c",&s);
                fprintf(plik,"%c",s);
            }
        fclose(plik);
        }
        i+=2;
    }
    if(argc==1){
        printf("Wczytaj znaki: ");
        while(1){
            scanf("%c",&s);
            printf("%c",s);
        }
    }   
    return 0;
}

no i generalnie każda z trzech opcji (-q jeszcze nie dodawałem) działa poprawnie, ale wyłącznie jeśli uruchamiamy program tylko z jedną opcją, czyli np. ./nazwa_programu -i plik, ale jak już chce dołączyć dowolną kolejną opcję to już nie działa tak jak trzeba. Jeśli np. uruchomię ./nazwa_programu -i plik1 -O plik1, to program pobierze z pliku plik1 znaki, wypisze je na ekran, ale już nie wykona drugiej opcji, czyli nie nadpisze potem do pliku plik1, tzn. w ogóle nie można potem już nic pisać z klawiatury w konsoli. I teraz moje pytanie brzmi, jak to można zmodyfikować, albo w ogóle napisać żeby można było te wszystkie opcje łączyć (wykonywać jedna po drugiej)? Poza tym sposób jaki zastosowałem w tym kodzie nie jest zbyt uniwersalny, bo na sztywno przyjąłem, że podczas wywołania zawsze będzie jakaś opcja i potem nazwa pliku, a więc dla indeksów nieparzystych mam te opcje, a dla parzystych pliki (w sensie w tablicy argv[]), ale jeśli miałbym np. wywołanie postaci ./nazwa_programu -o -i plik, to wtedy ten sposób nie zadziała, bo tam gdzie jest "-i" będzie chciał otwierać plik (to jest argv[2] - czyli indeks parzysty).
Także podsumowując przede wszystkim jak poprawić ten kod, aby móc wykonywać wszystkie podane opcje po sobie oraz ewentualnie jak zmodyfikować, aby ten program był bardziej uniwersalny? Dziękuję za wszelkie wskazówki

edytowany 1x, ostatnio: Waszok, 2017-11-12 18:58

Pozostało 580 znaków

2017-11-12 19:20
0

Przede wszystkim dlaczego:

i+=2;

W ten zaczynając od drugiego elementu (czyli argumentu po nazwie) od razu przeskakujesz do czwartego, którego możliwe nawet, że nie podałeś... Spróbuj inkrementować licznik i++;

A co do uniwersalności... jest wiele sposobów, ale przede wszystkim myśl o programie jak o kolejnych etapach/elementach/cegłówkach/klockach... Zrób funkcję pobierającą kolejny argument, funkcję sprawdzającą liczbę argumentów, ich formę, zrób funkcje parsujące odpowiednie argumenty, a może i uniwersalną dla wszystkich? Może jakiś switch() case w środku?

Pozostało 580 znaków

2017-11-12 19:31
0
i+=2;

dlatego, że tak jak napisałem na sztywno przyjąłem, że najpierw jest opcja, potem plik i tak na przemian, więc jakbym inkrementował o 1, to wpisując ./nazwa_programu -i -o plik, to argv[1]="-i", czyli w odpowiednim ifie miałbym

plik = fopen(argv[i+1],"r")

a w tym przypadku i+1=2, a więc próbowałbym otworzyć "-o", bo argv[2]="-o" - od razu dostałbym błąd.
O zastosowaniu swich() case myślałem, ale nie wiem jak go użyć w tym przypadku.
Wiesz dlaczego ten kod nie działa tak jak trzeba, tzn. dlaczego nie mogę uruchomić programu z więcej niż jedną opcją?

edytowany 2x, ostatnio: Waszok, 2017-11-12 19:33

Pozostało 580 znaków

2017-11-12 19:37
0

Zacznij od napisania tej aplikacji tak, aby nie było tam kopiuj-wklej.


Pozostało 580 znaków

2017-11-12 19:46
0

No dobra, to od początku:

  • Co otrzymujesz, gdy uruchamiasz ten program z większą ilością parametrów? Program kończy się normalnie? Przestaje działać?
  • Czy udaje Ci się otworzyć podany plik?
  • Dlaczego zmienna s nie jest zainicjalizowana jakąś wartością? To może powodować problemy! Nadaj jej przynajmniej wartość zero...

Pozostało 580 znaków

2017-11-12 20:04
0

No więc, tak jak pisałem, jeśli jest jedna opcja to wszystko działa ok.
Jeśli natomiast uruchomię to następująco: ./nazwa_programu -i plik -o plik, to pierwsza opcja się wykonuje tzn. otwiera dany plik sczytuje znaki i wypisuje na ekran, potem chyba otwiera plik ponownie (aby wykonać opcję -o) ale nie mam możliwości nic wpisać z klawiatury, po prostu program kończy działanie, a wynikiem jest to co sczytało w opcji -i. A domyślam się, że program otwiera plik dla opcji -o, bo wszystko co w nim do tej pory było zostaje usunięte (po wykonaniu programu plik jest pusty), czyli tak jakby program właśnie otworzył plik, przygotował go odpowiednio, ale ponieważ nic nie mogę podać z klawiatury to plik ostatecznie jest pusty.
Z pozostałymi opcjami jest podobnie, czyli zwykle jedna (niekoniecznie pierwsza) się wykonuje, a pozostałe nie lub nie do końca, ale zawsze program się kończy bez żadnych błędów.

Zainicjowanie zmiennej s nic nie pomogło.

edytowany 2x, ostatnio: Waszok, 2017-11-12 20:13

Pozostało 580 znaków

2017-11-12 20:22
0

Nadanie początkowej wartości zmiennej s nic nie pomaga, ustawiłem:

char s = 0;

i wszystko jest bez zmian

Pozostało 580 znaków

2017-11-12 20:24
1

Problem polega na tym, że Twoja zmienna s jest dostępna dla wszystkich opcji, znajduje się na samej górze głównej funkcji.
Natomiast ten warunek:

            while( s != '\n' ){
                scanf( "%c", &s );
                fprintf( plik, "%c", s );
            }

Skutecznie uniemożliwia Ci wpisanie znaków, ponieważ wciąż przechowuje znak z pliku...
Rozwiązaniem jest umieszczenie zmiennej w każdym z tych procesów/opcji lub przynajmniej utworzenie jej w kolejnej iteracji pętli głównej.

Pomogę... Coś takiego:

    while( i<argc ){
        char s = 0;
        if( strcmp( argv[ i ], "-i" ) == 0 ){
            plik = fopen( argv[ i + 1 ], "r" );
            while( fscanf( plik, "%c", &s ) != EOF ){
                printf( "%c", s );
            }
            fclose( plik );
        }
/* Tutaj dalszy Twój kod... */

może rozwiązać problem.
(nie zapomnij też dodać zmiennej s dla:

    if( argc == 1 ){
        char s = 0;
        printf( "Wczytaj znaki: " );
        while( 1 ){
            scanf( "%c", &s );
            printf( "%c", s );
        }
    }
edytowany 1x, ostatnio: Bartosz36, 2017-11-12 20:29

Pozostało 580 znaków

2017-11-12 20:40
0

Ok, tak właśnie zrobiłem i teraz działa! Dziękuję.
Teraz jednak warto zmodyfikować kod, tak aby na sztywno nie mieć określonej tej reguły wpisywania opcja/plik opcja/plik itd, tylko właśnie aby móc inkrementować i o 1 i w dowolnej formie podawać te argumenty, czyli zamiast np. ./nazwa_pliku -i plik -O plik móc podać ./nazwa_pliku -i -O plik.
Poza tym fajnie jakby "\n" nie kończyło wpisywania, ale żeby móc je kończyć jakoś inaczej, np. jakimś innym przyciskiem, typu "ESC", bo jeśli chciałbym wpisać do pliku kilka linii tekstu, to potrzebuję "enter" a w tym momencie po wciśnięciu "enter" kończy się całe wpisywanie z klawiatury ...
.

Pozostało 580 znaków

2017-11-12 21:30
0

Możesz też użyć getopt, tu masz więcej info i przykład http://man7.org/linux/man-pages/man3/getopt.3.html

Pozostało 580 znaków

2017-11-12 21:44
0

@Waszok: mała porada. stdin traktuj jak plik. Dzięki temu piszesz:

FILE* infile;
if (from_stdin) {
  infile = stdin;
} else {
  infile = fopen(file, "r");
}

if (infile == NULL) error("Cannot open file", quiet);

FILE* outfile;
if (to_stdout) {
  outfile = stdout;
} else {
  outfile = fopen(file, append ? "a" : "w");
}

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0