Delphi Unicode String Тип, хранящийся непосредственно по его адресу (или «Unicode ShortString») - PullRequest
3 голосов
/ 11 мая 2010

Мне нужен строковый тип Unicode, который хранит строку непосредственно по адресу переменной, как в случае типа ShortString (только для Ansi).

Я имею в виду, что если я объявлю S: ShortString и разрешу S := 'My String', то при @S я найду длину строки (как один байт, поэтому строка не может содержать более 255 символов) с последующим самой строкой в ​​кодировке ANSI.

Что я хотел бы, так это вариант Unicode. То есть мне нужен строковый тип, такой, что при @S я найду 32-разрядное целое число без знака (или на самом деле достаточно одного байта), содержащее длину строки в байтах (или в символах, что это половина количества байтов), за которым следует Unicode-представление строки. Я пробовал WideString, UnicodeString и RawByteString, но все они появляются только для хранения адреса в @S и реальной строки где-то еще (я думаю, это имеет отношение к подсчету ссылок и тому подобное). Обновление: Наиболее важной причиной этого, вероятно, является то, что было бы очень проблематично, если бы sizeof (строка) была переменной.

Я подозреваю, что нет встроенного типа для использования, и что мне нужно придумать свой собственный способ хранения текста так, как я хочу (что на самом деле забавно). Я прав?

Обновление Мне, между прочим, нужно будет использовать эти строки в упакованных записях. Мне также нужно вручную читать / записывать эти строки в файлы / кучу. Я мог бы жить со строками фиксированного размера, такими как <= 128 символов, и я мог бы изменить дизайн задачи, чтобы она работала со строками с нулевым символом в конце. Но PChar не будет работать, так как sizeof (PChar) = 1 - это просто адрес. </p>

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

Ответы [ 5 ]

4 голосов
/ 11 мая 2010

Ты прав. Нет точного аналога ShortString, который содержит символы Unicode. Есть много вещей, которые подходят близко, в том числе WideString, UnicodeString и массивы WideChar, но если вы не желаете вернуться к тому, как вы собираетесь использовать тип данных (сделайте побайтовым копии в памяти и в файлах, при этом их можно использовать во всех контекстах, можно разрешить строку), тогда ни один из встроенных типов Delphi не будет работать для вас.

WideString завершается неудачно, потому что вы настаиваете, что длина строки должна существовать по адресу строковой переменной, но WideString является ссылочным типом; единственное, что по его адресу, это другой адрес. Его длина находится по адресу в переменной , минус четыре. Однако это может измениться, потому что все операции этого типа должны проходить через API.

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

Массив WideChar может быть скопирован без проблем, но он не отслеживает его эффективную длину и не очень часто действует как строка. Вы можете назначить ему строковые литералы, и он будет действовать так, как вы назвали StrLCopy, но вы не можете присвоить ему строковые переменные .

Вы можете определить запись, которая имеет поле для длины и другое поле для массива символов. Это решило бы проблему длины, но все равно имело бы все остальные недостатки недекорированного массива.

На вашем месте я бы просто использовал встроенный строковый тип. Затем я написал бы функции, которые помогут перенести его между файлами, блоками памяти и собственными переменными. Это не так сложно; вероятно, намного проще, чем пытаться заставить перегрузку оператора работать правильно с пользовательским типом записи. Подумайте, сколько кода вы будете писать для загрузки и хранения ваших данных, и сколько кода вы собираетесь писать, который использует вашу структуру данных как обычную строку. Вы собираетесь написать код персистентности данных один раз , но до конца жизни проекта вы будете использовать эти строки и захотите, чтобы они выглядели и действовали как настоящие струны. Так что используйте реальные строки. «Пострадать» от неудобств, связанных с ручным созданием нужного формата на диске, и воспользоваться преимуществом возможности использования всех существующих функций библиотеки строк.

1 голос
/ 11 мая 2010

На самом деле у вас это есть с новыми строками Unicode.
s как указатель указывает на s [1], а 4 байта слева содержат длину.
Но почему бы просто не использовать Length (s)?

А для прямого считывания длины из памяти:

procedure TForm9.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := 'hlkk ljhk jhto';
  {$POINTERMATH ON}
  Assert(Length(s) = (PInteger(s)-1)^); 
  //if you don't want POINTERMATH, replace by PInteger(Cardinal(s)-SizeOf(Integer))^
  showmessage(IntToStr(length(s)));
end;
1 голос
/ 11 мая 2010

Не существует версии ShortString в Юникоде. Если вы хотите хранить данные в Юникоде внутри объекта, а не как ссылочный тип, вы можете выделить буфер:

var
  buffer = array[0..255] of WideChar;

Это имеет два недостатка. 1, размер фиксирован, а 2, компилятор не распознает его как строковый тип.

Основная проблема здесь # 1: фиксированный размер. Если вы собираетесь объявить массив внутри более крупного объекта или записи, компилятор должен знать, насколько он велик, чтобы вычислить размер объекта или самой записи. Для ShortString это не было большой проблемой, так как они могли занимать всего 256 байтов (1/4 от K), что не так уж много. Но если вы хотите использовать длинные строки, адресуемые 32-разрядным целым числом, максимальный размер составляет 4 ГБ. Вы не можете поместить это внутрь объекта!

Это, а не подсчет ссылок, поэтому длинные строки реализованы как ссылочные типы, чей встроенный размер всегда является постоянным sizeof (указатель). Затем компилятор может поместить строковые данные в динамический массив и изменить его размер в соответствии с текущими потребностями.

Зачем вам нужно помещать что-то подобное в упакованный массив? Если бы я догадался, я бы сказал, что это, вероятно, как-то связано с сериализацией. Если это так, то лучше использовать TStream и обычную строку Unicode, а также записать целое число (размер) в поток, а затем содержимое строки. Это оказывается намного более гибким, чем пытаться собрать все в упакованный массив.

1 голос
/ 11 мая 2010

PChar должен работать так, верно? AFAIK, это массив символов, хранящихся там, где вы их положили. Ноль завершен, не уверен, как это работает с Unicode Chars.

0 голосов
/ 12 мая 2010

Решение, которое я в конце концов выбрал, заключается в следующем (пример из реальной жизни - строка, конечно, является третьим членом, называемым «Ident»):

TASStructMemHeader = packed record
  TotalSize: cardinal;
  MemType: TASStructMemType;
  Ident: packed array[0..63] of WideChar;
  DataSize: cardinal;
  procedure SetIdent(const AIdent: string);
  function ReadIdent: string;
end;

, где

function TASStructMemHeader.ReadIdent: string;
begin
  result := WideCharLenToString(PWideChar(@(Ident[0])), length(Ident));
end;

procedure TASStructMemHeader.SetIdent(const AIdent: string);
var
  i: Integer;
begin
  if length(AIdent) > 63 then
    raise Exception.Create('Too long structure identifier.');
  FillChar(Ident[0], length(Ident) * sizeof(WideChar), 0);
  Move(AIdent[1], Ident[0], length(AIdent) * sizeof(WideChar));
end;

Но потом я понял, что компилятор действительно может интерпретировать array[0..63] of WideChar как строку, поэтому я мог просто написать

  var
    MyStr: string;

  Ident := 'This is a sample string.';
  MyStr := Ident;

Следовательно, в конце концов, ответ, данный Мэйсоном Уилером выше, на самом деле ответ .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...