1

Continuing from my previous question: How to save UTF-16 (Little Endian) and String value inside a file in Delphi 7?

When I use the following code from this answer:

uses
  Classes;
    
const
  BOM: WideChar = $FEFF;
var
  W: WideString;
  FS: TFileStream;
begin
  W := 'ᗿABC';
  FS := TFileStream.Create('text.txt', fmCreate);
  try
    FS.WriteBuffer(BOM, Sizeof(BOM));
    FS.WriteBuffer(PWideChar(W)^, Length(W) * Sizeof(WideChar));
  finally
    FS.Free;
  end;
end;

I have these problems:

  1. when I used this: W := 'ᗿABC'; my IDE showed W := '?ABC'; Can I fix this problem? Because [?] is not my goal

  2. When running the code, I get the error in the image:

image

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Adam Luper
  • 230
  • 9
  • Please add the procedure / function that saves the content of your edit boxes to the file. – Tom Brunberg May 07 '23 at 07:24
  • 1
    [You've asked this before and it was answered before](https://stackoverflow.com/questions/76150675/how-to-save-utf-16-little-endian-and-string-value-inside-a-file-in-delphi-7). – AmigoJack May 07 '23 at 07:44
  • @TomBrunberg, Yes Of course i used of these codes for save file: var MyText: TStringlist; begin MyText:= TStringlist.create; try MyText.Add('line 1'); MyText.Add('line 2'); MyText.SaveToFile('c:\folder\filename.txt'); finally MyText.Free end; {try} – Adam Luper May 07 '23 at 07:46
  • and use of these codes for HEX convert: function String2Hex(const Buffer: AnsiString): string; begin SetLength(Result, Length(Buffer) * 2); BinToHex(PAnsiChar(Buffer), PChar(Result), Length(Buffer)); end; function Hex2String(const Buffer: string): AnsiString; begin SetLength(Result, Length(Buffer) div 2); HexToBin(PChar(Buffer), PAnsiChar(Result), Length(Result)); end; var hex: string; str: AnsiString; begin hex := String2Hex('stackoverflow'); ShowMessage(hex); // shows 'hex' str := Hex2String(hex); ShowMessage(str); // shows 'stackoverflow' end; – Adam Luper May 07 '23 at 07:47
  • @AmigoJack, No, unfortunately, I had asked the wrong question, that's why I didn't get the right answer! – Adam Luper May 07 '23 at 07:49
  • @AdamLuper I already covered this in your [previous question](https://stackoverflow.com/questions/76150675/). You are not asking a new question, you are just rephrasing the last question. The same solution I gave you earlier still applies here. – Remy Lebeau May 07 '23 at 07:50
  • @RemyLebeau, Thank you, but I get the following error when running the code: [Error] Unit1.pas(28): Incompatible types: 'WideChar' and 'Integer' – Adam Luper May 07 '23 at 07:54
  • @AdamLuper then you are not using the code correctly. If you had a problem with the code you were given earlier, you should have commented on it back then. Now, nothing in this new question actually relates to this new problem you are having. So please, [edit] this question to remove all of the irrelevant info, and instead show the actual code you are having trouble with now, and the error message, and indicate where in the code the error is occurring. – Remy Lebeau May 07 '23 at 07:57
  • @RemyLebeau, I'm sorry, I thought this question was different from the previous one, that's why I asked a new question, should I continue with the same question or edit the previous question? – Adam Luper May 07 '23 at 08:01
  • @AdamLuper the previous question has already been answered and closed. This new question has nothing to do with the error message you are getting now. So either close this question and post a new one just about the error. Or else edit this question accordingly. As it stands right now, this new question is not answerable in its current form. – Remy Lebeau May 07 '23 at 08:04

2 Answers2

1
  1. The code editor in Delphi 7 does not support Unicode characters very well, if at all. You really should stop using a 25-year-old compiler and upgrade.

    In any case, try this instead:

    W := #$15FF'ABC';
    

    Or, you may have to resort to something more like this:

    W := WideChar($15FF) + WideString('ABC');
    
  2. Try type-casting the integer value to WideChar:

    const
      BOM: WideChar = WideChar($FEFF);
    

    Otherwise, use Word instead of WideChar:

    const
      BOM: Word = $FEFF;
    
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you for your many tips. What version of Delphi do you recommend that is most compatible with Delphi 7 codes and does not require learning a new language? (Because I have many sources that are useful in Delphi 7 language!) – Adam Luper May 07 '23 at 09:01
  • I got the full answer to my question, just to get more information, I will ask some very beginner questions and if you don't like it, don't answer: const BOM: WideChar = WideChar($FEFF); What exactly does this value ($FEFF) do? Can't get inside W := WideChar($15FF) + WideString('ABC'); put directly? Values in WideChar ($15FF) are stored in reverse in the file? or values ($15FF) FF after 15 will not be in const BOM: WideChar = WideChar($FEFF); put directly? I want to know exactly what the use of each one is and if there is no problem if each one is edited? – Adam Luper May 07 '23 at 09:10
  • instead of the number 15 in the following code: W := WideChar($15FF) + WideString('ABC'); How can I set a variable so that the user's age is received from the edit box and is placed instead of the value 15? – Adam Luper May 07 '23 at 09:20
  • @AdamLuper you definitely need to use a Unicode compatible Delphi version, that is Delphi 2009 and newer! Then you wouldn't have to fiddle around with BOM as hex codes etc.! – Delphi Coder May 07 '23 at 10:22
  • @DelphiCoder, Thank you, you are right, will all the codes used in Delphi 7 change and become unusable to switch to the new Delphi? Is it possible to use the variable in W := WideChar($15FF) instead of the number 15? – Adam Luper May 07 '23 at 13:13
  • @AdamLuper `$FEFF` is the UTF-16 [BOM (Byte Order Mark)](https://en.m.wikipedia.org/wiki/Byte_order_mark), which lets readers know the text's encoding and endian. Code written in pre-2009 Delphi will port to post-2009 Delphi with little-to-no changes, except when dealing with code that assumed `Sizeof(Char)=1`, or stored binary data in `string`, etc. See [Unicode in RAD Studio](https://docwiki.embarcadero.com/RADStudio/en/Unicode_in_RAD_Studio) and Embarcadero's [Migration and Upgrade Center](https://www.embarcadero.com/rad-in-action/migration-upgrade-center#unicode) for more details. – Remy Lebeau May 07 '23 at 17:03
  • @AdamLuper based on your descriptions, you do not appear to be dealing with a pure text-only file, but rather a mix of text and binary data. You probably should NOT be trying to encode the Age and Score values as Unicode characters to begin with. For instance, write a `$FF` byte first, followed by the Age value as its own separate byte. Repeat for Score – Remy Lebeau May 07 '23 at 17:07
  • @RemyLebeau, I used these codes: const BOM: WideChar = WideChar($FEFF); var W1: WideString; FS: TFileStream; Num_value: string; begin Num_value := Hex2String(IntToStr(Edit1.GetTextLen)); W1 := WideChar($FF) + Num_value; output file in Hex Editor: FFFE3F002100 -> is not true But if i used static 21 number (WideChar($21FF)), output file in Hex editor is : FFFEFF21 -> this is true Which part am I doing wrong? – Adam Luper May 07 '23 at 19:16
  • @AdamLuper I still think you have a fundamental misunderstanding of what Unicode is and what your file data is actually meant to represent (you never explained why the file needs to look the way you want), and so you are approaching your issue from the wrong direction to begin with. But, to answer your specific question, to let the user enter `21` and write it out as a `WideString` as `WideChar($21FF)` (bytes `FF 21`), you would need something more like this instead: `Num_value := WideChar(((StrToInt('$'+Edit1.Text) and $FF) shl 8) or $FF)` – Remy Lebeau May 08 '23 at 08:04
  • @RemyLebeau, This code was awesome! Num_value := WideChar(((StrToInt('$'+Edit1.Text) and $FF) shl 8) or $FF) Now how can I save this number as Hex! That is, for example, the value inside Edit1.Text should be converted to Hex, for example, if the number 21 is inside Edit1.Text, it should be converted to Hex 15. – Adam Luper May 08 '23 at 13:46
  • @RemyLebeau, I received the best, most complete and most professional answers from you. I am working on a series of simple text files that I use this method to prevent the manipulation of text values so that normal people cannot easily change the text values and any change will make the text file unreadable and usable. ! And I use the combination of text and Hex with a series of other values! – Adam Luper May 08 '23 at 13:46
  • @AdamLuper if the user is meant to enter a decimal number instead of a hex number, then simply drop the `'$' ` when calling `StrToInt()`, eg: `Num_value := WideChar(((StrToInt(Edit1.Text) and $FF) shl 8) or $FF)` – Remy Lebeau May 08 '23 at 15:12
1

Using Delphi 7 kind of works with Unicode in WideStrings, but it is not consistent:

var
  RegExprWLineSeparators: WideString;
begin
  // In the following line the literal ends up producing ASCII question marks for the 5th and 6th character.
  RegExprWLineSeparators:= #$d#$a#$b#$c+ WideChar($2028)+ WideChar($2029)+ #$85;

  // But assigning characters individually will make both correct - so first do the one above
  // (or provide anything, because you want to reassign it anyway) and later make it per character.
  RegExprWLineSeparators[5]:= WideChar($2028);
  RegExprWLineSeparators[6]:= WideChar($2029);

A few characters aren't even assignable this way (neither via text literal, nor via ordinal literal), so you use a different approach to test against these:

var
  sText: Widestring;
begin
  sText:= <something>;

  // Checking if the first character is a UTF-16 BE or LE BOM
  case Word(sText[1]) of
    $FEFF,
    $FFFE: Delete( sText, 1, 1 );  // Remove such a character
  end;

Thumb of rules are:

  • use Words and cast them to WideChar when using text literals
  • use Word over WideChar when comparing/checking
  • noncharacters (like U+FFFE and U+FFFF) are usually unassignable

Using Delphi 7 for Unicode as a beginner should be avoided - do this when you're confident with Unicode, UTF-16 and Pascal in general. I started this since Windows 2000 on Delphi 5 and later continued with Delphi 7, having experiences like these in different occasions (regular expressions, amongst others).

As an alternative to a dated Delphi version you could try the free Lazarus IDE for FPC - it uses UTF-8 as an approach to Unicode and should treat/support text literals in code much better. The IDE even looks like the robust Delphi 7 one.

AmigoJack
  • 5,234
  • 1
  • 15
  • 31
  • Thank you for the useful information you provided. What version of Delphi do you recommend to replace Delphi 7 that has the least problems with Delphi 7 code? I don't just mean changing the IDE. – Adam Luper May 07 '23 at 19:25
  • None - I'd recommend Lazarus. Also take the [tour] - you can upvote answers and/or comments if you're thankful. – AmigoJack May 08 '23 at 12:42
  • I Know but (You need at least 15 reputation to cast a vote) i can't cast vote! – Adam Luper May 08 '23 at 13:24