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