Zapis pliku XML z kodowaniem UTF8

0

Pytanie może i trywialne, ale nie wiem jak to zrobić.

Chodzi o zapis wygenerowanych danych XML do pliku z kodowaniem UTF8.

Dodam, że chodzi o D7 i kiedyś coś takiego niby zrobiłem z wykorzystaniem jakiejś biblioteki wykorzystującej WideStringi, ale po niewczasie okazało się, że nie bardzo ma to to ochotę zapisywać w UTF8 - połączenie z BD, z której wyciągam dane na pewno jest w UTF8, tabele też maja takie kodowanie.

Potrzebuję pewnego i sprawdzonego sposobu.

0

jeśli stringi wyciągane z bazy są w kodowaniu UTF-8, to gdy je najnormalniej zapiszesz do pliku to będzie UTF-8. nie trzeba widestringów.

0
  1. mało które (na pewno nie standardowe) kontrolki BD w D7 potrafią przenieść utf-8
  2. aby w D7 można było pokazać coś w utf-8 potrzeba mieć kontrolki, które to potrafią (np. TNT unicode control)
  3. może daj jakiś kod jak to próbujesz robić bo tak to sobie można gdybać
0

Kurcze. Nie dam sobie łapy obciąć, ale kiedyś przedstawiciel Embarcadero w polsce mial serie szkoleń z Delphi X - m.in. o tworzeniu aplikacji z wykorzystaniem unicode. Padło tam takie zdanie, że w związku z tym , że stringi sa jednobajtowe - nie pozwalają na przechowywanie unikodów (unikody obcinane są do jednego bajta) i żeby poprawnie przetwarzać unikody trzeba właśnie używać WideStringów. Jak mówie - nie jestem aż tak mega zaawansowany, żeby rzeczowo dyskutować - używam Delphi do produkowania konkretnych efektów, a technikaliami interesuję się wtedy kiedy coś nie robi takich efektów jak trzeba.

Generalnie aplikacja nie pokazuje niczego konktretnego na ekranie - to znaczy nic, co potem byłoby przetwarzane - owszem kontrolnie siedzi tam DBGrid, ale tylko po to, żeby pokazać który rekord jest przetwarzany.

To co robi aplikacja, to wyciągnięcie (poprzez ZEOSa, ale w innych projektach mam MyDACa DevArta) danych z BD - ZQuery i od razu wrzucenie do odpowiednich węzłów XMLa przygotowanych XML Data Binderem (problemy z nim wydzieliłem do osobnego wątku)

Kod. hmmm ... wkleję to co ma jakies większe znaczenie. Jest sobie klasa:

TUnicodeFileStream = class(TFileStream)
  public
    constructor Create(const FileName: string);
    procedure WriteWStr(u: widestring);
    procedure WriteWStrLn(u: widestring);
  end;

(...)

implementation

constructor TUnicodeFileStream.Create(const FileName: string);
const CUnicodeNormal: WideChar = WideChar($FEFF);
begin
    if FileExists(filename) then begin
        inherited Create(filename,fmOpenWrite);
    end else begin
        inherited Create(filename,fmCreate);
    end;
    Write(CUnicodeNormal,sizeof(WideChar));
end;

procedure TUnicodeFileStream.WriteWStr(u: widestring);
begin
    if u<>'' then Write(u[1],Length(u)*SizeOf(WideChar));
end;

procedure TUnicodeFileStream.WriteWStrLn(u: widestring);
const
    CUnicodeCR: WideChar = WideChar($000D);
    CUnicodeLF: WideChar = WideChar($000A);
begin
    WriteWStr(u);
    Write(CUnicodeCR,sizeof(WideChar));
    Write(CUnicodeLF,sizeof(WideChar));
end;

 

Gdzies kiedyś walcząc właśnie z zapisem unikodów przy przetwarzaniu danych z BD (chodziło o migrację danych pomiędzy systemami BD i wymogiem było przejście przez plik CSV formatowany w wymagany przez klienta sposób i kodowany właśnie w UTF-8

Okej - wracajac do wątku. W klasie głownej TForm1 mam zadeklarowany:

TForm1 = class(TForm)
(...)
public
    (...)
    ufs : TUnicodeFileStream; 
    (...)
    procedure GenExp;
    (...)
end;

a kod odpowiadający za przetwarzanie pliku wygląda mniej wiecej tak:

 
procedure TForm1.GenExp;
var komunikat : IXMLKomunikatType;
    ok : Boolean;
    ZestSwd : IXMLZestawswiadczenType;
    PozRozl : IXMLPozycjarozlType;
begin
    Memo1.Lines.Clear;
    ProgressBar1.Min:=0;
    ProgressBar1.Max:=1;
    ProgressBar1.Position:=0;
    ProgressBar2.Min:=0;
    ProgressBar2.Max:=1;
    ProgressBar2.Position:=0;
    if Connection.Connected then begin
        Memo1.Lines.Add('Rozpoczęcie generowania eksportu');
        if SaveDialog1.Execute then begin
            ok:=False;
            Memo1.Lines.Add('Rozpoczęcie pobierania danych');
            ZestSwdQuery.SQL.Clear;
            ZestSwdQuery.SQL.Add('SELECT * ');
            ZestSwdQuery.SQL.Add('FROM import ');
            ZestSwdQuery.SQL.Add('GROUP BY ID_SWIADCZ_NAD ');
            ZestSwdQuery.SQL.Add('LIMIT 0,10');
            ZestSwdQuery.Active:=TRUE;
            if ZestSwdQuery.Active then begin
                ok:=TRUE;
                ProgressBar1.Max:=ZestSwdQuery.RecordCount-1;
                Memo1.Lines.Add('Pobieranie danych zakończone pomyślnie');
            end else begin
                Memo1.Lines.Add('Błąd pobierania danych');
            end;
//------------------------------------------------------------------------------
            if ok then begin
                Memo1.Lines.Add('Eksport danych');
                komunikat:=Newkomunikat;
                try
                    if FileExists(SaveDialog1.FileName) then begin
                        DeleteFile(SaveDialog1.FileName);
                    end;
                    Memo1.Lines.Add('Rozpoczęcie zapisu do pliku');
                    komunikat.XmlnsNFZ:='xxxxxxxxx';
                    komunikat.Typ:='xxxxxxxxx';
                    komunikat.Wersja:=5;
                    komunikat.WersjaNFZ:='xxxxxxxxx';
                    komunikat.Idodb:='xxxxxxxxx';
                    komunikat.Idnad:='xxxxxxxxx';
                    komunikat.Idinstnad:='xxxxxxxxx';
                    komunikat.Nrgen:=0;
                    komunikat.Czasgen:=DateToStr(Now())+'T'+TimeToStr(Now());
                    komunikat.Infoapliknad:='xxxxxxxxx';
                    komunikat.Infokontaktnad:='xxxxxxxxx';
                    komunikat.Swiadczeniodawca.Typidswd:='X';
                    komunikat.Swiadczeniodawca.Idswd:='xxxxxxxxx';
                    komunikat.Swiadczeniodawca.Idinst:='xxxxxxxxx';
                    ZestSwdQuery.First;
                    while not ZestSwdQuery.Eof do begin
                        ZestSwd:=komunikat.Zestawswiadczen.Add;
                        ZestSwd.Idzestswiad:=ZestSwdQuery.FieldByName('ID_SWIADCZ_NAD').AsInteger;
                        ZestSwd.Nrwersji:=ZestSwdQuery.FieldByName('NR_WERSJI_NAD').AsInteger;
                        ZestSwd.Nrwersjirozl:=ZestSwdQuery.FieldByName('NR_WERSJI_NAD').AsInteger;
                        ZestSwd.Typ:=ZestSwdQuery.FieldByName('TYP_SWIADCZENIA').AsString;
                        ZestSwd.Usun:='N';
                        ZestSwd.Momwprow:=StringReplace(ZestSwdQuery.FieldByName('DT_UTWORZENIA').AsString,' ','T',[]);
                        ZestSwd.Mommodyf:=StringReplace(ZestSwdQuery.FieldByName('DT_MODYFIKACJI').AsString,' ','T',[]);
//------------------------------------------------------------------------------
                        ZestSwd.Danezestawu.Charakter:=ZestSwdQuery.FieldByName('CHARAKTER_ZESTAWU').AsInteger;
                        ZestSwd.Danezestawu.Swiadczenie.Idswiad:=ZestSwdQuery.FieldByName('ID_SWIADCZ_NAD').AsInteger;
//------------------------------------------------------------------------------
                        ProgressBar1.Position:=ProgressBar1.Position+1;
                        ProgressBar2.Min:=0;
                        ProgressBar2.Max:=1;
                        ProgressBar2.Position:=0;
                        PozRozlQuery.SQL.Clear;
                        PozRozlQuery.SQL.Add('SELECT * ');
                        PozRozlQuery.SQL.Add('FROM import ');
                        PozRozlQuery.SQL.Add('WHERE ID_SWIADCZ_NAD='+ZestSwdQuery.FieldByName('ID_SWIADCZ_NAD').AsString+'; ');
                        PozRozlQuery.Active:=TRUE;
                        ProgressBar2.Max:=PozRozlQuery.RecordCount-1;
                        PozRozlQuery.First;
                        while not PozRozlQuery.Eof do begin
                            ProgressBar2.Position:=ProgressBar2.Position+1;
                            //PozRozl:=ZestSwd.
                            PozRozlQuery.Next;
                        end;
                        ZestSwdQuery.Next;
                    end;
//------------------------------------------------------------------------------
                    OpenUFSFile(SaveDialog1.FileName);
                    ufs.WriteWStrLn('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
                    ufs.WriteWStrLn(komunikat.XML);
                    {XMLDocument1.XML.Text:=komunikat.XML;
                    XMLDocument1.Active:=TRUE;
                    XMLDocument1.SaveToFile(SaveDialog1.FileName);}
                finally
                    CloseUFSFile;
                    Memo1.Lines.Add('Koniec zapisu do pliku');
                end;
            end;
        end;
        Memo1.Lines.Add('Zakończenie generowania eksportu');
    end else begin
        Application.MessageBox('Brak połaczenia z bazą danych','Błąd połączenia',MB_ICONERROR+MB_OK);
    end;
end;

Oczywiście jest tak sporo śmieci z testów, ale clou progamu, to:

 
ufs.WriteWStrLn('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
ufs.WriteWStrLn(komunikat.XML);

Dlaczego tak, a nie:

 
komunikat.SaveToFile(SaveDialog1.FileName);

Bo komunikat jest zdefiniowany przez XML Data Bindera w ten sposób:

 
IXMLKomunikatType = interface(IXMLNode)

Czyli na wyjściu daje XML : WideString

0
toyman napisał(a)

Kurcze. Nie dam sobie łapy obciąć, ale kiedyś przedstawiciel Embarcadero w polsce mial serie szkoleń z Delphi X - m.in. o tworzeniu aplikacji z wykorzystaniem unicode. Padło tam takie zdanie, że w związku z tym , że stringi sa jednobajtowe - nie pozwalają na przechowywanie unikodów (unikody obcinane są do jednego bajta)
i to jest prawda do wersji 2006 chyba bo później string jest już dwubajtowy.
i żeby poprawnie przetwarzać unikody trzeba właśnie używać WideStringów.
tak, z tym, że całe VCL dostarczane z Delphi używało właśnie string a nie widestring i stąd właśnie to, że aby pokazać w aplikacji (w jakiejś kontrolce) widestring trzeba doinstalować sobie kontrolki, które to potrafią (wspomniany już TNT np.)

Co do zeosów to one na pewno nie wspierają unicode, a co za tym idzie np. tutaj ZestSwdQuery.FieldByName('TYP_SWIADCZENIA').AsString dostajesz zwykły string w aktualnej stronie kodowej windowsa na którym uruchomisz program.

spróbuj zamienić to ufs.WriteWStrLn(komunikat.XML); na ufs.WriteWStrLn(AnsiToUtf8(komunikat.XML));

0

akurat ZEOSa używam przy łaczeniu z PostgreSQLem w pewnym konkretnym zastosowaniu, gdzie strona kodowa jest ustawiona na Win1250, w przypadku MySQLa korzystam z MyDAC DevArta (bo ten właśnie wspiera kodowanie utf-8).

Swoją drogą - czy jest jakiś komponent/driver do PostgreSQLa, działający na D7, który wspierałby kodowanie utf-8 ?

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