Декодировать UTF-8 из файла JSON - PullRequest
1 голос
/ 16 апреля 2019

У меня есть файл JSON с закодированным строковым полем UTF-8, представляющим содержимое в формате JPG:

"ImageData": "ÿØÿà\u0000\u0010JFIF\u0000\u0001\u0002\u0000\u0000d\u0000d\u0000\u0000

Я анализирую JSON и получаю это значение:

var imageString : string;
...
imageString:=jv.GetValue<string>('ImageData');

Но у меня возникают проблемы при декодировании байтов и сохранении их в файл

Вариант 1. SaveBytesToFile(BytesOf(imageString),pathFile);

Как видите, заголовок неверный (должен начинаться с ÿØÿà)

option1

Вариант 2. SaveBytesToFile(TEncoding.UTF8.GetBytes(imageString),pathFile);

Проблема, аналогичная варианту 1

option2

Код для SaveBytesToFile:

procedure SaveBytesToFile(const Data: TBytes; const FileName: string);
var
  stream: TMemoryStream;
begin
  stream := TMemoryStream.Create;
  try
    if length(data) > 0 then
      stream.WriteBuffer(data[0], length(data));
    stream.SaveToFile(FileName);
  finally
    stream.Free;
  end;
end;

Как мне правильно его декодировать?

Ответы [ 2 ]

10 голосов
/ 16 апреля 2019

JSON - это текстовый формат, он вообще не предусматривает обработки двоичных данных. Почему байты изображения не кодируются в текстово-совместимом формате, например base64 , base85 , base91 и т. Д.? В противном случае используйте вместо этого что-то вроде BSON (двоичный JSON) или UBJSON (универсальный двоичный JSON), которые поддерживают двоичные данные.

В любом случае, BytesOf() будет повреждать байты, поскольку он использует языковой стандарт пользователя по умолчанию (через TEncoding.Default, который является UTF-8 на платформах, отличных от Windows!), Поэтому символы вне диапазона ASCII являются объектом интерпретация локали и не будет производить нужные вам байты.

В вашей ситуации, убедитесь, что библиотека JSON декодирует файл JSON как UTF-8, тогда вы можете просто перебрать полученную строку (библиотека JSON должна анализировать экранированные последовательности в символы для вас) и обрезать символы как есть до 8-битных значений. Не выполняйте преобразование кодировок вообще. Например:

var
  imageString : string;
  imageBytes: TBytes;
  i: Integer;
  ...
begin
  ...

  imageString := jv.GetValue<string>('ImageData');

  SetLength(imageBytes, Length(imageString));
  for i := 0 to Length(imageString)-1 do begin
    imageBytes[i] := Byte(imageString[i+1]);
  end;

  SaveBytesToFile(imageBytes, pathFile);

  ...
end;

image

Кстати, ваш SaveBytesToFile() может быть значительно упрощен, не тратя память на копирование TBytes:

procedure SaveBytesToFile(const Data: TBytes; const FileName: string);
var
  stream: TBytesStream;
begin
  stream := TBytesStream.Create(Data);
  try
    stream.SaveToFile(FileName);
  finally
    stream.Free;
  end;
end;

Или:

procedure SaveBytesToFile(const Data: TBytes; const FileName: string);
var
  stream: TFileStream;
begin
  stream := TFileStream.Create(FileName, fmCreate);
  try
    stream.WriteBuffer(PByte(Data)^, Length(Data));
  finally
    stream.Free;
  end;
end;

Или:

uses
  ..., System.IOUtils;

procedure SaveBytesToFile(const Data: TBytes; const FileName: string);
begin
  System.IOUtils.TFile.WriteAllBytes(FileName, Data);
end;
0 голосов
/ 17 апреля 2019

C3 BF C3 98 C3 BF C3 A0 - правильные байты для строки UTF-8 ÿØÿà, я бы сказал, что ваше преобразование Варианта 1. работает.

Не обманывайтесь вашим шестнадцатеричным редактором: все символы UTF-8 ÿØÿà находятся вне диапазона ASCII, но шестнадцатеричные редакторы обычно отображают символы e-ASCII для каждого отдельного байта, поэтому он отображает ÿ вместо ÿ.

Проверьте ли локаль, упомянутую @RemyLebeau, Я думаю, что использование TEncoding.ASCII будет правильным .

...