Autor Beitrag
hRb
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 267
Erhaltene Danke: 12



BeitragVerfasst: Mi 26.07.17 00:02 
Hallo,
Ich stelle ein funktionsfähiges D7-Programm (Ansi-String) auf XE3 um. Überwiegend sind Datenbankexporte in csv-Format zu verarbeiten. Die folgende "schulmäßige" Methode funktioniert nicht mehr, nämlich das Einlesen von ANSI-Daten .
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var fStream:string;
    ch     :char;
if Execute then with TFileStream.Create(FileName,fmShareDenyNone) do  //fmOpenRead,fmShareDenyWrite) do
 if FindFirst(FileName, faAnyFile, SearchRec)=0 then
   begin
     len:=SearchRec.Size;
     FindClose(SearchRec);
     SetLength(fStream,len);
     Read(fStream[1],len);
   ... 
fStream wird nun Byteweise analysiert, u.a. nach Trennzeichen und evtl. Hochkomma. Dann zeilenweise verarbeitet. Zugriff mit
    ch:=fStream[i];

Unter XE3 gibt es mehrere Fehler:
1. nach obiger Befehlsfolge stehen in fStream keine ANSI-Zeichen, sondern chinesische Hyroglyphen
hier Anlage ansehen (jpg-Bild) (kann leider kein Bild einfügen)

2. definiere ich um
var fStream:TStream;

so lässt sich das Programm nicht mehr compilieren. Folgender Befehl führt zum Fehler
ausblenden Delphi-Quelltext
1:
ch:=fStream[i];					

Compilermeldung: [dcc32 Fehler] Unit1.pas(2108): E2149 Klasse besitzt keine Standardeigenschaft

Frage1: warum steht in fStream kein Ascii-Text
Frage2: wie greife ich auf Stream-Daten zeichenweise zu

Wer kann helfen?
Einloggen, um Attachments anzusehen!
Holgerx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 62
Erhaltene Danke: 27

Win95 - Win11 / MSServer2000 - MSServer2019
Delphi 6pro / XE4
BeitragVerfasst: Mi 26.07.17 10:16 
Hmm..

D7 : String = AnsiString = 1 Byte je Char
XE : String = UniCode = min. 2 Byte je Char

(Quick and Dirty)
Gehe in deiner Routine hin und ersetze das

var fStream:string;

durch

var fStream:AnsiString;

;)

Ein Stream arbeitet immer Byte-Basierend und nicht Char-Basierend.
Deshalb klappt das hier nicht mit UniCode..

Hinweis:
Das funktioniert nur, wenn die CSV-Datei ANSI/ASCII codiert ist und nicht UniCode ;)
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mi 26.07.17 22:14 
Bitte denke dran, dass du mit fStream[i] immer genau eine CodeUnit bekommst, die 1-2 Codepoints beschreibt. Das ist nicht wirklich hilfreich.
Lösung: Zugriff auf den Indexer eines Strings möglichst vermeiden.

Du könntest die komplette Datei in einen String einlesen (Encoding angeben!) und danach na den Semikolons splitten.
In neueren Delphis gibt es dafür offenbar schon eine Funktion:
ausblenden Delphi-Quelltext
1:
function ReadAllText(const Path: string;  const Encoding: TEncoding): string;					

Solange deine Datei nicht größer wird als 500MB sollte das passen. (Wenn doch, wird es knapp mit dem 1GB String im Speicher)
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 267
Erhaltene Danke: 12



BeitragVerfasst: Mi 26.07.17 23:11 
Hallo, liebe Helfer,
ich verstehe die hilfreichen Tipps nur teilweise.
1. Ich stelle das Programm ja gerade von AnsiString auf String um, damit mögliche Sonderzeichen verarbeitet werden können.
2. Der Typ String bei XE ist m.W. ja UTF8-Zeichensatz und belegt nur dann 2 Byte, wenn die Zeichen den Ascii-zeichenraum 0..128 überschreiten (darunter nur 1 Byte).
3. Mit der Anweisung
ausblenden Delphi-Quelltext
1:
len:=SearchRec.Size;					

lese ich ja genau die Länge der Datei in Byte und mit
Read(fStream[1],len);
exakt die Datei ein.
Nun liegt es doch an meiner Verarbeitung festzustellen ob ein Zeichen zwei Byte belegt. Demzufolge sollte ich auch auf jedes einzelne Byte im String zugreifen können. Oder liege ich da falsch? bzw. wozu ist dann die Read-Methode in XE-Compilern noch da?

Hinweis: Ich habe im Programm auch den Opendialog mit
ausblenden Delphi-Quelltext
1:
Lines.LoadFromFile(DateiName);					

Da werden die Zeichen korrekt dargestellt. Da jedoch bei csv-ExportDaten die Werte oft mit " .. " gepackt werden und innerhalb dieser Zeichenfolge #10 oder #13 als Umbruch vorkommen können, habe ich bisher die Read-Methode verwendet.

Keinen Hinweis gab es, warum der Compiler den Befehl ch:=fStream[i] unter XE nicht übersetzt
Holgerx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 62
Erhaltene Danke: 27

Win95 - Win11 / MSServer2000 - MSServer2019
Delphi 6pro / XE4
BeitragVerfasst: Do 27.07.17 05:47 
Hmm..

user profile iconhRb hat folgendes geschrieben Zum zitierten Posting springen:

2. Der Typ String bei XE ist m.W. ja UTF8-Zeichensatz und belegt nur dann 2 Byte, wenn die Zeichen den Ascii-zeichenraum 0..128 überschreiten (darunter nur 1 Byte).


Wie kommst Du denn darauf?
UniCode ist immer 2 Bytes je Char (bestimmte Codepages ziehen 2 UniCode Chars zu einer CodeUnit zusammen) und sollte in XE ein UTF-16 sein!

user profile iconhRb hat folgendes geschrieben Zum zitierten Posting springen:

3. Mit der Anweisung
ausblenden Delphi-Quelltext
1:
len:=SearchRec.Size;					

lese ich ja genau die Länge der Datei in Byte und mit
Read(fStream[1],len);
exakt die Datei ein.
Nun liegt es doch an meiner Verarbeitung festzustellen ob ein Zeichen zwei Byte belegt. Demzufolge sollte ich auch auf jedes einzelne Byte im String zugreifen können. Oder liege ich da falsch? bzw. wozu ist dann die Read-Methode in XE-Compilern noch da?


Richtig! Du liest BYTES ein, keine Chars!

Wenn deine Datei Ansicodiert ist, dann stehen da immer 1 Byte = 1 Char drinnen.
Wenn Du nun diese Bytes (und was anderes list du mit Read nicht ein) in einen Unicode String einliest, dann werden somit 2 Bytes (= 2 AnsiChars) in ein Unicode Char gelesen. Das passiert auch im Windows-Editor, wenn dieser Chinesische Zeichen anzeigt ;)

Wenn Du mit String Index arbeiten möchtest, musst Du vorher prüfen (Stichwort BOM) ob es eine Ansi oder Unicode Datei ist und dann entsprechend in ein Ansistring oder Unicodestring einlesen!

Sprich bei ASCII/ANSI-Dateien mit AnsiString einlesen und bei Unicode-Dateien mit string (= UnicodeString unter XE).

Mit deinem FStream[x] greifst Du nicht auf das x-te BYTE sondern auf das x-te CHAR zu. somit bei Ansistring immer auf das Byte, bei UniCode immer auf 2/4 Bytes!

Somit ist die Methode nicht wirklich brauchbar zum 'nachträglichen' aufdröseln..
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 267
Erhaltene Danke: 12



BeitragVerfasst: Do 03.08.17 21:50 
Zitat:
Wie kommst Du denn darauf?

Ok, da muss ich wohl umdenken. Glaubte gelesen zu haben, dass der Aufruf LoadFromFile ein encoding vornimmt und in UTF8 codiert. Zumindest glaube ich dies bei txt- csv- oder pas-Dateien zu erkennen. (Hätte bei ReadStream ja auch sein können)
Zitat:
UniCode ist immer 2 Bytes je Char
Das ist klar, mir ging es um UTF8-Code
Zitat:
bestimmte Codepages ziehen 2 UniCode Chars zu einer CodeUnit zusammen
heißt das ein Byte? z.B. bei Ascii-Zeichen? Wenn Ja, welche Codepages tun dies bzw. kann ich als Programmierer die Codepage festlegen / wie?

Eines jedenfalls wird für mich klar: so einfach -wie oft beschrieben- stellt man Ansi-String-Programme nicht auf den allgemeinen String-Typ um.
PS: mit der encoding-Function laboriere ich gerade und sammle Erfahrungen
Gruß hRb