No mapping for the Unicode character exists in the target multi-byte code page ? (Delphi XE6)

Yordan Yanakiev picture Yordan Yanakiev · Jul 9, 2014 · Viewed 24.3k times · Source

The following code works on Win32, anyway it is trowing exception if run on Android or iOS. The exception is : "No mapping for the Unicode character exists in the target multi-byte code page"

function Form1.ZDecompressString(aText: String): String;
var
  strInput,
  strOutput : TStringStream;
  Unzipper  : TZDecompressionStream;
begin

   Result    := '';
   strInput  := TStringStream.Create( aText );
   strOutput := TStringStream.Create;

   try

      Unzipper := TZDecompressionStream.Create( strInput );

      try
         strOutput.CopyFrom( Unzipper, Unzipper.Size );
      finally
         Unzipper.Free;
      end;

      Result := strOutput.DataString;

   finally
      strInput.Free;
      strOutput.Free;
   end;
end;

procedure Form1.getUsers;
var
   XMLRqst     : String;
   XMLResponse : TStringStream;
   XMLRequest  : TStringStream;
   idHTTP      : TIdHTTP;

   s : String;
begin

   XMLRqst     := UTF8ToString( '<root company="belvew"/>' );
   XMLRequest  := TStringStream.Create( XMLRqst, TEncoding.UTF8 );
   XMLResponse := TStringStream.Create( '' );

   try
      try
         idHTTP                 := TIdHTTP.Create( Self );
         idHTTP.CookieManager   := idCookieManager;
         idHTTP.ReadTimeout     := 60000;{ IdTimeoutInfinite; }
         idHTTP.ConnectTimeout  := 60000;
         idHTTP.HandleRedirects := True;

         XMLResponse.Position   := 0;
         XMLRequest.Position    := 0;

         idHTTP.Post( 'http://localhost/API/getUsers.aspx', XMLRequest, XMLResponse );
         idHTTP.Disconnect;

         unique_id := ZDecompressString( XMLResponse.DataString );

         XMLRequest.Free;
         XMLResponse.Free;

      except
         on E : Exception do
        begin
            ShowMessage( 'exception : '#13 + E.Message );
        end;
      end;

   finally
      idHTTP.Free;
   end;

end;

procedure Form1.onCreate( Sender : TObject );
begin
   getUsers;
end;
  • Why the code does not work under Android or iOS ?

Answer

David Heffernan picture David Heffernan · Jul 9, 2014

I would say that you need to throw this code away and start again. Like all compression algorithms that I have ever encountered, zlib does not operate on text. It operates on byte arrays. Back in the days when a Delphi string used 8 bit encoding, people played fast and loose with such strings, treating them as if they were byte arrays. And ever since those days, the misconception that compression can operate on strings has endured.

You need to pick an encoding to use to convert text into byte arrays. A good choice would be UTF-8. And then if you want to represent the compressed data as text, you need to use an encoding like base64.

The compression process is as follows:

  1. Encode the text as a UTF-8 byte array.
  2. Compress with zlib.
  3. Encode the compressed byte array with base64.

In the opposite direction you would:

  1. Decode the base64 text to a compressed byte array.
  2. Decompress this with zlib.
  3. Decode the UTF-8 encoded byte array to text.

To encode/decode UTF-8 you use TEncoding.UTF8. The GetBytes and GetString methods are what you need. And for base64 encode/decode you would probably be best advised to use the Indy library since you are already using it.


According to the comments, you've got as far as decompressing to a UTF-8 encoded byte array. In which case the final step is to write:

text := TEncoding.UTF8.GetString(utf8byteArray);