Как преобразовать AnsiString в TBytes и наоборот?

У меня есть AnsiString, и мне нужно преобразовать его наиболее эффективным способом в TBytes. Как я могу это сделать ?


person zeus    schedule 19.01.2018    source источник


Ответы (2)


Предполагая, что вы хотите сохранить ту же кодировку, вы можете сделать это

SetLength(bytes, Length(ansiStr));
Move(Pointer(ansiStr)^, Pointer(bytes)^, Length(ansiStr));

В обратном порядке это идет

SetLength(ansiStr, Length(bytes));
Move(Pointer(bytes)^, Pointer(ansiStr)^, Length(bytes));
person David Heffernan    schedule 19.01.2018
comment
спасибо, Дэвид, грустно, что у них нет другого пути, кроме копирования памяти :( - person zeus; 19.01.2018
comment
Это требование иметь его как ТБ, которое вызывает копирование памяти. Переосмыслите это требование, чтобы избежать копирования. - person David Heffernan; 19.01.2018
comment
В любом случае, почему вы вообще работаете со строками в кодировке ANSI? Действительно нет места для этого. - person David Heffernan; 19.01.2018
comment
потому что ansiString намного мощнее, чем unicodestring. на веб-сервере каждый ввод и вывод, которые вы делаете, находятся в 8-битной строке (utf8), глупо преобразовывать все в 16-байтовую строку в середине (и это может привести к ошибке, если ввод был плохо закодирован в UTF8) - person zeus; 19.01.2018
comment
Таким образом, UTF8String является более мощным (в каком смысле, на самом деле мощным?), но, конечно, не AnsiString (кодовые страницы - это в основном концепция Windows). И не все пишут вебсерверы. - person Rudy Velthuis; 19.01.2018
comment
Почти уверен, что вы не хотите преобразовывать UTF8 в ANSI ....... - person David Heffernan; 19.01.2018
comment
@RudyVelthuis Не все пишут веб-серверы. Нет, но некоторые люди делают. И для них важна производительность. То, что вы не используете Delphi определенным образом, не означает, что это не важно для кого-то. Это ваш обычный рефрен, когда вы склонны игнорировать критику, если она относится к слабостям, которые вас не затрагивают. - person David Heffernan; 19.01.2018
comment
Тем не менее, даже для веб-сервера AnsiString, скорее всего, не самый полезный тип. UTF8String могла быть, наверное, но не AnsiString. В любом случае, я не отклоняю критику, но я знаю, что loki хочет, чтобы Embarcadero полностью сбросил UnicodeString и снова заменил его на AnsiSring (или UTF8String). Вот что я имел в виду. Я не думаю, что UTF8String или AnsiString более мощные, тем более что большинство API и платформ по умолчанию используют UTF-16. - person Rudy Velthuis; 19.01.2018
comment
@RudyVelthuis Нет, AnsiString в наши дни не очень полезен. Неплохая поддержка первого класса для строк в кодировке UTF-8. Как вы сказали, большинство API-интерфейсов платформы используют UTF-16. Linux является очевидным исключением. - person David Heffernan; 19.01.2018
comment
@David: я действительно ожидал, что они сделают UTF8String основным типом строки в Linux. Но, к сожалению, это было не так. Я думаю, они решили, что это будет слишком много работы и слишком много $IFDEF в библиотеке времени выполнения. - person Rudy Velthuis; 19.01.2018
comment
@loki: при определенных обстоятельствах вам может вообще не понадобиться копировать. Это очень сильно зависит от того, что вы делаете с ТБ. В некоторых случаях вы, вероятно, могли бы просто выполнить приведение (напрямую или, при необходимости, через приведение к указателю, сейчас это невозможно проверить) к TBytes. Но в этом случае вы должны быть уверены, когда и как, и вы должны абсолютно знать, что вы делаете. Это был бы настоящий хак, но он мог бы сэкономить время. Все зависит от того, что вы хотите делать с ТБ. - person Rudy Velthuis; 19.01.2018
comment
@Rudy: ansiString/UTF8string/и т. д. независимо от того, что это то же самое, это 8-битная строка! И я работаю только с данными UTF8 внутри ansiString, а не с UTF8String, потому что 99% крошечных 8-битных функций сделаны с параметрами ansiString, а не с UTF8String (как ваш последний ansiString posEx, который вы сделали). Да, я хотел бы найти способ преобразовать ansiString в Tbytes, так как в конце концов он содержит те же данные, но похоже, что мы не можем :( - person zeus; 19.01.2018
comment
На самом деле нет, это не то же самое. Кодировка другая. Копии, безусловно, можно избежать. Но не тогда, когда вы используете AnsiString и TBytes. - person David Heffernan; 19.01.2018
comment
@Rudy: также я не хочу, чтобы Embarcadero сбрасывала UnicodeString, я хочу, чтобы Embarcadero продолжала полностью поддерживать ansiString (это безумие, что у них нет такой функции, как inttostr в ansiString). Я надеялся, что с linux они пересмотрят свою позицию по поводу ansiString, но, поскольку linux был сделан в дуге, я думаю, у них очень (очень) мало клиентов, и они, вероятно, больше ничего не будут делать для linux :( - person zeus; 19.01.2018
comment
AnsiString вообще не имеет смысла в Linux, где нет кодировки ANSI. - person David Heffernan; 19.01.2018
comment
@Loki: Ты можешь догадаться о многом, но я сомневаюсь, что ты прав. Линукс не нуждается в AnsiString, ему нужен только UTF8String. Ansi и его кодовые страницы предназначены для Windows. И ARC не имеет к этому абсолютно никакого отношения. - person Rudy Velthuis; 19.01.2018
comment
@David: в POSIX есть способы декодировать/кодировать/преобразовывать кодовые страницы Windows, но IME — это довольно медленный процесс, который используется нечасто. Я думаю, что для этого также требуется сторонний код (iconvert или UCI, IIRC), хотя он включен в среду выполнения для Delphi. Ansi действительно является идиомой Windows. Веб-сервер, конечно, не должен его использовать. - person Rudy Velthuis; 19.01.2018
comment
@loki: все эти функции также принимают UTF8String, насколько мне известно. Мой RVPosEXA, конечно, да, но и другие тоже. Вы знаете, что UTF8String — это AnsiString с кодовой страницей 65001 (IIRC), верно? - person Rudy Velthuis; 19.01.2018
comment
@Rudy, да, я знаю, что AnsiString - это UTF8String с кодовой страницей 65001. но очень важно избегать использования двух разных типов строк (например, UTF8string и aniString), даже если они имеют одинаковую кодовую страницу, потому что компилятор во время компиляции не знает, что кодовая страница та же и будет выполнять транслитерацию (например, MyAnsiStringUTF8 := MyUTF8String приведет к UTF8 => UTF16 => UTF8) - person zeus; 19.01.2018
comment
@David: да, я знаю, когда я говорю об ansiString, я говорю о 8-битной строке. лично, если мы сможем удалить информацию о кодовой странице из ansiString, это будет лучший способ :) - person zeus; 19.01.2018
comment
@loki: действительно, не используйте разные типы строк. Просто используйте UTF8String вместо AnsiString (возможно, с локальной кодовой страницей) с содержимым UTF-8. Избавьтесь от своего AnsiStringWithUTF8Content. Преобразование не произойдет, если ваши строки имеют одинаковую кодовую страницу. - person Rudy Velthuis; 19.01.2018
comment
Почему бы вам просто не использовать TBytes везде? - person David Heffernan; 19.01.2018
comment
@Rudy, я проверил, и, выполняя anAnsiString := anUTF8String, сделал некоторую транслитерацию в utf16, чтобы вернуться к utf8 :( так что единственный способ - иметь UTF8String везде везде везде :( - person zeus; 19.01.2018
comment
@DavidHeffernan: потому что, как вы делаете что-то вроде pos('xxx', myTbytes) - person zeus; 19.01.2018
comment
Напишите простую функцию для этого. Если вы действительно хотите избежать копирования и выделения кучи, а также перекодирования, то это простой выход. - person David Heffernan; 19.01.2018
comment
Возвращаюсь к одному замечанию, про анзитринг, который не должен содержать байтов, это нонсенс. если вы ищете пример в исходном коде delphi, то реализация процедуры BinToHex(Buffer: PAnsiChar; Text: PWideChar; BufSize: Integer); ... для emb PansiChar может содержать байты, но ansiString не может, когда PansiChar и ansistring имеют одну и ту же цель! полный абсурд, конечно, ansiString может содержать байты, просто эту транслитерацию нужно деактивировать! - person zeus; 20.01.2018
comment
BinToHex должен принимать PByte. Это неправильно, что он может принимать PAnsiString. - person David Heffernan; 20.01.2018

Функция BytesOf преобразует AnsiString в терабайты.

var
  A: AnsiString;
  B: TBytes;
begin
  A := 'Test';
  B := BytesOf(A);

  // convert it back
  SetString(A, PAnsiChar(B), Length(B));
end;
person Sebastian Z    schedule 19.01.2018
comment
@DavidHeffernan Нет, но это был не вопрос ОП :-) - person HeartWare; 19.01.2018
comment
@HeartWare Прочитайте заголовок вопроса еще раз - person David Heffernan; 19.01.2018
comment
Это было в значительной степени скрыто в названии. Я добавил примечание о преобразовании его обратно. - person Sebastian Z; 19.01.2018
comment
Верно. Я добавил этот бит только в качестве редактирования после того, как запоздало заметил его в заголовке. - person David Heffernan; 19.01.2018
comment
Обратите внимание, что BytesOf() аварийно завершает работу, если входная строка пуста, потому что она не проверяет наличие Length=0 перед индексацией как байтов, так и строки. Использование SetLength()+Move() с приведением типов Pointer, как показал Дэвид, не страдает от этой проблемы. Также обратите внимание, что если пойти другим путем, использовать SetString() проще, чем явно использовать SetLength()+Move(): SetString(ansiStr, PAnsiChar(bytes), Length(bytes)); - person Remy Lebeau; 19.01.2018
comment
Да, SetString() лучше. Я обновил код. BytesOf('') у меня не вылетает. Мне просто повезло? - person Sebastian Z; 20.01.2018
comment
@remy RTL компилируется без проверки диапазона. Таким образом, BytesOf не будет терпеть неудачу с пустой строкой. Один из нюансов кода в моем ответе заключается в том, что он избегает вызовов UniqueString. И BytesOf, и ваш вызов SetString не будут. - person David Heffernan; 21.01.2018