Discussion:
TStringStream - Unicode lesen
(zu alt für eine Antwort)
Matthias Frey
2014-11-15 18:56:47 UTC
Permalink
Hallo,
ich habe hier eine Datei die z.B. mit FF FE 5B 00 45 00
beginnt. Diese möchte ich in einen TStringStream laden und
zwar als Unicode.
Wie kann ich das tun? Bislang lade ich erst ein TFileStream
und kopiere dann mit CopyFrom den kompletten Inhalt.
Allerdings werden dann auch die 00 zu einem Zeichen.

Alternativ: wie kann ich die ganze Datei in einen
Unicode-String laden?

Matthias
Julius Kavay
2014-11-15 19:40:18 UTC
Permalink
Post by Matthias Frey
ich habe hier eine Datei die z.B. mit FF FE 5B 00 45 00
beginnt. Diese möchte ich in einen TStringStream laden und
zwar als Unicode.
bei einem (Tex- also nicht Binaere-) File, das mit FF FE ...
beginnt, steht FF FE fuer BOM (Byte-Order-Mark),
in diesem Fall bedeutet das, dass die nachfolgenden Zeichen
UTF-16/LE codiert sind.

Vereinfacht gesagt bedeutet das, dass die nachfolgenden
Zeichen:

a) 16Bit lang sind und
b) dass sie in Little-Endian Format vorliegen.
Post by Matthias Frey
Wie kann ich das tun? Bislang lade ich erst ein TFileStream
und kopiere dann mit CopyFrom den kompletten Inhalt.
Allerdings werden dann auch die 00 zu einem Zeichen.
sorry, ich kann (ein wenig) Delphi, bin aber der "Guru".
Post by Matthias Frey
Alternativ: wie kann ich die ganze Datei in einen
Unicode-String laden?
Ich denke, den ersten Zeichen auslassen (das ist ein Steuerzeichen)
und den Rest als Unicode (Little-Endian zB. alle Intel CPUs) lesen.

Gruesse
Julius
--
(my real mail address ends with a .net tld)
Matthias Frey
2014-11-15 21:51:41 UTC
Permalink
Post by Julius Kavay
ich habe hier eine Datei die z.B. mit FF FE 5B 00 45 00
beginnt. Diese möchte ich in einen TStringStream laden und
zwar als Unicode.
bei einem (Tex- also nicht Binaere-) File, das mit FF FE ...
beginnt, steht FF FE fuer BOM (Byte-Order-Mark),
in diesem Fall bedeutet das, dass die nachfolgenden Zeichen
UTF-16/LE codiert sind.
Vereinfacht gesagt bedeutet das, dass die nachfolgenden
a) 16Bit lang sind und
b) dass sie in Little-Endian Format vorliegen.
Hm ja, deshalb habe ich auch FF FE in die Datei vorne
reingeschrieben. ;-)
Post by Julius Kavay
Wie kann ich das tun? Bislang lade ich erst ein TFileStream
und kopiere dann mit CopyFrom den kompletten Inhalt.
Allerdings werden dann auch die 00 zu einem Zeichen.
sorry, ich kann (ein wenig) Delphi, bin aber der "Guru".
Alternativ: wie kann ich die ganze Datei in einen
Unicode-String laden?
Ich denke, den ersten Zeichen auslassen (das ist ein Steuerzeichen)
und den Rest als Unicode (Little-Endian zB. alle Intel CPUs) lesen.
Öh, ja schon. Also das würde ich hinbekommen. Dann immer zwei
Zeichen lesen, als AnsiChar/Byte interpretieren, zu einem Word
zusammensetzen und als WideChar nehmen.
Ich kann mir aber nicht vorstellen dass man heutzutage das
mit Delphi (XE2) noch zu Fuss machen muss.
TStringStream.Datastring ist ja schon ein UnicodeString.

Notfalls nehme ich eine TStringlist, das müsste gehen, auch
wenn ich dann mehr umbauen muss.
Post by Julius Kavay
Gruesse
Julius
Matthias
Helmuth J.H. Adolph
2014-11-17 14:28:59 UTC
Permalink
Hallo,
hier 2 Units mit deren Hilfe Ansi-, UTF8- und Unicode-Strings gelesen
und geschrieben werden können. Das funktioniert für Delphi Versionen
mit und ohne Unicode-Unterstützung:

unit Kompat;

interface

TYPE
{$IFDEF UNICODE }
TString = String;
{$ELSE }
TString = WideString;
{$ENDIF }

implementation
end.

unit UniStream;

interface

USES
Kompat,
Classes;
TYPE
TTextStream = Class(TFileStream)
constructor Create(const FileName: String; Mode: Word);
destructor Destroy; override;
PROCEDURE ReadLn(VAR Line : TString);
PROCEDURE WriteStr(CONST Line : TString);
PROCEDURE WriteStrLF(CONST Line : TString);
PROCEDURE Reset;
Function EOF : Boolean;
Public
IOResult : Integer;
Header : AnsiString;
HasHeader : Boolean;
IsUniCode : Boolean;
DateiName : String;
Private
EndeErreicht : Boolean;
Geschrieben : Boolean;
MyPosition : Int64;
MySize : Int64;
AnsPuffer : Array[0..8192] Of AnsiChar;
UniPuffer : Array[0..8192] Of WideChar;
PROCEDURE WriteHeader;

END;

implementation

USES
SysUtils;

VAR
WLineEnd : Array[0..1] OF WideChar;
ALineEnd : Array[0..1] OF AnsiChar;

Constructor TTextStream.Create(const FileName: String; Mode: Word);
VAR
AnsZeichen1 : AnsiChar;
AnsZeichen2 : AnsiChar;
AnsZeichen3 : AnsiChar;
Gelesen : Integer;
BEGIN
Inherited Create(FileName, Mode);
DateiName := FileName;
EndeErreicht := FALSE;
IOResult := 0;
IsUniCode := FALSE;
HasHeader := FALSE;
Geschrieben := FALSE;
Header := '';
IF Mode = fmOpenRead THEN BEGIN
Gelesen := Read(AnsZeichen1, SizeOf(AnsiChar));
IF Gelesen = SizeOf(AnsiChar) THEN BEGIN
Gelesen := Read(AnsZeichen2, SizeOf(AnsiChar));
IF Gelesen = SizeOf(AnsiChar) THEN BEGIN
IF (Ord(AnsZeichen1) = 254) AND (Ord(AnsZeichen2) = 255) THEN BEGIN
IsUniCode := TRUE; // Big Endian
IOResult := 1006;
END
ELSE BEGIN
IF (Ord(AnsZeichen1) = 255) AND (Ord(AnsZeichen2) = 254) THEN
BEGIN
IsUniCode := TRUE; // Little Endian
END;
END;
END;
END;
IF IsUniCode = TRUE THEN BEGIN
HasHeader := TRUE;
Header := AnsZeichen1 + AnsZeichen2;
END
ELSE BEGIN
Gelesen := Read(AnsZeichen3, SizeOf(AnsiChar));
IF Gelesen = SizeOf(AnsiChar) THEN BEGIN
IF (Ord(AnsZeichen1) = 239) AND (Ord(AnsZeichen2) = 187) AND
(Ord(AnsZeichen3) = 191) THEN BEGIN
HasHeader := TRUE; // UTF 8
Header := AnsZeichen1 + AnsZeichen2 + AnsZeichen3;
END;
END;
END;
IF HasHeader = FALSE THEN
Seek(0, soFromBeginning);
MyPosition := Position;
MySize := Size;
END;
END;

PROCEDURE TTextStream.Reset;
BEGIN
EndeErreicht := FALSE;
IF HasHeader = FALSE THEN
Seek(0, soFromBeginning)
ELSE BEGIN
IF IsUniCode = TRUE THEN
Seek(2, soFromBeginning)
ELSE
Seek(3, soFromBeginning);
END;
MyPosition := Position;
MySize := Size;
END;

Destructor TTextStream.Destroy;
BEGIN
Inherited Destroy;
END;

PROCEDURE TTextStream.ReadLn(VAR Line : TString);
VAR
UniZeichen : WideChar;
AnsZeichen : AnsiChar;
Gelesen : Integer;
i : Integer;
BEGIN
i := 0;
IOResult := 0;
IF IsUniCode = TRUE THEN BEGIN
While MyPosition < MySize Do BEGIN
Gelesen := Read(UniZeichen, SizeOf(WideChar));
IF Gelesen <> SizeOf(WideChar) THEN BEGIN
IOResult := -1;
exit;
END;
INC(MyPosition, SizeOf(WideChar));
IF UniZeichen = WideChar(13) THEN BEGIN
IF MyPosition < MySize THEN BEGIN
Read(UniZeichen, SizeOf(WideChar));
IF UniZeichen <> WideChar(10) THEN
Seek(SizeOf(WideChar)*-1,soFromCurrent)
ELSE
INC(MyPosition, SizeOf(WideChar));
END;
Break;
END;
IF UniZeichen = WideChar(10) THEN BEGIN
Break;
END;
UniPuffer[i] := UniZeichen;
INC(i);
END;
UniPuffer[I] := WideChar(0);
Line := UniPuffer;
END
ELSE BEGIN
While MyPosition < MySize Do BEGIN
Gelesen := Read(AnsZeichen, SizeOf(AnsiChar));
IF Gelesen <> SizeOf(AnsiChar) THEN BEGIN
IOResult := -1;
exit;
END;
INC(MyPosition, SizeOf(AnsiChar));
IF AnsZeichen = AnsiChar(13) THEN BEGIN
IF MyPosition < MySize THEN BEGIN
Read(AnsZeichen, SizeOf(AnsiChar));
IF AnsZeichen <> AnsiChar(10) THEN
Seek(SizeOf(AnsiChar)*-1,soFromCurrent)
ELSE
INC(MyPosition, SizeOf(AnsiChar));
END;
Break;
END;
IF AnsZeichen = AnsiChar(10) THEN BEGIN
Break;
END;
AnsPuffer[i] := AnsZeichen;
INC(i);
END;
AnsPuffer[I] := AnsiChar(0);
AnsPuffer[I+1] := AnsiChar(0);
IF HasHeader = TRUE THEN BEGIN
IF I = 0 THEN
Line := ''
ELSE BEGIN
UTF8ToUnicode(Unipuffer,8000,Anspuffer,I); // Wenn I = 0 wird
kein Leerstring gespeichert
Line := TString(Unipuffer);
END;
END
ELSE
Line := TString(AnsPuffer);
END;
IF MyPosition >= MySize THEN
EndeErreicht := TRUE;
END;

Function TTextStream.EOF : Boolean;
BEGIN
Result := EndeErreicht;
END;

PROCEDURE TTextStream.WriteHeader;
BEGIN
IF HasHeader = TRUE THEN BEGIN
INHERITED Write(Header[1], SizeOf(AnsiChar));
INHERITED Write(Header[2], SizeOf(AnsiChar));
IF IsUniCode = FALSE THEN
INHERITED Write(Header[3], SizeOf(AnsiChar));
END;
Geschrieben := TRUE;
END;

PROCEDURE TTextStream.WriteStr(CONST Line : TString);
VAR
AnsLine : AnsiString;
UtfLine : UTF8String;
BEGIN
IF geschrieben = FALSE THEN
WriteHeader;
IF Length(Line) = 0 THEN
Exit;
IF IsUniCode = TRUE THEN
INHERITED Write(Line[1], Length(line)*SizeOF(WideChar))
ELSE BEGIN
IF HasHeader = TRUE THEN BEGIN
UtfLine := UTF8Encode(Line);
INHERITED Write(UtfLine[1], Length(UtfLine)*SizeOF(AnsiChar));
END
ELSE BEGIN
AnsLine := AnsiString(Line);
INHERITED Write(AnsLine[1], Length(AnsLine)*SizeOF(AnsiChar));
END;
END;
END;

PROCEDURE TTextStream.WriteStrLF(CONST Line : TString);
BEGIN
WriteStr(Line);
IF IsUniCode = TRUE THEN
INHERITED Write(WLineEnd, 2*SizeOF(WideChar))
ELSE
INHERITED Write(ALineEnd, 2*SizeOF(AnsiChar));
END;
INITIALIZATION
WLineEnd[0] := #13; WLineEnd[1] := #10;
ALineEnd[0] := #13; ALineEnd[1] := #10;

end.
Post by Matthias Frey
Hallo,
ich habe hier eine Datei die z.B. mit FF FE 5B 00 45 00
beginnt. Diese möchte ich in einen TStringStream laden und
zwar als Unicode.
Wie kann ich das tun? Bislang lade ich erst ein TFileStream
und kopiere dann mit CopyFrom den kompletten Inhalt.
Allerdings werden dann auch die 00 zu einem Zeichen.
Alternativ: wie kann ich die ganze Datei in einen
Unicode-String laden?
Matthias
---
Diese E-Mail ist frei von Viren und Malware, denn der avast! Antivirus Schutz ist aktiv.
http://www.avast.com
Andreas Tscharner
2014-11-20 15:14:36 UTC
Permalink
Post by Matthias Frey
Hallo,
Auch hallo,

[snip]
Post by Matthias Frey
Alternativ: wie kann ich die ganze Datei in einen
Unicode-String laden?
var
strList : TStringList;
myStr : string;
begin
strList := TStringList.Create();
strList.LoadFromFile(filename, TEncoding.Unicode);
myStr := strList.Text;
end;

Das bedingt allerdings, dass Du schon weisst, dass die Datei eine
UTF-16/LE Encoding hat. Ansonsten musst Du ev. voher ein paar Bytes der
Datei *binär* einlesen und mit der
class function TEncoding.GetBufferEncoding
das Encoding bestimmen

(ungetestet)

Gruss
Andreas


--- news://freenews.netfront.net/ - complaints: ***@netfront.net ---
Loading...