Binarne kopiowanie plików, Argumenty wiersza poleceń w "C"

0

Jako zadanie na końcu rozdziału z książki S.Prata mam

Napisz program kopiujacy pliki ,który pobiera nazwępliku żródłowego i docelowego z wiersza poleceń. Skorzystaj ze standardowego wejścia/wyjścia i trybu binarnego.

Mój kod to:

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>;


int main(int argc, char * argv[])
{
	long count,end;
	FILE *in, *out;
	int ch;

	if( argc != 3)
	{
		fprintf(stderr,"Nie poprawana liczba argumentow");
		Sleep(2000);
		exit(1);
		
	}
    
	if (   (in = fopen(argv[1],"rb") ) != NULL )
	{
		fprintf(stderr,"Blad otwarcia pliku %s", argv[1]);
		Sleep(2000);
		exit(2);
	}

	if (   (out = fopen(argv[1],"wb") ) != NULL )
	{
		fprintf(stderr,"Blad otwarcia strumienia zapisu do %s", argv[2]);
		Sleep(2000);
		exit(3);

	}

	fseek(in,0L,SEEK_END);
	end = ftell(in);

	for(count = 0L ; count < end ; count++)
	{
		fseek(in,count,SEEK_SET);
		ch = getc(in);
		fputc(ch,out);
	}  

	return 0;
} 

Wywołuje pogram za pomocą wiersza poleceń(próbowałem przed nazwami plików(argumentów) dać ścieżkę))

start C:\program.exe pierwszy.txt drugi.txt

Jednak program wywala mi błąd odczytu. Czy mógłby mi ktoś powiedzieć co jest błędne w kodzie ewentualnie może źle uruchamiam program.

Dziękuje za Waszą pomoc i pozdrawiam.

0

fseek(in,count,SEEK_SET);
ch = getc(in);
fputc(ch,out);

aaarrggh.... toż to będzie wieki trwało, nawet jeśli byłoby bez błędu.

1
viruss3000 napisał(a)
	if (   (in = fopen(argv[1],"rb") ) != NULL )
	if (   (out = fopen(argv[1],"wb") ) != NULL )

Tak się kończy robienie copy & paste :)

1

Ma znaczenie na ile pakietów podzielę dane wejściowe

Ma ogromne znaczenie, bo w ten sposób dla pliku 1 GB będziesz robił miliard razy (z hakiem) fseek/getc/fputc.

0

Dziękuje za Waszą pomoc ale program niestety nadal nie kopiuje zawartości pliku ;(

Kod po porwakach to:

 #include<stdio.h>
#include<stdlib.h>
#include<windows.h>;


int main(int argc, char * argv[])
{
	long count,end;
	FILE *in, *out;
	int ch;

	if( argc != 3)
	{
		fprintf(stderr,"Nie poprawana liczba argumentow");
		Sleep(2000);
		exit(1);
		
	}
    
	if (   (in = fopen(argv[1],"rb") ) == NULL )
	{
		fprintf(stderr,"Blad otwarcia pliku %s", argv[1]);
		Sleep(2000);
		exit(2);
	}

	if (   (out = fopen(argv[2],"wb") ) == NULL )
	{
		fprintf(stderr,"Blad otwarcia strumienia zapisu do %s", argv[2]);
		Sleep(2000);
		exit(3);

	}

	fread(out,sizeof(in), 1 , in);
	fclose(in);
	fclose(out);

	printf("Kopiowanie zakonczone");
	Sleep(2000);

	





	  

	return 0;
}

Próbowałem też tak i dalej nic

 while(bytes = fread(temp,sizeof(char), ROZMIAR_BUF,in) > 0 )
		fwrite(temp,sizeof(char), bytes, out);
0

sizeof(in) zwraca rozmiar wskaźnika (4 w programie 32-bitowym) a nie rozmiar pliku na dysku.

0

Witam,

Mam ten sam problem, stoję w tym samym miejscu co twórca tego wątku. Problem nie jest rozwiązany, ma ktoś jeszcze jakieś sugestie do tego ćwiczenia? Mi nie kopiuje plików tylko zawiesza się program po włączeniu.

Mam coś takiego:

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

int main(int argc, char * argv[])
{
    long licznik, koniec;
    FILE * fwsk1, * fwsk2;
    long * c;
    if(argc!=3)
        {
            printf("Za malo argumentow!");
            getch();
            exit(1);
        }

    if((fwsk1=fopen(argv[1], "rb"))==NULL || (fwsk2=fopen(argv[2], "wb"))==NULL)
        {
            printf("Blad w odczycie plikow!");
            getch();
            exit(2);
        }
    fseek(fwsk1, 0L, SEEK_END);
    koniec=ftell(fwsk1);
    for(licznik=0; licznik<koniec; licznik++)
        {
            fseek(fwsk1, licznik, SEEK_SET);
            fseek(fwsk2, licznik, SEEK_SET);
            fread(c, sizeof(long), 1, fwsk1);
            fwrite(c, sizeof(long), 1, fwsk2);
        }
    fclose(fwsk1);
    fclose(fwsk2);
    getch();
    return 0;      
}
1
Luke2 napisał(a):

Mam ten sam problem, stoję w tym samym miejscu co twórca tego wątku. Problem nie jest rozwiązany, ma ktoś jeszcze jakieś sugestie do tego ćwiczenia? Mi nie kopiuje plików tylko zawiesza się program po włączeniu.

A czytałeś odpowiedzi w tym wątku?

Luke2 napisał(a):
    if(argc!=3)
        {
            printf("Za malo argumentow!");
            getch();
            exit(1);
        }

Nie ma czegoś takiego jak getch() w używanych przez Ciebie bibliotekach. Nie przepisuj na pałę z innych programów.
getch() jest w curses, których i tak tu nie potrzebujesz.
W stdio jest getchar(), ale po co Ci on tutaj?
Dlaczego exit() zamiast return?

Luke2 napisał(a):
    if((fwsk1=fopen(argv[1], "rb"))==NULL || (fwsk2=fopen(argv[2], "wb"))==NULL)
        {
            printf("Blad w odczycie plikow!");
            getch();
            exit(2);
        }

Fajnie... A teraz postaw się w sytuacji użytkownika. Dostajesz komunikat Blad w odczycie plikow i kombinuj teraz którego pliku nie można odczytać i dlaczego.
BTW, to jest raczej błąd otwarcia.
Znasz znaczenie flagi "b" w argumencie określającym tryb otwarcia? Wiesz co ona robi (a raczej czego nie robi) w Linuksie?

Luke2 napisał(a):
    fseek(fwsk1, 0L, SEEK_END);
    koniec=ftell(fwsk1);

Użyj stat() to określenia rozmiaru pliku. Kiedy chcesz sprawdzić ile kilometrów jest z Gdańska do Krakowa to też wskakujesz w samochód i jedziesz, czy patrzysz na mapę/Google/cokolwiek?

Luke2 napisał(a):
    for(licznik=0; licznik<koniec; licznik++)
        {
            fseek(fwsk1, licznik, SEEK_SET);
            fseek(fwsk2, licznik, SEEK_SET);
            fread(c, sizeof(long), 1, fwsk1);
            fwrite(c, sizeof(long), 1, fwsk2);
        }

Źle. fread() i fwrite() jako pierwszy argument przyjmują wskaźnik zawierający adres do zaalokowanego miejsca w pamięci, z/do którego będą odczytywać/zapisywać. Ty przekazujesz wskaźnik wskazujący "w kosmos". long *c nie zaalokuje Ci magicznie miejsca w pamięci pod c. Zaalokuje jedynie miejsce pod *c.
I co z tym odczytywaniem po długości long? Tryb binarny nie oznacza, że masz użyć typów liczbowych. fread() i fwrite() nie interesuje jaki typ danych jest pod przekazanym wskaźnikiem. Je obchodzi tylko adres do bloku pamięci i rozmiar tego bloku.
Zaalokuj bufor o rozmiarze np. 4kB i użyj jego.
Nie sprawdzasz czy odczyt/zapis się udał. A co jeśli np. podczas kopiowania zabraknie miejsca w systemie plików?
No i ten fseek()... Skąd Ty to wziąłeś? fread() i fwrite() przesuwają kursor pliku w miarę odczytu/zapisu. Nie musisz go ręcznie popychać. Nie wspominając już o tym, że robisz to źle (long ma więcej bajtów niż 1).

Luke2 napisał(a):
    fclose(fwsk1);
    fclose(fwsk2);
    getch();
    return 0;      
}

I znowu ten getch(). Dobrze, że chociaż tym razem dałeś return.

Ten kod wygląda na składany z kilku innych, ze szczyptą własnej, szalonej inwencji twórczej. To bardzo dobrze, jednakże warto także poczytać dokumentację wykorzystywanych funkcji zanim zacznie się ich używać. Mniej okazji do pytania innych dlaczego coś nie działa.

0

Dziękuję za obszerną wypowiedź, daje mi dużo do myślenia i dużo zapału do jeszcze cięższej pracy. Używałem exit() zamiast return, tak mnie nauczyły przykłady z książki (i tu się pewnie kłania to, że złą książkę czytam, bo nie jest nią "Język ANSI C"). getch() ktoś raz polecał zamiast getchar() i użyłem jej po to, aby ktoś przeczytał komunikat o błędzie zanim zniknie, chociaż te komunikaty nie są kompletne jak mówiłeś. Nie pisałem tego programiku pod innych userów tylko pod siebie, proszę o wyrozumiałość. Użyłem trybu binarnego w Windowsie, nie w Linuxie co miało znaczenie gdyż chciałem przekopiować plik z wartościami binarnymi. Funkcji stat() nie znałem do tej pory i wyszukałem koniec pliku na swój sposób. Dalej to też tragedia, źle zrozumiałem fread() i fwrite(). Przepraszam za kłopot, po prostu brnę do przodu za szybko z tymi rozdziałami i się potykam. Dzięki jeszcze raz za odpowiedź i pozdrawiam serdecznie.

2
Luke2 napisał(a):

Dziękuję za obszerną wypowiedź, daje mi dużo do myślenia i dużo zapału do jeszcze cięższej pracy.

Gooood, gooood... [zacieranie rąk z szyderczym uśmieszkiem na ustach]

Luke2 napisał(a):

Używałem exit() zamiast return, tak mnie nauczyły przykłady z książki (i tu się pewnie kłania to, że złą książkę czytam, bo nie jest nią "Język ANSI C").

Tę funkcję stosuje się raczej do zakończenia działania programu z funkcji innej niż main. W pozostałym przypadku jej użycie jest uznawane za nieeleganckie. To tak, jakbyś wyskakiwał z wody zamiast z niej wyjść na brzeg... like a sir.

Luke2 napisał(a):

getch() ktoś raz polecał zamiast getchar() i użyłem jej po to, aby ktoś przeczytał komunikat o błędzie zanim zniknie,

Takie "wstrzymywanie terminala" robi się w przypadku stosowania IDE, które np. prezentuje wynik działania w oknie zamykanym automatycznie po zakończeniu działania programu. Teraz chyba tylko Dev ma taki "ficzer". Normalnie nie powinno się czegoś takiego robić, szczególnie w programach nieinteraktywnych.

Luke2 napisał(a):

chociaż te komunikaty nie są kompletne jak mówiłeś. Nie pisałem tego programiku pod innych userów tylko pod siebie, proszę o wyrozumiałość.

Po pierwsze, wyrabiaj sobie nawyki. Po drugie, Ty także jesteś użytkownikiem tego programu. Jeśli wystąpiłby ten błąd, to Ty musiałbyś kombinować którego pliku nie udało się otworzyć i dlaczego.

Luke2 napisał(a):

Użyłem trybu binarnego w Windowsie, nie w Linuxie co miało znaczenie gdyż chciałem przekopiować plik z wartościami binarnymi.

No widzisz, napisałeś że masz tę samą sytuację co autor wątku, ale nie poinformowałeś nas, że dotyczy ona innego systemu operacyjnego. W przypadku Windows użycie flagi "b" jest prawidłowe. Jednakże, robi ona co innego niż myślisz. Tryb tekstowy nie oznacza, że będziesz czytał tekst, a tryb binarny - liczby. Odczyt i zapis jest tego samego - bajtów. Nie ma znaczenia czy w danych są wartości z "dolnej" tablicy ASCII czy z całej. Tryb tekstowy pod Windows zamienia automatycznie znaki LF (line feed, 0x0A, '\n') na CR LF (carriage return i line feed, 0x0D 0x0A, "\r\n"), gdyż w tym systemie nową linię oznacza się kombinacją dwóch znaków. Tryb binarny takiej translacji przy odczycie/zapisie nie robi.

Luke2 napisał(a):

Funkcji stat() nie znałem do tej pory i wyszukałem koniec pliku na swój sposób.

Szalona inwencja twórcza jest i tak lepsza od braku inwencji jakiejkolwiek. Próbowałeś, zostałeś poprawiony, wzbogaciłeś się o nową wiedzę. Ciesz się.

Luke2 napisał(a):

Dalej to też tragedia, źle zrozumiałem fread() i fwrite().

Pytanie tylko czy zrozumiałeś co zrobiłeś źle. Inną kwestią jest czy wiesz jak to zrobić dobrze. Poczytaj, popróbuj, a jak nic nie wykombinujesz - pytaj.

Luke2 napisał(a):

Przepraszam za kłopot, po prostu brnę do przodu za szybko z tymi rozdziałami i się potykam.

Nie masz za co przepraszać zbytnio. I nie ucz się języka C "na szybko". Wymaga on skupienia i zrozumienia jak coś działa i dlaczego. Bez tego będzie tylko trudniej i więcej błędów trudnych do wykrycia. Przykładem jest chociażby ten Twój long *c, który wynika z braku wiedzy o wskaźnikach (lub wiedzy niekompletnej).

Na zachętę podpowiem Ci, że nie musisz znać rozmiaru pliku kopiowanego, a tym samym używać pętli for (aczkolwiek pętla będzie konieczna). Zobacz w dokumentacji co i w jakich przypadkach zwraca funkcja fread().

0

Pomyliłem się. Sposób wyznaczania końca pliku jest zerżnięty z książki (tylko zapomniałem, że to z książki). W książce użyto pewnie takiego sposobu dlatego, że funkcja stat() ma argument typu jeszcze nie przerabianego w książce. Tak wygląda definicja stat() :

int stat(char *filename,struct old_stat *statbuf)

Typ struct nie był jeszcze przerabiany i może dlatego nie użyto tej funkcji.

Co do Twojego posta to po prostu profesjonalizm, którego zazdroszczę.

1

No tak, ale jak już napisałem, wiedza o rozmiarze kopiowanego pliku jest zbyteczna. Chyba, że robisz jakiś progress bar lub chcesz najpierw sprawdzić czy nowy plik się zmieści w miejscu docelowym.
Jeśli książka każe Ci sprawdzać rozmiar pliku, a potem w pętli for jeszcze to zliczać, to ja bym proponował zmienić książkę.

EDIT: sprawdzanie czy nowy plik się zmieści w miejscu docelowym jest bez sensu, o ile nie piszesz programu pod DOS. Nie wpadaj na pomysł żeby coś takiego implementować.

0

W książce w tym przykładzie gdzie był wykrywany koniec pliku chodziło o wypisywanie znaków z pliku od końca do początku, więc chyba wszystko jest ok z książką. Problem był jedynie ze mną, myślałem, że będzie to potrzebne ale nie jest.

1

Tu nie ma większej różnicy. Może wyjaśnię inaczej. Do wykonania jakiejś czynności używasz mechanizmu, który informuje Cię o osiągnięciu stanu granicznego. Wykrywanie wcześniej tego stanu jest niepotrzebną redundancją, tym bardziej że wiąże się z kosztownymi (mającymi długie opóźnienie) operacjami we/wy.
Demonstrowanie wypisywania zawartości pliku przez odczyt znak po znaku jest uczeniem złych nawyków. Taki sposób jest mało wydajny i można odnieść błędne wrażenie, że wydajność jest mało znacząca. Wypisywanie znak po znaku można przecież pokazać na zwykłej tablicy w pamięci, nawet wypełnianej zawartością pliku (co przy okazji zademonstrowałoby czym jest bufor).
Moim zdaniem ta książka jest zła. Uczy złych nawyków i zawiera przykłady, które bardziej szkodzą niż uczą. Miej to na uwadze podczas pochłaniania wiedzy z niej.

0

Ok, pochwale się, zobaczymy czy na 100% dobry jest ten program. Jednak udało mi się zwalczyć zawiłości w Ubuntu i w nim ma działać program. Wpisuję dwa parametry "Liczby.txt" "Liczby_Kopia.txt" i u mnie kopiuje liczby byle jak poukładane z pierwszego pliku do drugiego.

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

int main(int argc, char * argv[])
{
    FILE * f_in, * f_out;
    int C;
    if(argc!=3)
        {
            printf("Za malo lub za duzo argumentow.");
            return 0;
        }
    if((f_out=fopen(argv[1], "r"))==NULL)
        {
            printf("Blad w otwieraniu pliku %s.", argv[1]);
            return 0;
        }
    if((f_in=fopen(argv[2],"w"))==NULL)
        {
            printf("Blad w otwieraniu pliku %s.", argv[2]);
            return 0;
        }
    while(fread(&C, sizeof(int), 1, f_out)==1)
        fwrite(&C, sizeof(int), 1, f_in);
    if(fclose(f_out)!=0)
        {
            printf("Blad w zamykaniu pliku %s.", argv[1]);
            return 0;
        }
    if(fclose(f_in)!=0)
        {
            printf("Blad w zamykaniu pliku %s.", argv[2]);
            return 0;
        }
    return 0;      
}

Pozdrawiam.

1
Luke2 napisał(a):

Ok, pochwale się, zobaczymy czy na 100% dobry jest ten program.

No niestety, wciąż nie jest do końca dobrze.
Po pierwsze, fread() nie musi Ci zwrócić tylu bajtów, ile oczekujesz. Powinieneś zapisywać tylko tyle, ile odczytałeś. I tu pojawia się problem z zastosowanym przez Ciebie rozwiązaniem (przy którym się upierasz, nie wiadomo czemu). Najmniejszą porcją danych, jaką możesz zapisać, są cztery bajty. Oznacza to, że jeśli odczytasz mniej, to nie możesz zapisać tylko "połowy inta". Dlatego przy kopiowaniu plików odczytuje się i zapisuje do charów, dzięki czemu najmniejszą porcją danych jest jeden bajt.
Po drugie, wciąż czytasz po 4 bajty per fread(). To marnotrawstwo. Użyj bufora (chyba już o tym pisałem).
Po trzecie, nie obsługujesz błędów odczytu/zapisu. Dodałeś za to obsługę błędów zamknięcia pliku, które są mało istotne i często się ich nie obsługuje.

Poniżej masz najprostsze rozwiązanie z buforem (chociaż w tym przypadku jestem raczej zwolennikiem użycia read() i write())

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

#define BUFSIZE     4096    /* bytes */



int  main(int  argc, const char  *argv[])
{
    size_t      bread, bwritten = 0;
    FILE        *src, *dst;
    char        buf[BUFSIZE];
    int         errcode = 0;


    if ( argc != 3 ) {
        fprintf(stderr, "usage:  fcopy SRC DST\n");
        return 1;
    };

    if ( (src = fopen(argv[1], "rb")) == NULL ) {
        fprintf(stderr, "Error opening file \"%s\" for reading: %s\n", argv[1], strerror(errno));
        return 2;
    };

    if ( (dst = fopen(argv[2], "wb")) == NULL ) {
        fprintf(stderr, "Error opening file \"%s\" for writing: %s\n", argv[2], strerror(errno));
        return 3;
    };

    do {
        bread = fread(buf, sizeof(char), BUFSIZE, src);
        if ( bread )
            bwritten = fwrite(buf, sizeof(char), bread, dst);
    } while ( (bread == BUFSIZE) && (bwritten == bread) );

    if ( ferror(src) || ferror(dst) ) {
        fprintf(stderr, "Error %s file: %s\n", bwritten == bread ? "reading" : "writing", strerror(errno));
        errcode = 4;
    };

    fclose(src);
    fclose(dst);

    return errcode;
}

EDIT: Autokorekta. fread() zawsza zwróci albo tyle danych ile oczekujesz, albo 0. Nie zmienia to jednak zbytnio sytuacji. Jeśli na wejściu będzie mniej danych niż wielokrotność 4 bajtów, ostatnie bajty nie zostaną zapisane na wyjściu.

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