Нечетные символы в строке Unicode - PullRequest
3 голосов
/ 03 марта 2012

Я сталкиваюсь с чем-то, когда дело доходит до реализации MP3 ID3 v2. У меня это работает по большей части, за исключением этой проблемы, которая, вероятно, вообще не связана с этим. Во всяком случае, я использую приведенный ниже код для обработки извлечения данных тегов заголовка, которые содержат текст.

Я сталкиваюсь с тем, что (наверное?) Я сталкиваюсь с символами Юникода в некоторых разных строках. Я сделал попытку преобразовать это ниже, и это работает. Но я получаю $ 3F как символ перед строкой и $ 3F $ 3F позже. Могу ли я что-нибудь сделать с кодом ниже, чтобы разобрать их, или мне придется делать это самому? Файлы были закодированы ITunes, если это поможет.

function Id3v2_string(currp: pointer; datasize: integer): string;
{ handles string processing for ID3v2 data }
  const
    IS_TEXT_UNICODE_UNICODE_MASK = $0F;
  var
    outstr: string;
    uscan: integer;
  begin
    outstr := '';
    SetLength(outstr, datasize);
    uscan := IS_TEXT_UNICODE_UNICODE_MASK;
    if IsTextUnicode(currp, datasize, @uscan) then
      outstr := WideCharToString(currp)
    else
      move(currp^, outstr[1], datasize);
    Result := outstr;
  end;

Обратите внимание, я действительно не заинтересован в медиа-библиотеке для этого, так как все, что я хочу сделать, это редактировать теги ID3, а не воспроизводить файлы - реализация уже завершена, за исключением нескольких мелких проблем, подобных этой.

1 Ответ

6 голосов
/ 03 марта 2012

В зависимости от того, какая версия ID3 v2 используется, текстовым строкам может предшествовать или не предшествовать байт, чтобы сообщить вам фактическое кодирование строки. Не используйте IsTextUnicode(), чтобы угадать, что такое кодировка (тем более что она может сообщать ложные результаты ).

В ID3 v2 до v2.3 байт кодирования отсутствует, текст либо ISO-8859-1, либо UCS-2, а строки UCS-2 всегда начинаются с спецификации, поэтому вы знаете порядок байтов. Например:

// prior to Delphi 2009 - String is Ansi
function Id3v2_string(currp: Pointer; datasize: Integer): String; 
var
  W: WideString;
  I: Integer;
  Ch: WideChar;
begin 
  Result := '';
  if (datasize >= SizeOf(Word)) and ((PWord(currp)^ = $FEFF) or (PWord(currp)^= $FFFE)) then begin
    // UCS-2 with BOM
    W := WideCharLenToString(PWideChar(Integer(currp) + SizeOf(Word)), (datasize - SizeOf(Word)) div SizeOf(WideChar)); 
    if PWord(currp)^ = $FFFE then begin
      // BE, convert to LE
      for I := 1 to Length(W) do begin
        Ch := W[I];
        W[I] := WideChar(((Word(Ch) and $FF) shl 8) or (Word(Ch) shr 8));
      end;
    end;
  end else begin
    // ISO-8859-1
    I := MultiByteToWideChar(28591, 0, PAnsiChar(currp), datasize, nil, 0);
    if I > 0 then begin
      SetLength(W, I);
      MultiByteToWideChar(28591, 0, PAnsiChar(currp), datasize, PWideChar(W), I);
    end;
  end;
  Result := TrimRight(W);
end; 

.

// Delphi 2009+ - String is Unicode
function Id3v2_string(currp: Pointer; datasize: Integer): String; 
var
  Enc: TEncoding;

  function Convert(P: Pointer; Size: Integer): String;
  var
    Buf: TBytes;
  begin
    SetLength(Buf, Size);
    if Size > 0 then Move(P^, Buf[0], Size);
    Result := Enc.GetString(Buf);
  end;

begin 
  Result := '';
  if (datasize >= SizeOf(Word)) and ((PWord(currp)^ = $FEFF) or (PWord(currp)^ = $FFFE)) then begin
    // UCS-2 with BOM
    if PWord(currp)^ = $FFFE then begin
      // BE
      Enc := TEncoding.BigEndianUnicode;
    end else begin
      // LE
      Enc := TEncoding.Unicode;
    end;
    Result := Convert(PWord(currp)+1, datasize - SizeOf(Word));
  end else begin
    // ISO-8859-1
    Enc := TEncoding.GetEncoding(28591);
    try
      Result := Convert(currp, datasize);
    finally
      Enc.Free;
    end;
  end;
end; 

ID3 v2.4 переключает UCS-2 на UTF-16 и добавляет поддержку UTF-8 и UTF-16BE без спецификации, например:

// prior to Delphi 2009 - String is Ansi
function Id3v2_string(currp: Pointer; datasize: Integer; Encoding: Byte): String; 
var
  W: WideString;
  I: Integer;
  Ch: WideChar;
begin 
  Result := '';

  case Encoding of
    $00: begin
      // ISO-8859-1
      I := MultiByteToWideChar(28591, 0, PAnsiChar(currp), datasize, nil, 0);
      if I > 0 then begin
        SetLength(W, I);
        MultiByteToWideChar(28591, 0, PAnsiChar(currp), datasize, PWideChar(W), I);
      end;
    end;
    $01: begin
      // UTF-16 with BOM
      SetString(W, PWideChar(Integer(currp) + SizeOf(Word)), (datasize - SizeOf(Word)) div SizeOf(WideChar));
      if PWord(currp)^ = $FFFE then begin
        // BE, convert to LE
        for I := 1 to Length(W) do begin
          Ch := W[I];
          W[I] := WideChar(((Word(Ch) and $FF) shl 8) or (Word(Ch) shr 8));
        end;
      end;
    end;
    $02: begin
      // UTF-16BE without BOM, convert to LE
      SetString(W, PWideChar(currp), datasize div SizeOf(WideChar));
      for I := 1 to Length(W) do begin
        Ch := W[I];
        W[I] := WideChar(((Word(Ch) and $FF) shl 8) or (Word(Ch) shr 8));
      end;
    end;
    $03: begin
      // UTF-8
      I := MultiByteToWideChar(65001, 0, PAnsiChar(currp), datasize, nil, 0);
      if I > 0 then begin
        SetLength(W, I);
        MultiByteToWideChar(65001, 0, PAnsiChar(currp), datasize, PWideChar(W), I);
      end;
    end;
  end;
  Result := TrimRight(W);
end;

.

// Delphi 2009+ - String is Unicode
function Id3v2_string(currp: Pointer; datasize: Integer; Encoding: Byte): String; 
var
  Enc: TEncoding;

  function Convert(P: Pointer; Size: Integer): String;
  var
    Buf: TBytes;
  begin
    SetLength(Buf, Size);
    if Size > 0 then Move(P^, Buf[0], Size);
    Result := Enc.GetString(Buf);
  end;

begin 
  Result := '';

  case Encoding of
    $00: begin
      // ISO-8859-1
      Enc := TEncoding.GetEncoding(28591);
      try
        Result := Convert(currp, datasize);
      finally
        Enc.Free;
      end;
    end;
    $01: begin
      // UTF-16 with BOM
      if PWord(currp)^ = $FFFE then begin
        // BE
        Enc := TEncoding.BigEndianUnicode;
      end else begin
        // LE
        Enc := TEncoding.Unicode;
      end;
      Result := Convert(PWord(currp)+1, datasize - SizeOf(Word));
    end;
    $02: begin
      // UTF-16BE without BOM
      Enc := TEncoding.BigEndianUnicode;
      Result := Convert(currp, datasize);
    end;
    $03: begin
      // UTF-8
      Enc := TEncoding.UTF8;
      Result := Convert(currp, datasize);
    end;
  end;
  Result := TrimRight(Result);
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...