2010-04-01 7 views
4

Je convertis un programme D2006 en D2010. J'ai une valeur stockée dans un seul octet par chaîne de caractères dans ma base de données et je dois le charger dans un contrôle qui a un LoadFromStream, donc mon plan était d'écrire la chaîne dans un flux et l'utiliser avec LoadFromStream. Mais cela n'a pas fonctionné. En étudiant le problème, je vois un problème qui me dit que je ne comprends pas vraiment comment la conversion d'AnsiString en chaîne Unicode fonctionne. Voici un morceau de code autonome qui illustre le problème que je suis confus:Conversion d'une chaîne AnsiString en chaîne Unicode

procedure TForm1.Button1Click(Sender: TObject); {$O-} 
var 
    sBuffer: String; 
    oStringStream: TStringStream; 
    sAnsiString: AnsiString; 
    sUnicodeString: String; 
    iSize1, 
    iSize2: Word; 
begin 
    sAnsiString := '12345'; 
    oStringStream := TStringStream.Create(sBuffer); 
    sUnicodeString := sAnsiString; 
    iSize1 := StringElementSize(sAnsiString); 
    iSize2 := StringElementSize(sUnicodeString); 
    oStringStream.WriteString(sUnicodeString); 
end; 

Si vous cassez sur la dernière ligne, et inspectez la propriété Bytes de ostringstream, vous verrez qu'il ressemble à ceci:

Bytes (49 {$31}, 50 {$32}, 51 {$33}, 52 {$34}, 53 {$35} 

Je me attendais qu'il pourrait ressembler à

(49 {$31}, 00 {$00}, 50 {$32}, 00 {$00}, 51 {$33}, 00 {$00}, 
52 {$34}, 00 {$00}, 53 {$35}, 00 {$00} ... 

Apparemment, mes attentes sont erronées. Mais alors, comment convertir un AnsiString en unicode? Je n'obtiens pas les bons résultats de LoadFromStream car il lit le flux deux octets à la fois, mais les données qu'il reçoit ne sont pas disposées de cette façon. Que dois-je faire pour donner à LoadFromStream un flux de données bien formé basé sur une chaîne unicode?

Nous vous remercions de votre aide.

+3

Je ne crois pas qu'il y ait suffisamment d'information dans la question pour permettre une réponse significative. Quel type sont les variables impliquées? Cela sera potentiellement significatif en termes d'auto-conversions déclenchées dans le code généré par le compilateur. En particulier, quel est le type de oPayGrid? La présence d'une propriété sStream sur cet objet suggère qu'il ne s'agit pas d'un flux VCL standard. Idéalement, je voudrais voir l'exemple de code dans la question retravaillé/étendu dans un exemple de travail autonome qui démontre le comportement sans avoir besoin de plus d'explications/divination. – Deltics

+0

Salut à Aotearoa! Désolé. J'essayais d'éviter d'encombrer la question avec des détails inutiles. Je suppose que j'avais trop de succès. oPaygrid est une classe (TObject). oPaygrid.sStream est un AnsiString mal nommé. sUnicodeString est une chaîne Delphi qui est par défaut une chaîne unicode. iSize1 et iSize2 sont des entiers. Ma question est principalement conceptuelle. Quand un AnsiString est jeté dans une chaîne unicode, dois-je m'attendre à voir deux octets par caractère dans la chaîne unicode? Je ne le vois pas et il semble que ce soit ce qui m'empêche de charger mon contrôle avec LoadFromStream. – jrodenhi

+0

Vous ne devez pas utiliser StringElementSize(). Cela n'est nécessaire que si votre code est appelé à partir d'un module C++ Builder à demi-migration. L'affectation 'sUnicodeString: = sAnsiString' corrige la charge utile de la chaîne vers Char = WideChar, de sorte que l'appel à StringElementSize retournera toujours SizeOf (AnsiChar) pour votre AnsiString et SizeOf (Char) pour votre UnicodeString. SizeOf (AnsiChar)/SizeOf (Char) est aussi beaucoup plus rapide, plus facile à lire et à comprendre et plus court à écrire. –

Répondre

5

Quel est le type du paramètre oStringStream.WriteString? Si c'est AnsiString, vous avez une conversion implicite d'Unicode en Ansi et cela explique votre exemple.


Mise à jour: Maintenant, la vraie question est de savoir comment TStringStream stocke les données en interne. Dans l'exemple de code suivant (Delphi 2009)

procedure TForm1.Button1Click(Sender: TObject); 
var 
    S: string; 
    SS: TStringStream; 

begin 
    S:= 'asdfg'; 
    SS:= TStringStream.Create(S); // 1 byte per char 
    SS.WriteString('321'); 
    Label1.Caption:= SS.DataString; 
    SS.Free; 
end; 

TStringStream utilise le codage à l'intérieur du système par défaut ANSI (1 octet par char). Les procédures constructor et WriteString convertissent un argument de chaîne d'unicode en ANSI.

Pour remplacer ce comportement, vous devez déclarer le codage explicitement dans le constructeur:

procedure TForm1.Button1Click(Sender: TObject); 
var 
    S: string; 
    SS: TStringStream; 

begin 
    S:= 'asdfg'; 
    SS:= TStringStream.Create(S, TEncoding.Unicode); // 2 bytes per char 
    SS.WriteString('321'); 
    Label1.Caption:= SS.DataString; 
    SS.Free; 
end; 
+0

sBuffer est déclaré comme suit: var sBuffer: UnicodeString; – jrodenhi

0

Je pense que vous voulez utiliser:

LoadFromStream(stream, TEncoding.ASCII); 

Si votre texte d'un seul octet n'est pas ASCII, mais est basé sur une page de code, alors cela pourrait fonctionner:

LoadFromStream(stream, TEncoding.GetEncoding(1252)); 

où le "1252" est la page de code que yo Votre texte à un octet est basé sur.

+0

LoadFromStream est une méthode de AdvStringrid de TMS. Cela prend seulement un paramètre. – jrodenhi

+0

Je n'utilise pas TMS, mais peut-être que TTntStringGrid de TMS qui est dans leur Unicode Component Pack peut le faire pour vous. Voir: http://www.tmssoftware.com/site/tmsuni.asp Sinon, je vous recommande de contacter TMS et de leur indiquer votre problème, et ils peuvent ajouter le 2ème paramètre à leur LoadFromStream pour le rendre compatible avec Delphi 2009+ Unicode . – lkessler

+0

Cela ne semble pas être un problème avec la grille. S'il vous plaît voir mes modifications à mon message original. Il ne semble pas que la conversion d'un AnsiString en unicode modifie la mise en forme interne de la chaîne. – jrodenhi

0

Le format de flux dépend en grande partie de TStringStream.Encoding. Dans votre exemple, la page de codes utilisée doit être la même que sBuffer (voir implentation de TStringStream.Create).

Puisque oStringStream.WriteString(sUnicodeStream); semble enregistrer en octets simples, je suppose que sBuffer est un Ansistring ou un RawByteString.

Maintenant ... pourquoi la lecture échoue ... Vous n'avez pas encore fourni un exemple de la façon dont vous relisez ce flux.

+0

Donc, comme moi, vous vous attendez à ce que si sUnicodeStream est déclaré comme UnicodeString, vous verrez une chaîne composée de deux octets par caractère. Si vous essayez mon code exemple nouvellement édité, vous verrez que cela ne fonctionne apparemment pas de cette façon. – jrodenhi

+0

Serg a raison ... TStringStream ne vérifie que la page de codes dans la version ansistring. –