Parser pliku txt

0

Mam dany plik w następującym formacie:

-KSBLOZ-------NazwaTowaru---------------------------------CenaTrans-VAT-
 116590111009 Vit. A kaps. 12,000 j.m. 50 szt.                1.55   2
 116590311057 Vit. A plyn 50.000 j.m./1ml 10 ml               1.00   2
 119530111009 Vit. A+D3 (2000j.m.A+1000j.m kaps. 50 szt.      1.55   2
 119530311057 Vit. A+D3 solutio aquosa krople 10ml            1.00   2

Moim zadaniem jest stworzenie zestawu klas do operacji na nim, ale za cholerę nie wiem jak się do tego zabrać.

Ilość kolumn może być większa, przedstawiony plik prezentuje jedynie format. Nazwy kolumn i ich ilość będą się zmieniać. To co udało mi się zrobić to opracować algorytm do zliczania ilości kolumn w pliku oraz ich długości (ilość znaków w nazwie oraz liczba "-" determinują max ilość znaków dla poszczególnej kolumny).

Jedyne na co wpadłem to zapisanie tych informacji w tablicy, co jednak nie jest perfekcyjne gdyż do momentu zliczenia kolumn nie wiem jak duża potrzebna mi jest tablica. Może jest coś odpowiadającego ArrayList z javy?

Nie oczekuje gotowego rozwiązania ale potrzebuje jakichś wskazówek odnośnie wykonania tego zadania, to dla mnie bardzo ważne.

Pozdrawiam

1

Jedyne na co wpadłem to zapisanie tych informacji w tablicy, co jednak nie jest perfekcyjne gdyż do momentu zliczenia kolumn nie wiem jak duża potrzebna mi jest tablica. Może jest coś odpowiadającego ArrayList z javy?

var
a:array of array of ansistring;
i,j:integer;

setlength(a,2);
setlength(a[0],2);
setlength(a[1],2);
for i:=0 to length(a)-1 do for j:=0 to length(a[i])-1 do a[i,j]:='cos';

A co do reszty - co to za problem użyć Pos, copy, i paru innych funkcji?

0

setlength(a,2); //tu deklarujesz długość tablicy a do 2 elementów

setlength(a[0],2); //co się dzieje w tych punktach? Pod a[0] znajduje się kolejna 2-elementowa tablica?
setlength(a[1],2);

1

hint: własną klasę dziedziczącą po TStringList (albo i nie dziedziczącą ale mającą zmienną prywatną tego typu) i automagicznie (czyli zaszyte w kodzie klasy) rozpoznającą i dzielącą pojedynczą linijkę tekstu zapakować we własną klasę dziedziczącą po TObjectList (moduł contnrs) z metodami do odczytu pliku, wyciągania nazw kolumn oraz długości poszczególnych pól i dodawania poszczególnych linii do klasy dziedziczącej po TStringList. Jak to dobrze rozplanujesz to dodanie nowego szablonu pliku nie będzie wymagało od Ciebie ani jednego znaku kodu do napisania.
Tablica dynamiczna tablicą dynamiczną ale po co sobie utrudniać życie jak TStringList daje Ci wygodne operowanie na liście stringów

0

@Misiekd fajnie Cie znowu czytać :P

podążałem za Twoimi wskazówkami odnośnie TStringList. Wkleję fragmencik kodu:

  begin    
    AssignFile(TF, 'G:\\test.txt');
    Reset(TF);

    Readln(TF, S);
    names:=TStringList.Create;
    names.Delimiter := '-';
    //names.QuoteChar := '-';
    names.DelimitedText := S;

    for i := 0 to names.Count - 1 do showmessage(names[i]);

  end;

Wziąłem na początek pod uwagę jedynie pierwszą linię pliku.
To jednak wyświetla mi między dobrze pobranymi nazwami nagłówka puste komunikaty. Podejrzewam, że to puste miejsca pomiędzy kolejnymi znakami -
Może jest sposób żeby się ich pozbyć?


EDIT:
Ależ ze mnie bęcwał. Wystarczyło dodać do pętli warunek:

    for i := 0 to names.Count - 1 do
      begin
        if names[i]<>'' then showmessage(names[i]);
      end;

Z drugiej strony może jest "higieniczniejsze" rozwiązanie, które nie zapisywałoby mi pustych pól do StringListy? (oczywiście w pętli moge przerzucic już do nowej stringlisty tylko istotne pola)

0

Niemal bym skończył gdyby nie jeden zasadniczy problem:

-KSBLOZ-------NazwaTowaru---------------------------------CenaTrans-VAT-
 116590111009 Vit. A kaps. 12,000 j.m. 50 szt.                1.55   2
 116590311057 Vit. A plyn 50.000 j.m./1ml 10 ml               1.00   2
 119530111009 Vit. A+D3 (2000j.m.A+1000j.m kaps. 50 szt.      1.55   2
 119530311057 Vit. A+D3 solutio aquosa krople 10ml            1.00   2

Przy pobieraniu rekordów poniższy kod się nie sprawdza:

            values:=TStringList.Create;
            values.Delimiter := ' ';
            values.DelimitedText := S;

Jak widzicie w nazwie towaru występują spację, które powodują rozdzielanie danych do różnych pozycji na liście. Jakieś obejście?

1

ale to nie tak! Najpierw musisz wyciągnąć z pierwszej linii takie dane jak
nazwa kolumny chociaż jak Ci to nie potrzebne to możesz olać
długość kolumny
i tak dla wszystkich kolumn
Wyciągnięcie liczby kolumn i długości każdej powinieneś mieć w klasie głównej.

Metoda ParsujPlik (czy jak sobie ją nazwiesz) w klasie głównej powinna wyglądać mniej więcej tak

function TKlasaGlowna.ParsujPlik(Plik: string): Boolean
var
  sl: TStringList;
begin
  sl.LoadFromFile(Plik);
  WyodrebnijKolumny(sl[0]);
  for i := 1 to sl.Count - 1 do
    TKlasaRekord.Create(FKolumny, sl[i]); //FKolumny to coś (np. tablica dynamiczna) z opisem kolumn, który powstaje w metodzie WyodrebnijKolumny
end;

//FPola to np. TStringList z listą wartości w poszczególnych polach
constructor TKlasaRekord.Create(AKolumny: TKolumny; l: string)
begin
  for i := 0 to Length(AKolumny) - 1 do
  begin
    FPola.Add(Copy(l, 1, AKolumny[i].Dlugosc_kolumny));
    Delete(l, 1A, Kolumny[i].Dlugosc_kolumny);
  end;
end;

Sama klasa TStringList nie wyciągnie za ciebie danych z poszczególnych linii i nie porozbija ich - tego nie da się załatwić delimiterem ponieważ w danych źródłowych taki nie istnieje.
Z drugiej strony jak zrobisz to tak jak proponuję to jeśli dojdzie Ci w pliku nowa kolumna, zmieni się ilość znaków w kolumnie czy cokolwiek innego to nie będzie to miało znaczenia - jeśli nagłówek (czyli pierwsza linia) będzie poprawny w stosunku do reszty danych to zawsze zadziała Ci to prawidłowo. Także wyświetlanie czy obróbka sparsowanych danych nie będzie problemem

0

Trochę się pogubiłem

Rozumiem, że ma być:

Delete(l, 1, AKolumny[i].Dlugosc_kolumny);

Jeśli chodzi o ta linijkę:

FPola.Add(Copy(l, 1, AKolumny[i].Dlugosc_kolumny));

Dlugosc_kolumny to funkcja zadeklarowana w klasie TKolumny?

0

Najpierw musisz wyciągnąć z pierwszej linii takie dane jak
nazwa kolumny chociaż jak Ci to nie potrzebne to możesz olać
długość kolumny

/FKolumny to coś (np. tablica dynamiczna) z opisem kolumn, który powstaje w metodzie WyodrebnijKolumny

0

/FKolumny to coś (np. tablica dynamiczna) z opisem kolumn, który powstaje w metodzie WyodrebnijKolumny

Jak przekazać tablice jako wynik funkcji? tzn z tego co się orientuje jest to niemożliwe.

1

dokładnie tak samo jak każdą inną zmienną

type
  TJakasTablica = array of cokolwiek;

function JakasSUperZajebistaFunkcja: TJakasTablica
begin
  SetLength(Result, 10);
  Result[0] := Cokolwiek;
end;
0

@Misiekd Robie to powoli, trochę po swojemu, ze względu na ograniczony zasób wiedzy, ale bez Twoich wskazówek bym z miejsca nie ruszył. udało mi się napisać funkcje do pobierania długości kolumn (niezaleznie od ilości kolumn) oraz ich nazwy. Obecnie pracuje nad przeniesieniem pól do dwuwymiarowej tablicy niezdefiniowanej.
Używam takiego oto polecenia, które nie działa ;)

DArray[0, i] := Copy(S, cs, columns.getColumnsLength(i));

Przy czym tablica jest zdefiniowana jako:

DArray: array of array of string; 

Zmienne w momencie wykonywania polecenia zwracają kolejno:
S = "116590111000 Vit. A kaps. 12,000 j.m. 50 szt. 10.000"
cs = 0
columns.getColumnsLength(i) = 13
i = 0

Sama funkcja Copy(S, cs, columns.getColumnsLength(i)) zwraca poprawnie wycinek = "116590111000"

Oczekiwałem zatem umieszczenia na pozycji DArray[0,0] wartosci "116590111000" a wywala mi program

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