Jak otworzyć plik z polskimi literami w ścieżce?

0
 
procedure TForm1.OtworzPlik;
var
  Sciezka: String;
  Plik: TextFile;
  Linia: String;
begin
  Sciezka := 'Łukasz/info.txt';
  AssignFile(Plik, Sciezka);
  Reset(Plik);
  while not EOF(Plik) do
    begin
      readln(Plik, Linia);
      ShowMessage(Linia);
    end;
  CloseFile(Plik);
end;

Podany kod nie znajduje pliku przez umieszczenie polskiego znaku w ścieżce. W przypadku zamienienia Sciezka := 'Łukasz/info.txt'; na Sciezka := 'Lukasz/info.txt'; wszystko działa prawidłowo. Zapewne da się to jakoś ominąć. Takich folderów z polskimi literami mam kilkadziesiąt wiec nie bardzo chce mi się zmieniać nazwy, szczególnie, że musiałbym dodać jeszcze funkcje usuwającą polskie litery w przypadku podawania ścieżki przez użytkownika. Druga sprawa że w kodzie mam kilka takich miejsc gdzie podobny błąd może wystąpić. Program pisałem na szybko, byle tylko działał i jakakolwiek modyfikacja kody jest w tym momencie bardzo trudna, a sam program jest mi bardzo potrzebny i to jak najszybciej. Dlatego proszę o jakąś poradę jak to naprawić.

1
sciezka:=UTF8ToAnsi(sciezka) 

Pozdrawiam

0

Plus sprawdź kodowanie samego pliku źródłowego.

0

Z samym plikiem problemów nie ma. Chodzi tylko o nazwę folderu. Plik otworzyć mogę, gdy nie ma polskich liter w ścieżce.

0

Nie potrafię sobie poradzić z tym kodowaniem. Mam dwa pliki. Oba mają kodowanie ANSI. W pierwszym zapisuje się wszystko bez problemów. W drugim... nie. Chciałem wklejać tam tekst z Edita. Próbowałem używać ANSIToUTF8 i UTF8ToANSI, ale za każdym razem wyświetla się inaczej, ale zawsze nie tak jak powinno.

Mój błąd. W pierwszym pliku wcześniej miałem kodowanie UTF-8, dlatego wszystko się zapisywało prawidłowo.

Spróbowałem zmienić kodowanie plików na UTF-8. Zapisuje się wtedy prawidłowo. Ale problem jest z odczytem. W pierwszej linii się coś dodaje i program odczytuje ją inaczej niż jest zapisana.

0

@dani17 - ładowanie i zapisywanie danych z i do plików nijak ma się do problemu z otwieraniem plików...

W pierwszym zapisuje się wszystko bez problemów.

Pokaż jakiś (aktualny) kod, żeby było wiadomo co robisz;

W drugim... nie

Też pokaż kod;

Chciałem wklejać tam tekst z Edita.

Komponenty i całe LCL domyślnie korzysta z UTF-8, więc jeśli plik ma być kodowany w ANSI - łańcuchy musisz konwertować;

Próbowałem używać ANSIToUTF8 i UTF8ToANSI, ale za każdym razem wyświetla się inaczej, ale zawsze nie tak jak powinno.

No to jakieś cuda muszą się u Ciebie dziać... Jeśli ładujesz dane z pliku, to nie konwertuj ich na ANSI, bo one są już kodowane w ANSI; Natomiast jeśli tekst pobierasz z komponentów - konwertuj na ANSI; A najlepiej to nie baw się i wszędzie używaj UTF-8 - przecież to nie problem i nijak nie wpływa na poprawność danych;


A tak przy okazji - szkoda, że biblioteka standardowa FPC nie posiada jakiegoś strumienia, który pozwoliłby ładować pojedyncze linie z pliku tekstowego do pamięci; Albo ja o niej jeszcze nie dowiedziałem się; Wtedy można by spokojnie używać strony kodowej UTF-8, bez kombinacji z konwersjami; Samo napisanie takiej klasy było by dość proste i nawet mógłbym się tego podjąć, jednak czasu jak zwykle za mało...

A tak to albo trzeba korzystać ze starej konstrukcji, albo ładować wszystko do pamięci (używając TStringList).

0

Jeśli chodzi o kodowanie to ustawiłem wszystko na UTF-8. Jakiś czas temu już nie pamiętam dlaczego potrzebowałem kodowania ANSI, ale nie wiem czy to nie było jeszcze kiedy zaczynałem od zwykłego Pascala i konsoli. Później tak mi zostało że wszystko robiłem na ANSI.

Nie mam problemów z otwieraniem tylko właśnie z kodowaniem. Przez złe kodowanie program nie może znaleźć plików i przez to ich otworzyć.

Pokazać jakiś kod? Może nie jest to napisane najlepiej, ale to tak na szybko tylko.

 
procedure TForm1.Odczyt;
var
  Plik: TextFile;
  Linie: Array of String;
  Ilosc: Integer;
  I: Integer;
  K: Integer;
begin
  Ilosc := 0;
  AssignFile(Plik, 'info.txt');
  Reset(Plik);
  While not EOF(Plik) do
    begin
      Inc(Ilosc);
      SetLength(Linie, (Ilosc + 1));
      ReadLn(Plik, Linie[Ilosc]);
    end;
  CloseFile(Plik);
  For I := 1 to Ilosc do
    If Linie[I] = '[start]' then
      begin
        K := I;
        Repeat
          Inc(K);
          SzukajParametry;
        Until (K = Ilosc) or (Linie[K] = '[start]');
      end;
end;

Tak na prawdę problem pojawia się w If Linie[I] = '[start]' then. Znak strukturę pliku. W pierwszej linijce jest [start], jednak program w przypadku gdy I = 0 nie widzi tego. Tak jakby ta pierwsza linijka zawierała coś innego. O ile w tym przypadku mogę to zabezpieczyć w taki sposób If (Linie[I] = '[start]') or (I = 1) then to w przypadku gdy nie będę wiedział jaką strukturę ma plik może się to okazać dużym problemem.

2

Po pierwsze - masz wyciek pamięci, bo otwierasz plik przez Reset, ale nigdzie nie zamykasz go za pomocą CloseFile; Poza tą metodą nie masz już dostępu do zmiennej lokalnej Plik, więc nie możesz zwolnić uchwytu - stąd wyciek pamięci;

Po drugie - łańcuch [start] zarówno w kodowaniu ASCII, ANSI, jak i UTF-8 zawsze będzie opisany na siedmiu bajtach, więc nawet jeśli wczytasz z pliku linię kodowaną w ANSI oraz przyrównasz ją do literału (kodowanego w UTF-8) - porównanie zwróci True;

Po trzecie - jeżeli operujesz na łańcuchach kodowanych w UTF-8, to dobrą praktyką jest użycie typu UTF8String (jest to alias na typ AnsiString); Mniej będziesz miał później problemów;

Po czwarte - skoro i tak ładujesz całą zawartość do pliku, to skorzystaj z klasy TStringList; Po pierwsze mniej kodu trzeba będzie, a po drugie - znalezienie linijki z wartością [start] to jedno wywołanie metody IndexOf, która oczywiście zwróci indeks linii, w której ta wartość jest zapisana; A jeśli nie znajdzie, to zwróci -1;

Podsumowując - łatwiej by było, gdybyś napisał co chcesz osiągnąć, czyli co ma kod wykonać.

0
furious programming napisał(a):

Po pierwsze - masz wyciek pamięci, bo otwierasz plik przez Reset, ale nigdzie nie zamykasz go za pomocą CloseFile; Poza tą metodą nie masz już dostępu do zmiennej lokalnej Plik, więc nie możesz zwolnić uchwytu - stąd wyciek pamięci;

CloseFile mam w kodzie. Tak banalnego błędu bym nie popełnił. Przez przypadek tylko nie skopiowałem tej linijki.

Po drugie - łańcuch [start] zarówno w kodowaniu ASCII, ANSI, jak i UTF-8 zawsze będzie opisany na siedmiu bajtach, więc nawet jeśli wczytasz z pliku linię kodowaną w ANSI oraz przyrównasz ją do literału (kodowanego w UTF-8) - porównanie zwróci True;

Tu jest problem tylko z pierwszą linijką. Jeśli [start] będzie w innej niż pierwsza wszystko działa prawidłowo. Jeśli wyświetlę tą pierwszą linijkę bez zmiany kodowania to wyświetla się prawidłowo, ale porównując ją w ten sposób: If Linia[1] = "[start]" to zwraca False. Teraz tak myśląc nad tym, zastanawiam się czy wina nie może być po stronie samego pliku? Może zmiana kodowania coś takiego spowodowała?

Po trzecie - jeżeli operujesz na łańcuchach kodowanych w UTF-8, to dobrą praktyką jest użycie typu UTF8String (jest to alias na typ String); Mniej będziesz miał później problemów;

A jakie mógłbym mieć później problemy? Dużo to zmienia?

Po czwarte - skoro i tak ładujesz całą zawartość do pliku, to skorzystaj z klasy TStringList; Po pierwsze mniej kodu trzeba będzie, a po drugie - znalezienie linijki z wartością [start] to jedno wywołanie metody IndexOf, która oczywiście zwróci indeks linii, w której ta wartość jest zapisana; A jeśli nie znajdzie, to zwróci -1;

Podsumowując - łatwiej by było, gdybyś napisał co chcesz osiągnąć, czyli co ma kod wykonać.

Wydaje mi się, że nie mógłbym tego użyć, w taki sposób, aby potrzebne było mniej kodu. Struktura pliku jest taka

 
[start]
id = numer
nazwa atrybutu = wartosc
nazwa 2 atrybutu = wartosc
...
 
[start]
id = numer
nazwa atrybutu = wartosc
nazwa 2 atrybutu = wartosc
...
 
[start]
id = numer
nazwa atrybutu = wartosc
nazwa 2 atrybutu = wartosc
...

Gdy program znajdzie linijkę [start] to uruchamiana jest procedura, która w zakresie od [start] do następnego [start] wyszukuje odpowiednich parametrów i przypisuje do tablicy zdefiniowanego przeze mnie typu. Przy czym może się zdarzyć, że pierwsza "sekcja" będzie miała 10 atrybutów, a następna tylko np. 2.

2

Problem ze [start] w pierwszej linijce może polegać na tym że plik ma kodowanie UTF-8 z BOM i wtedy przed [start] występują inne znaki (tzw. bom) http://pl.wikipedia.org/wiki/BOM_%28informatyka%29 zmień kodowanie pliku albo w inny sposób się przed tym zabezpiecz.

0

Kodowania już teraz nie będę zmieniał bo wszystko pozostałe działa prawidłowo. A jeśli chodzi o zabezpieczenie to w jaki sposób jest to możliwe? O ile w tym przypadku znam strukturę pliku to w jeśli będę pisał program który będzie używał pliku, którego zawartości pierwszej linii nie będę znał to jak ją właściwie odczytać?

2

Choćby po CloseFile:

  if (Ilosc > 0) and (Copy(Linie[1], 1, 3) = #$EF#$BB#$BF) then //czy BOM
    Delete(Linie[1], 1, 3);

Ten kod zwyczajnie sprawdza czy 1 linia zawiera bom jeżeli tak to go usuwa i masz "czystą" linię w tablicy.

2
dani17 napisał(a)

CloseFile mam w kodzie. Tak banalnego błędu bym nie popełnił. Przez przypadek tylko nie skopiowałem tej linijki.

Moje niedopatrzenie - przywykłem do kodu, w którym poszczególne jego logiczne "sekcje" oddzielone są pustymi liniami, stąd czasem nie zauważam niektórych instrukcji; Co i tak nie zmienia faktu, że bezpieczniejszą konstrukcją jest poniższa:

var
  tfInput: TextFile;
begin
  AssignFile(tfInput, 'info.txt');
  {$IOCHECKS OFF}
  Reset(tfInput);
  {$IOCHECKS ON}

  if IOResult() = 0 then
  try
    { ładowanie zawartości pliku do pamięci }
  finally
    CloseFile(tfInput);
  end;
end;

Po pierwsze ukrywasz domyślną obsługę błędów wejścia/wyjścia, a po drugie zapewniasz zamknięcie pliku, które w przypadku zaistnienia wyjątku podczas ładowania zawartości pliku może zostać pominięte;

Tu jest problem tylko z pierwszą linijką.

W takim razie na pewno plik posiada sygnaturę BOM; Podobny "problem" miałem przy przystosowywaniu kodu mojej biblioteki do TreeStructInfo, bo parser walił wyjątkiem i nie pokapowałem się dlaczego; W każdym razie @kAzek pokazał Ci jak się tej sygnatury pozbyć;

Zainteresuj się jakimś sensownym edytorem (polecam Notepad++), w którym łatwo wykryjesz stronę kodową zawartości pliku, a po drugie łatwo będziesz ją mógł zmienić (opcje Convert to XXX);

A jakie mógłbym mieć później problemy? Dużo to zmienia?

W zależności od użytych dyrektyw kompilatora, typ String może być tożsamy z typem ShortString, AnsiString lub UnicodeString; Więcej na ten temat możesz wyczytać tutaj;

Wydaje mi się, że nie mógłbym tego użyć, w taki sposób, aby potrzebne było mniej kodu.

Możesz, np. tak:

var
  slInput: TStringList;
begin
  slInput := TStringList.Create();
  try
    slInput.LoadFromFile('info.txt');
    { obróbka danych }
  finally
    slInput.Free();
  end;
end;

Ładowaniem zajmują się wewnętrzne mechanizmy listy; Dodatkowo, klasa służy także jako kontener na dane, więc masz dwie rzeczy w jednym; Sam ręcznie nie musisz wszystkiego robić - klasa zrobi to za Ciebie, a dodatkowo oferuje wiele przydatnych metod, np. wspomniane wcześniej IndexOf;

Gdy program znajdzie linijkę [start] to uruchamiana jest procedura, która w zakresie od [start] do następnego [start] wyszukuje odpowiednich parametrów i przypisuje do tablicy zdefiniowanego przeze mnie typu. Przy czym może się zdarzyć, że pierwsza "sekcja" będzie miała 10 atrybutów, a następna tylko np. 2.

Składnia pokazanego przez Ciebie pliku zawiera się w specyfice składni plików INI, więc może zainteresuj się klasą do obsługi plików tego formatu - TIniFile.

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