-1

I have delphi application, I need to rewrite it for OS X. This app writes/reads data to/from HID-device.

I have issues when I'm trying to write string from mac.

Here is the line that I'm writing(from debugger on windows): 'Новый комплекс 1' and this works good. Meanwhile if copy this from debugger to somewhere it becomes 'Íîâûé êîìïëåêñ 1'. Device shows it as it was written, in cyrillic. And that's OK.

When I'm trying to repeat this steps on OS X, device shows unreadable symbols. But if I do hardcode 'Íîâûé êîìïëåêñ 1' from windows example it's OK again.

Give some hints.

How it on Windows

Some code:

 s:= 'Новый комлекс 1'

s:= AnsiToUtf8(ReplaceNull(s));

Here is ReplaceNULL:

function ReplaceNull(const Input: string): string;
var
Index: Integer;
Res: String;
begin
Res:= '';
for Index := 1 to Length(Input) do
begin
if Input[Index] = #0 then
  Res:= Res + #$12
else
  Res:= Res + Input[Index];
end;
 ReplaceNull:= Res;
end;

this string I put to Tstringlist and then save to file:

ProgsList.SaveToFile(Mwork.pathLibs+'stream.ini', TEncoding.UTF8);

Other program read this list and then writes to device:

Progs:= TStringList.Create();

Progs.LoadFromFile(****);

s:= UTF8ToAnsi(stringreplace(Progs.Strings[i], #$12, #0, [rfReplaceAll,   rfIgnoreCase]));

And then write it to device.

So the line which writes seems like this:

"'þ5'#0'ÿ'#$11'Новый комплекс 1'#0'T45/180;55;70;85;90;95;100;T45/180'#0'ÿ'"

On the Mac I successfully get the same string. But device can't show this in Cyrillic.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
artemk
  • 121
  • 4
  • 12
  • What do you mean? Use Unicode. Or are you a time traveller from the 20 years ago? – David Heffernan Mar 14 '18 at 17:35
  • What does mean "Use unicode"? – artemk Mar 14 '18 at 17:38
  • @artemk Delphi strings are all unicode - they will appear the same on MacOS as well as Windows. OSX has full unicode support. Unless you are consuming data from somewhere that is already encoded as some specific ANSI encoding? Otherwise, if you're having a problem please include some code to demonstrate what that problem is. – J... Mar 14 '18 at 17:53
  • 1
    If you don't know what Unicode is you need to stop whatever you doing and learn. – David Heffernan Mar 14 '18 at 17:58
  • Ok, i'm going to rewrite question. – artemk Mar 14 '18 at 18:04
  • Check now please – artemk Mar 14 '18 at 18:49
  • So, it sounds like you are writing to a hardware device with a text display. That hardware device must consume an ANSI encoded string in Windows-1251 format, is that correct? In your example, what type is the string `s`? I see you are using `AnsiToUTF8` but you seem to also be providing it a `string` type and not an `AnsiString`. Your hardcoded string constant will be in a format dictated by the source file - did you save your source files as UTF-8? Delphi should have warned you when you first added non ASCII character to the file, which option did you select? – J... Mar 14 '18 at 19:13
  • 1
    `Progs.LoadFromFile(****);` should be `Progs.LoadFromFile(****, TEncoding.UTF8);` – Remy Lebeau Mar 14 '18 at 19:39
  • What version of Delphi did the app you're rewriting originally target? If you're jumping from a non-unicode to unicode version you ***absolutely must*** understand the implications of this change first. – Disillusioned Mar 14 '18 at 23:39
  • App was written in XE5. – artemk Mar 15 '18 at 11:22
  • s - String. I don't use source files. And for windows ProgsList.SaveToFile(Mwork.pathLibs+'stream.ini', TEncoding.UTF8); So yes, source file saved as UTF8. But when i read this i convert it to ANSI again s:= UTF8ToAnsi(stringreplace(Progs.Strings[i], #$12, #0, [rfReplaceAll, rfIgnoreCase])); – artemk Mar 15 '18 at 12:21
  • Please edit your question title. It's nothing but a repeat of the information available in the tags you've added. Your question should describe the problem you're trying to solve or question you're asking, in a way that will be useful to a future reader who is looking through a list of search results. Your current title has zero meaning. – Ken White Mar 15 '18 at 22:47

2 Answers2

2

A Delphi string is encoded in UTF-16 on all platforms. There is no need to convert it, unless you are interacting with non-Unicode data outside of your app.

That being said, if you have a byte array that is encoded in a particular charset, you can convert it to another charset using Delphi's TEncoding.Convert() method. You can use the TEncoding.GetEncoding() method to get a TEncoding object for a particular charset (if different than the standard supported charsets - ANSI, ASCII, UTF-7, UTF-8, and UTF-16 - which have their own property getters in TEncoding).

var
  SrcEnc, DstEnc: TEncoding;
  SrcBytes, ConvertedBytes: TBytes;
begin
  SrcBytes := ...; // Cyrillic encoded bytes
  SrcEnc := TEncoding.GetEncoding('Cyrillic'); // or whatever the real name is...
  try
    DstEnc := TEncoding.GetEncoding('Windows-1251');
    try
      ConvertedBytes := TEncoding.Convert(SrcEnc, DstEnc, SrcBytes);
    finally
      DstEnc.Free;
    end;
  finally
    SrcEnc.Free;
  end;
  // use ConvertedBytes as needed...
end;

Update: To encode a Unicode string in a particular charset, simply call the TEncoding.GetBytes() method, eg:

s := 'Новый комлекс 1';
Enc := TEncoding.GetEncoding('Windows-1251');
try
  bytes := Enc.GetBytes(s);
finally
  Enc.Free;
end;

s := 'Новый комлекс 1';
bytes := TEncoding.UTF8.GetBytes(s);

You can use the TEncoding.GetString() to decode bytes in a particular charset back to a String, eg:

bytes := ...; // Windows-1251 encoded bytes
Enc := TEncoding.GetEncoding('Windows-1251');
try
  s := Enc.GetString(bytes);
finally
  Enc.Free;
end;

bytes := ...; // UTF-8 encoded bytes
s := TEncoding.UTF8.GetString(bytes);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • For the second bit, it's an important caveat, for hard coded strings, that the source file be saved in unicode format. Delphi defaults to ANSI for source files but prompts to save as unicode when the source file contains non-ASCII characters. – J... Mar 14 '18 at 20:15
  • @J...: sure, when the string data is a string literal at compile time. But if the data is coming from somewhere else (file, UI, etc), then what you say doesn't apply. – Remy Lebeau Mar 14 '18 at 20:26
  • OP appeared to be using string literals, as did your example. Thought it was important to mention. – J... Mar 15 '18 at 03:06
  • So you suggest to convert my string from MACOS to bytes, and then to windows-1251? – artemk Mar 15 '18 at 11:26
  • @artemk I'm merely pointing out HOW to do conversions. Whether you SHOULD be doing them depends on what you are trying to accomplish exactly – Remy Lebeau Mar 15 '18 at 14:44
0

The answer was next. Delphi Berlin 10.1 uses KOI8-R, and my device - cp1251. As i'd wanted to write russian symbols(Cyrillic) i've created table of matches for symbols from KOI8-R and cp1251.

So, i take string in KOI8-R make it in cp1251.

Simple code:

 Dict:=TDictionary<String,String>.Create;
 Dict.Add(#$439,#$E9);//'й'
 Dict.Add(#$44E,#$FE);//'ю'
 Dict.Add(#$430,#$E0);//'а'

....

function tkoitocp.getCP1251Code(str:string):string;
var i:integer; res,key,val:string;  pair:Tpair<String,String>;
begin
res:='';
for i:=1 to length(str) do
 begin
   if dict.ContainsKey(str[i]) then
   begin
      pair:= dict.ExtractPair(str[i]);

      res:=res+pair.Value;
      dict.Add(pair.Key,pair.Value);
   end
   else
   res:=res+str[i];
 end;
 Result:=res;

end;
artemk
  • 121
  • 4
  • 12