Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

c - Program do przekształacania pliku

adam220 01 Wrz 2016 17:47 1326 13
  • #1 01 Wrz 2016 17:47
    adam220
    Poziom 14  

    Witam,
    potrzebuję napisać w C program który czyta plik A.txt
    a zapisuje B.sql i C.sql.

    Wejściowy plik A.txt ma format ze stałą szerokościa pól, powiedzmy 2,3,3 znaki rozdzielone średnikiem:

    11;aa1;bb1
    22;aa2;bb2
    33;aa3;bb3

    Pierwszy plik wyjściowy B.sql ma mieć postać:

    11,aa1
    22,aa2
    33,aa3

    Wyjściowy plik C.sql poodbnie ale inne pole


    11,bb1
    22,bb2
    33,bb3

    Z jakiej funkcji czytającej skorzystać? fscanf fread ?
    z jakiej zapisującej?
    poproszę jakiś szkic operacji czytania i zapisu?

    0 13
  • Pomocny post
    #2 01 Wrz 2016 19:06
    Laurefinwe
    Poziom 6  

    Jeśli nie ma być prosto do bazy, to fscanf i fprintf.
    Do operowania na tekście standardowo string.h, chyba że preferujesz jakąś inną.
    Zaraz postaram się mniej więcej to sklecić.

    http://stackoverflow.com/questions/20342559/how-to-cut-part-of-a-string-in-c#20346241
    https://pl.wikibooks.org/wiki/C/scanf
    https://pl.wikibooks.org/wiki/C/printf
    http://en.cppreference.com/w/c/io/fopen

    Powinno pomóc.

    0
  • #3 01 Wrz 2016 19:09
    adam220
    Poziom 14  

    Uprościłem przykład pozbywając się elementów zaciemniających moje pytanie.
    @Laurefinwe będę wdzięczny za pomoc...teraz muszę wyjść ale jak wróce za 2h to się zgłoszę

    0
  • #5 01 Wrz 2016 22:20
    Krzysztof Gustaw
    Poziom 23  

    Witam!
    Moja pierwsza myśl - bufor pamięci, i funkcje fscanf i fprintf.

    Najpierw zarezerwuj sobie tablicę o np 10 elementach typu char np:

    char buf[10];

    i teraz sekwencja:

    funkcją:

    1 fscanf(plik_A.txt, "%s;", buf) czytasz 1 element do buf

    2 fprintf(plik_B.sql, "%s,", buf);
    3 fprintf(plik_C.sql, "%s,", buf);
    4 fscanf(plik_A.txt, "%s;", buf);
    5 fprintf(plik_B.sql, "%s,", buf);
    6 fscanf(plik_.A.txt, "%s;", buf);
    7 fprintf(plik_C.sql, "%s\n", buf);
    sprawdź czy koniec pliku i jeśli nie, to przejdź do pkt 1.

    Po wszystkim nie zapomnij zamknąć plików :)

    To oczywiście jedna z możliwości.
    Pozdrawiam
    KG

    0
  • Pomocny post
    #6 02 Wrz 2016 13:03
    Krzysztof Gustaw
    Poziom 23  

    Można jeszcze zrobić bez użycia tablic czy tych... hmmm... stringów i wykorzystać jedynie podstawowe funkcje czy makra czytające/piszące z/do pliku, np tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #7 02 Wrz 2016 16:02
    adam220
    Poziom 14  

    @Krzysztof Gustaw dzięki za kod. Działa bardzo dobrze ale nie do końca rozumiem jak...
    Co oznacza linia

    fputc(',', control ++ == 0 ? f2 : f3);

    Dodano po 53 [minuty]:

    Kod @Krzysztof Gustaw jest skuteczny, operuje na pojedynczych znakach.
    A jak to zrobić wiedząc że szerokość pól jest stała i czytając stringi o odpowiedniej szerokości.
    Coś mi nie wychodzi czy można prosić o kawałek działającego kodu odczytu scanf i zapisu printf całych wycinków?
    nie do końca rozumiem co sie wczyta do buf gdy wykonam
    fscanf(plik_A.txt, "%s;", buf);
    ile znaków się wczyta?
    U mnie cos nie działa, jakieś krzaczki widzę...

    0
  • Pomocny post
    #8 02 Wrz 2016 18:45
    Krzysztof Gustaw
    Poziom 23  

    Witam!
    Zapis:
    fputc(',', control ++ == 0 ? f2 : f3)
    oznacza, do pliku zostanie wpisany znak , (przecinek) Do którego pliku? Otóż wykorzystano tutaj operator warunkowy "?". podobny do instrukcji "if". Interpretuje się go tak:
    wyrażenie logiczne ? wartość1 : wartość2
    Jeśli warunek wyrażenie logiczne jest spełniony, wówczas wynik stanowi wartość1, w przeciwnym wypadku wartość2.
    Generalnie wykonanie tego wiersza: jeśli control == 0 to zapisz do pliku f2 a jeśli różne od zera to do pliku f3.
    W wyrażeniu logicznym użyto zapis: control ++ == 0 co oznacza, że działanie będzie następujące:
    Sprawdź, czy zmienna control jest równa zero, po sprawdzeniu zwiększ jej wartość o 1.
    Przy wczytaniu pierwszego pola wartość zmiennej control wynosi 0 co oznacza, że to pole będzie zapisane w obu plikach (f2 i f3). Następnie odczytywane jest drugie pole. Zmienna control nadal ma wartość 0. Teraz trzeba wpisać przecinek oddzielający pola. Najpierw będzie on wpisany do pliku f2. Zrobi to ta właśnie linia:
    putc(',', control ++ == 0 ? f2 : f3), przy czym wartość zmiennej control zostanie zwiększona o 1 co spowoduje, że warunek przestanie być spełniony i następny przecinek zostanie wpisany do pliku f3. Moją intencją było pokazanie tej właśnie możliwości.
    Można to załatwić instrukcją if else ale to rozwiązanie, moim zdaniem, jest bardziej eleganckie.

    Co do fscanf - ów, pierwotnie zakładałem, że średnik w formacie stanowić będzie ogranicznik pola... jednak zapomniałem, że w przypadku tekstu to nie zadziała (no cóż, jak pies się spieszył to ślepych narobił)

    W funkcji rodziny fscanf (i w fprintf) liczba umieszczona pomiędzy '%' a specyfikatorem danej stanowi rozmiar pola.
    A to wersja (poprawiona) z fprintfami i fscanfami o znanym formacie i rozmiarze pól:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    w funkcji fscanf w formatce zapis z gwiazdką np %*c oznacza, że pole (tu: pojedynczy znak ASCII) ma być pominięte. Tutaj w ten sposób pozbywamy się znaku ';'

    0
  • #9 05 Wrz 2016 13:42
    adam220
    Poziom 14  

    @Krzysztof Gustaw
    dzięki za obszerne wyjaśnienie.
    Mam jeszcze takie pytania:
    1. fscanf(f1, "%2s%*c", buf)!= EOF) /* czytaj pierwsze pole, przeskocz jeden znak (';') i badaj czy jest koniec pliku
    Rozumiem że %2s powoduje wczytanie 2 znaków do buf. Jak mozna porównać string o długości 2 z wartością EOF która ma długość 1? Widzę że to działa, ale jak? Czy EOF musi być na 1 pozycji? Przeprowadziłem próbę i chyba tak.

    2. Odczytujesz "%2s%*c" co znaczy że czytasz 2 znaki i ignorujesz trzeci.
    -Czy kolejny fscanf zacznie od czwartego znaku?
    -Jaki powinien być zapis aby zignorować inną liczbę znaków np 3?
    -Co oznacza ta gwiazdka w ogólności?

    3. Podczas czytania fscanf podajesz liczbę znaków jaką wczytać do buf.
    Ale podczas zapisu fprintf już nie podajesz liczby znaków.
    - Czy zasadniczo oznacza to że zostaną zapisane wszystkie jakie są w buf?
    - czy przy zapisie fprintf można podać liczbę znaków, np mniejszą niż zawiera buf i zostaną obcięte?

    Nie zadawałbym tych pytań, tylko sam spradził, ale widze że z tym trzeba ostrożnie bo poprawnie skompilowany program wywala się gdy manipuluję długością stringów...

    Dodano po 58 [minuty]:

    No i jeszcze jedno pytanie:
    4. Jak skasować ostatni znak z tak powstałego pliku wyjściowego.
    Plik mam otwarty, ostatnia operacja zapisu postawiła kursor za ostatnim zapisanym znakiem więc próbowałem
    fseek(f2, -1, 2);
    fprintf(f2, 0)
    ale to nie działa...dlaczego?

    0
  • #10 05 Wrz 2016 13:43
    Krzysztof Gustaw
    Poziom 23  

    Witam ponownie!
    Spróbuję odpowiedzieć.

    Ad 1) Funkcja fscanf ogólnie działa następująco: przede wszystkim jako parametry podaje się WSKAŹNIKI do zmiennych w których mają być umieszczone odczytane dane. Jest to zupełnie coś innego od zwracania danych w "klasyczny" sposób. A w ten "klasyczny" sposób funkcja fscanf zwraca ilość przeczytanych znaków a w przypadku natrafienia na koniec pliku - EOF. Tak więc te dane przekazywane są różnymi drogami - dane odczytane poprzez wskaźniki do zmiennych, gdzie mają być umieszczone, natomiast czy odczytywanie się udało - jest zwracane bezpośrednio. Gdy powodzenie to zwraca liczbę danych przypisanych wskazanym zmiennym, a jeśli jest koniec pliku to zwraca EOF.

    Ad 2) -Czy kolejny fscanf zacznie od czwartego znaku? Odp: Tak.
    -Jaki powinien być zapis aby zignorować inną liczbę znaków np 3? Przyznam, że nie sprawdzałem ale chyba tak: "%*3c". Sprawdź to.
    -Co oznacza ta gwiazdka w ogólności? W funkcjach rodziny fscanf oznacza pominięcie (lipne przeczytanie) danej typu określonego w specyfikatorze np: c - dana typu znak, d - liczba całkowita, f - liczba zmiennoprzecinkowa itd.

    Ad 3) Tak, w fscanf jest podana liczba znaków którą funkcja ma przeczytać i umieścić w buforze. W specyfikatorze jest to określone jako %3s czyli dana ta jest traktowana jako ciąg znaków. No więc przeczyta te 3 znaki i umieści je w buforze dopisując na końcu znak '\0' oznaczający koniec tekstu. Teraz funkcja fprintf czyta ten ciąg znaków dopóki nie natrafi na znak '\0' co kończy odczytywanie tego tekstu. Tak więc zostanie wypisany cała zawartość buf oczywiście do znaku '\0'. Jeśli nawet po '\0' w buforze będą jakieś inne znaki, to nie zostaną wypisane. Jakby ich nie było.
    W przypadku funkcji rodziny fprintf można podać liczbę znaków. Tutaj masz pole do dobrej zabawy, bo np "%3s" oznacza, że wypisane będą min. 3 znaki. Jeśli do zapisu jest np 1 znak to pozostałe miejsca zostaną uzupełnione spacjami. Zapis "%.3" - że wypisane będą co najwyżej 3 znaki. Jeśli tekst zawiera więcej niż 3 znaki to nadmiarowe znaki zostaną obcięte. Zapis "%5.3s" oznacza, że w polu długości 5 zostaną wypisane co najwyżej 3 znaki dosunięte do prawej strony pola. Jeśli mają być dosunięte do lewej strony to liczbę 5 należy poprzedzić minusem.
    W przypadku liczby zmiennoprzecinkowej to zapis "%6.2f" mówi, że liczba ma być wypisana z dokładnością do 2 miejsc po przecinku zaś jej długość całkowita pola wynosi 6

    Nie zadawałbym tych pytań, tylko sam spradził, ale widze że z tym trzeba ostrożnie bo poprawnie skompilowany program wywala się gdy manipuluję długością stringów...
    Wic polega na tym, że wraz z z zadaną długością stringów trzeba też odpowiednio zwiększać długość tablicy buf - jeśli np długość stringu wynosi 5, to buf[6], ponieważ musi byc jeszcze miejsce na znak '\0' oznaczający koniec stringu. A najlepiej "z grubej rury": char buf[100] albo i lepiej :)

    Co do pytania w ostatniej chwili, jeśli ma być usunięty ostatni znak w pliku to trzeba plik skrócić o 1 czyli obciąc plik funkcją trunc która, niestety, jest nieprzenośna... - trzeba wydobyć deskryptor pliku a to już trzeba odwołać się do struktury FILE - przynajmniej tak jest u mnie...
    A jeśli użyć fprintf wraz z fseek to: fprintf(uchwyt_pliku, "%c", '\0');

    0
  • #11 05 Wrz 2016 14:10
    adam220
    Poziom 14  

    Dzięki @Krzysztof Gustaw
    Ad 1. yyyy, taaa, chyba rozumiem:) Wczytam się jeszcze raz. Ach te wskaźniki...
    Ad 2. Zapis "%*3c" działa, sprawdziłem. Czyli Zapis "%*c" oznacza to samo co "%*1c"
    Czy można uogólnić że kursor w pliku staje tam gdzie zakończyła się ostatnia operacja na tym pliku i stoi tam aż do zamknięcia pliku? Czyli że każda funkcja fscanf, fprintf zacznie czytać lub dopisywać tam gdzie skończyła pracę poprzednia funkcja?
    Ad 3. Wszystko jasne.
    Ad 4. A gdyby chcieć nadpisać ostatni znak w pliku (nie skracając pliku) innym znakiem, np spacją, to dlaczego zaproponowana przeze mnie kombinacja fseek + fprint nie działa?

    0
  • Pomocny post
    #12 05 Wrz 2016 16:23
    Krzysztof Gustaw
    Poziom 23  

    Ad 2) Tak, można (i tak jest). Co do odczytu/zapisu z/do pliku to każda funkcja fscanf czy fprintf dopisuje tam gdzie zakończyła operację poprzednia funkcja czytająca czy zapisująca. Dotyczy to również funkcyj fgetc, getc, fputc, putc, fgets, fputs i innych. Każda, powtarzam każda funkcja zapisująca do pliku czy to fprintf, czy fputc, czy fputs czy jakaś inna zapisująca modyfikuje kursor w pliku w taki sposób, że kolejny znak będzie dopisywany bezpośrednio tam gdzie zakończyła zapis ostatnia funkcja. To samo dotyczy czytania z pliku. Mało tego, funkcje np fputc, fputs czy fprintf, czy putc itd mogą się przeplatać a i tak nie naruszy to sekwencji zapisu do pliku. To samo z odczytem z tym, że jest jeszcze funkcja ungetc. Poco ona jest? często jest tak, że program nie wie że skompletował już dane dopóki nie przeczyta za dużo. Fajnie by było zatem, gdyby istniała możliwość jak gdyby "cofnięcia" tego ostatnio przeczytanego znaku z powrotem do pliku. W ten sposób przy następnym wywołaniu funkcji czytającej z pliku jako pierwszy odczytany byłby ten właśnie znak. Tej funkcji intensywnie używają funkcje rodziny fscanf. Ale uwaga - "cofnięcie" znaku niekoniecznie jest tożsame ze zmniejszeniem wskaźnika pozycji w pliku i bywa tak, że można "cofnąć" do pliku tylko jeden znak.

    Ad 4)
    A jaką sekwencję stosujesz:
    działającą (sprawdziłem u siebie), czyli:

    Kod: c
    Zaloguj się, aby zobaczyć kod
    czy (nie mającą prawa działać ze względu na "cuda" w funkcji fprintf):
    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #13 05 Wrz 2016 17:12
    adam220
    Poziom 14  

    Takich wyjaśnień było mi potrzeba, cierpliwie, od ogółu do szczegółu, szeroko i z dygresjami (takimi jak ungetc).
    Dziękuję jeszcze raz.

    0
  • #14 09 Wrz 2016 09:29
    adam220
    Poziom 14  

    Rozwiązanie bez kompilatorów, w 5 linijkach kodu DOSowego skryptu podrzucił na PW penknife:
    [...]podany problem można by rozwiązać za pomocą skryptu .bat:

    Kod: dos
    Zaloguj się, aby zobaczyć kod

    0
  Szukaj w 5mln produktów