WideString — Тип Delphi


Delphi WideString и Delphi 2009+

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

Delphi 2005 WideString — это тот же тип, что и Delphi 2010 String

Delphi 2005 WideString char, а также Delphi 2010 String char гарантируется всегда размером 2 байта.

При использовании всех форматов Unicode я не хочу, чтобы один из символов в моей строке внезапно попадал в 3 байта или что-то в этом роде.

Изменить: Нашел это: «Я действительно сказал UnicodeString, а не WideString. WideString все еще существует и не изменяется. WideString выделяется диспетчером памяти Windows и должен использоваться для взаимодействия с объектами COM WideString сопоставляет непосредственно с BSTR-типом в COM.» на http://www.micro-isv.asia/2008/08/get-ready-for-delphi-2009-and-unicode/

Теперь я еще более смущен. Итак, Delphi 2010 WideString отличается от Delphi 2005 WideString ? Должен ли я использовать UnicodeString вместо этого?

Изменить 2: Нет типа UnicodeString в Delphi 2005. FML.

Для вашего первого вопроса: WideString не совсем тот же тип, что и D2010 string. WideString — это тот же тип COM BSTR, что и всегда. Он управляется Windows, без подсчета ссылок, поэтому он копирует весь BSTR каждый раз, когда вы его передаете.

UnicodeString , который по умолчанию является строкой в D2009 и включен, является в основном версией AnsiString UTF-16, которую все мы знаем и любим. Он получил счетчик ссылок и управляется компилятором Delphi.

Во втором случае тип char по умолчанию теперь WideChar , который является тем же самым символом, который всегда использовался в WideString . Это кодировка UTF-16, 2 байта на char. Если вы сохраните данные WideString в файл, вы можете без проблем загрузить его в UnicodeString . Разница между этими двумя типами связана с управлением памятью, а не с форматом данных.

Как упоминалось ранее, тип данных string (фактически UnicodeString) в Delphi 2009 и выше не эквивалентен типу данных WideString в предыдущих версиях, но формат содержимого данных одинаков. Оба они сохраняют строку в UTF-16. Поэтому, если вы сохраняете текст с помощью WideString в более ранних версиях Delphi, вы должны иметь возможность правильно его читать, используя строковый тип данных в последних версиях Delphi (2009 и выше).

Следует отметить, что производительность UnicodeString намного превосходит WideString. Поэтому, если вы собираетесь использовать один и тот же исходный код как в Delphi 2005, так и в Delphi 2010, я предлагаю вам использовать псевдоним типа string с условной компиляцией в вашем коде, чтобы вы могли использовать лучшее из обоих миров:

Теперь вы можете использовать MyStringType в качестве типа строки в исходном коде. Если компилятор является Unicode (Delphi 2009 и выше), то ваш тип строки будет алиасом типа UnicodeString, который представлен в Delphi 2009 для хранения строк Unicode. Если компилятор не является unicode (например, Delphi 2005), тогда ваш тип строки будет псевдонимом для старого типа данных WideString. И поскольку оба они являются UTF-16, данные, сохраненные в любой из версий, должны быть правильно прочитаны другим.

  • Delphi 2005 WideString имеет тот же тип, что и строка Delphi 2010

Это неверно — ex-строка Delphi 2010 содержит скрытое внутреннее поле кодовой страницы, но, вероятно, для вас это не важно.

  • Delphi 2005 WideString char, а также строка Delphi 2010 char гарантированно будет иметь размер 2 байта.

Это верно. В Delphi 2010 SizeOf (Char) = 2 (Char= WideChar).

Для строк unicode не может быть другой кодовой страницы — было введено поле кодовой страницы для создания общего двоичного формата для строк Ansi (для которых требуется поле кодовой страницы) и строки Unicode (это не нужно).

Если вы сохраняете данные WideString для потока в Delphi 2005 и загружаете одни и те же данные в строку в Delphi 2010, все должно работать нормально.

@Marco — строки Ansi и Unicode в Delphi 2009+ имеют общий двоичный формат (12-байтовый заголовок).

Кодовая страница UnicodeString CP_UTF16 = 1200;

  • Если вы хотите работать только с строками unicode внутри вашего модуля — используйте UnicodeString type (*).
  • Если вы хотите общаться с COM или с другими кросс-модульными целями, используйте тип WideString .

Вы видите, WideString — особый тип, поскольку он не является родным типом Delphi. Это псевдоним/оболочка для BSTR — тип системной строки, предназначенный для использования с COM или межмодульными сообщениями. Будучи юникодом — это просто побочный эффект.

С другой стороны, AnsiString и UnicodeString — являются родными типами Delphi, которые не имеют аналога на других языках. String является просто псевдонимом для AnsiString или UnicodeString .

Итак, если вам нужно передать строку в другой код — используйте WideString , в противном случае используйте либо AnsiString , либо UnicodeString . Простой.

(*) Для старого Delphi — просто место

где-то в вашем коде. Это исправление позволит вам написать тот же код для любой версии Delphi.

В то время как D2010 char всегда и ровно 2 байта, в символах UTF-16 присутствуют те же проблемы сложения и комбинирования символов, что и символы UTF-8. Вы не видите этого с узкими строками, потому что они основаны на кодировке, но с помощью строк unicode возможно (и в некоторых ситуациях общее) иметь аффективные, но невидимые символы. Примеры включают в себя знак порядка байтов (BOM) в начале файла или потока unicode, символы слева направо/справа налево и огромный диапазон сочетания акцентов. Это в основном затрагивает вопросы о том, «сколько пикселей будет шириной этой строки на экране» и «сколько букв находится в этой строке» (в отличие от «количества символов в этой строке» ), но также означает, t случайным образом измельчают символы из строки и предполагают, что они пригодны для печати. Такие операции, как «удалить последнюю букву из этого слова», становятся нетривиальными и зависят от используемого языка.

Вопрос о том, что «один из символов в моей строке внезапно имеет длину 3 байта», отражает небольшое недоверие к тому, как работает UTF. Возможно (и действительно) взять три байта в строке UTF-8, чтобы представить один печатный символ, но каждый байт будет действительным символом UTF-8. Скажем, письмо плюс два сочетания акцентов. Вы не получите символ в UTF-16 или UTF-32 длиной 3 байта, но может иметь длину 6 байтов (или 12 байтов), если он представлен с использованием трех кодовых точек в UTF-16 или UTF-32. Это приводит нас к нормализации (или нет).

Какая максимальная длинна может быть у строки типа W >29.09.2011, 22:43. Просмотров 1272. Ответов 1

Здравствуйте, интересует какая максимальная длинна может быть у строки типа данных WideString опытным путем выяснил что до 1 000 000 знаков, а есть тип данных который позволяет например больше знаков в строке сделать?
заранее спасибо

Добавлено через 12 минут
Типы строк в Delphi:
Тип строки
Максимальная длина
Требования к памяти
Используется для
Нулевой символ в конце
ShortString
255
от 2 байт до 256
обратной совместимости
нет
AnsiString

2 Гб)
от 4 байт до 2 ГБ
однобайтных, двухбайтных и многобайтных символов ANSI
есть
String

2 Гб
от 2 байт до 2 ГБ
символов ANSI или Unicode
есть или нет
WideString

1 Гб)
от 4 байт до 2 ГБ
символов Unicode, в серверах COM и интерфейсах
есть

29.09.2011, 22:43

Какова может быть максимальная длина строки URL?
Поскажите какова может быть максимальная длина строки URL. Например, если передавать большой обьем.

какая максимальная температура должна быть на такой системе?
привет всем.такой вопрос. какая максимальная температура должна быть на такой системе как эта .

Какая должна быть скорость мяча в момент броска, чтобы максимальная высота полета составила 10м
Спортсмен бросает мяч под углом 45 градусов к горизонту. Какая должна быть скорость мяча в.

Максимальная длинна шнурка
нужно удлиннить усб шнурок. Имеется баллансный экранированый микрофонный кабель PROEL.

Какая может быть ошибка в коде?
Программирую 1 месяц,поэтому прошу камни сразу не кидать.Это первая игра которую решила.

Delphi WideString и Delphi 2009+

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

Delphi 2005 WideString является точно таким же типом , как Delphi 2010 String

Delphi 2005 WideString голец, а также Delphi 2010 String полукокс гарантированно всегда быть 2 байта.

Со всем Unicode форматирует там я не хочу, чтобы ударить с одним из символов в моей струне вдруг быть 3 байта в ширине или что-то подобное.

Изменить: Найдено это: «Я сказал UnicodeString, не WideString WideString до сих пор существует, и остается неизменным WideString выделяется менеджером памяти Windows , и следует использовать для взаимодействия с COM — объектами WideString карты непосредственно к типу BSTR в COM. «. в http://www.micro-isv.asia/2008/08/get-ready-for-delphi-2009-and-unicode/

Теперь я еще больше запутался. Таким образом, Delphi 2010 WideString это не то же самое , как Delphi 2005 WideString ? Должен ли я использовать UnicodeString вместо этого?

Изменить 2: Там нет UnicodeString типа в Delphi 2005. FML.

Для вашего первого вопроса: WideString не точно такой же тип , как D2010 в строке . WideString тот же тип COM BSTR , что он всегда был. Это управляется ОС Windows, без подсчета ссылок, поэтому он делает копию всей BSTR каждый раз , когда вы передаете его куда — нибудь.

UnicodeString , Который по умолчанию строка типа в D2009 и, в основном версия UTF-16 из AnsiString все мы знаем и любим. У него есть счетчик ссылок и управляется компилятором Delphi.

Во — вторых, по умолчанию char тип теперь WideChar , что одни и те же символы , которые всегда были использованы в WideString . Это UTF-16 кодирования, 2 байта на символ. Если сохранить данные WideString в файл, вы можете загрузить его в UnicodeString без проблем. Разница между этими двумя типами имеет дело с управлением памятью, а не формат данных.

Как уже упоминалось другие, строка (на самом деле UnicodeString) тип данных в Delphi 2009 и выше не является эквивалентом WideString типа данных в предыдущих версиях, но формат содержимого данных является то же самое. Оба из них сохранить строку в UTF-16. Так что если вы сохраните текст, используя WideString в более ранних версиях Delphi, вы должны быть в состоянии прочитать его правильно, используя строковый тип данных в последних версиях Delphi (2009 и выше).

Вы должны принять к сведению, что выполнение UnicodeString является способом выше, чем WideString. Так что если вы собираетесь использовать один и тот же исходный код как в Delphi 2005 и Delphi 2010, я предлагаю вам использовать тип строки псевдоним с помощью условной компиляции в коде, так что вы можете иметь лучшее из обоих миров:

Теперь вы можете использовать MyStringType в качестве типа строки в исходном коде. Если компилятор Unicode (Delphi 2009 и выше), то ваш тип строки будет псевдонимом типа UnicodeString, который вводится в Delphi 2009 для хранения строк Unicode. Если компилятор не Юникод (например, Delphi 2005), то ваш тип строки будет псевдонимом для старого типа данных WideString. И так как они оба являются UTF-16, данные, сохраненные на любой из версий должна быть прочитана другой правильно.

  1. Delphi 2005 WideString точно такой же тип, как Delphi 2010 строки

Это не так — бывший Delphi 2010 строки имеет скрытую внутреннее поле кодовой страницы — но, вероятно, это не имеет значения для вас.

  1. Delphi 2005 WideString голец, а также Delphi 2010 Строка полукокс гарантированно всегда 2 байта.

Это правда. В Delphi 2010 SizeOf (Char) = 2 (Char = WideChar).

Там не может быть другими кодовой юникод строк — поле кодового было введено, чтобы создать общий двоичный формат для обоего строк ANSI (что поле необходимо кодовое) и строку Unicode (которые не нужно).

Если сохранить данные WideString на поток в Delphi 2005 и загружать одни и те же данные в строку в Delphi 2010 все должно работать нормально.

@Marco — Ansi и Unicode строки в Delphi 2009+ имеют общий двоичный формат (заголовок 12 байт).

UnicodeString кодовая CP_UTF16 = 1200;

Я пишу класс, который будет сохранять широкие строки в двоичный файл.

Когда вы пишете класс в D2005 вы будете использовать WideString При переходе на D2010 WideString все еще будет действовать и работать должным образом. WideString в D2005 такое же, как WideString в D2010.

Тот факт, что String = WideString в D2010 нет необходимости рассматривать, так как компилятор имеет дело с этими вопросами легко.

Ваш вклад рутина, чтобы сохранить с (AString: String) нужна только одна линия, входящего в прок

В то время как D2010 символ всегда и ровно 2 байта, то же складной характер и сочетающие проблемы присутствуют в UTF-16 символов, как в UTF-8 символов. Вы не видите это с узкими строками, потому что они основаны кодовым, но с юникод строками возможно (и в некоторых случаях общих) иметь аффективные, но не видимые символы. Примеры включают в себя метку порядка байтов (BOM) в начале файла Юникода или потока, слева направо / справа осталось символов индикатора, а огромный диапазон сочетания акцентов. Это главным образом влияет на вопросы «сколько пикселей будет эта строка будет на экране» и «сколько букв в этой строке» (в отличии от «сколько символов в этой строке»), но также означает, что вы можете» т случайным образом измельчить символы из строки и предположит, что они для печати.

Вопрос о «один из символов в моей строке внезапно будучи длиной 3 байта» отражает немного confustion о том, как работает UTF. Возможно (и действует) принять три байта в строке UTF-8, чтобы представить один печатный знак, но каждый байт будет допустимым UTF-8 символов. Скажем, письмо плюс два сочетающие акцентов. Вы не получите символ в UTF-16 или UTF-32 быть длиной 3 байта, но это может быть 6 байт (или 12 байт) длиной, если она представлена ​​с помощью трех кодовых точек в кодировке UTF-16 или UTF-32. Что приводит нас к нормализации (или нет).

Но если вы дело только со строками в виде целых вещей, все это очень просто — вы просто строку, записать его в файл, а затем прочитать его обратно в Вам не придется беспокоиться о тонкой печати струнного дисплея. и манипуляции, что это все обрабатывается операционной системы и библиотек. Strings.LoadFromFile (имя) и Listbox.Items.Add (строка) работают точно так же в D2010 как в D2007, то юникода материал все прозрачно для вас в качестве программиста.

  • Если вы хотите работать с юникод строк внутри модуля только — использовать UnicodeString тип (*).
  • Если вы хотите общаться с COM или с другими целями кросс-модуль — использование WideString типа.

Вы видите, WideString это особый тип, так как это не родной тип Delphi. Это псевдоним / оболочка для BSTR — типа системы струн, intendent для использования с COM или кросс-модуля связи. Будучи юникода — это просто побочный эффект.

С другой стороны, AnsiString и UnicodeString — являются носителями типа Delphi, которые не имеют аналогов в других языках. String это всего лишь псевдоним либо AnsiString или UnicodeString .

Так что , если вам нужно передать строку в какой — то другой код — использование WideString , в противном случае — использовать либо AnsiString или UnicodeString . Просто.

(*) Для старого Delphi — просто место

где-то в вашем коде. Это исправление позволит вам написать один и тот же код для любой версии Delphi.

WideString — Тип Delphi

@param ws Unicode string.

@param codePage Code page to be used in conversion.

@returns Converted ansi string.

function WideStringToString( const ws: WideString; codePage: Word): AnsiString;

WC_COMPOSITECHECK or WC_DISCARDNS or WC_SEPCHARS or WC_DEFAULTCHAR,

@ws[ 1 ], — 1 , nil , 0 , nil , nil );

SetLength(Result, l — 1 );

WC_COMPOSITECHECK or WC_DISCARDNS or WC_SEPCHARS or WC_DEFAULTCHAR,

@ws[ 1 ], — 1 , @Result[ 1 ], l — 1 , nil , nil );

Взято с сайта http://www.swissdelphicenter.ch/en/tipsindex

Более новые статьи:

Более старые статьи:

Случайные материалы:

Добавить комментарий

Не использовать не нормативную лексику.

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

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!

Какие строковые типы существуют в Delphi, и чем они отличаются друг от друга?

В Delphi 1.0 существовал лишь единственный строковый тип String, полностью эквивалентный одноименному типу в Turbo Pascal и Borland Pascal. Однако, этот тип имеет существенные ограничения. Для обхода этих ограничений, в Delphi 2, разработчики из Borland устроили небольшую революцию. Теперь, начиная с Delphi 2, имеются три фундаментальных строковых типа: ShortString, AnsiString, и WideString. Кроме того, тип String теперь стал логическим. Т.е., в зависимости от настройки соответствующего режима компилятора (режим больших строк), он приравнивается либо к типу ShortString (для совместимости со старыми программами), либо к типу AnsiString (по умолчанию). Управлять режимом, можно используя директиву компиляции <$LONGSTRINGS ON/OFF>(короткая форма <$H+/->) или из окна настроек проекта – вкладка «Compiler» -> галочка «Huge strings». Если режим включен, то String приравнивается к AnsiString, иначе String приравнивается ShortString. Из этого правила есть исключение: если в определении типа String указан максимальный размер строки, например String[25], то, вне зависимости от режима компилятора, этот тип будет приравнен к ShortString соответствующего размера.

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


Существуют различия между типами AnsiString и WideString. Эти типы имеют практически одинаковую реализацию, и отличаются лишь тем, что WideString используется для представления строк в кодировке UNICODE использующей 16-ти битное представление каждого символа (WideChar). Эта кодировка используется в тех случаях когда необходима возможность одновременного присутствия в одной строке символов из двух и более языков (помимо английского). Например, строк содержащих одновременно символы английского, русского и европейских языков. За эту возможность приходится платить – размер памяти, занимаемый такими строками в два раза больше размера, занимаемого обычными строками. Использование WideString встречается не часто, поэтому, я буду в основном рассказывать о строках типа AnsiString. Но, поскольку они имеют одинаковую реализацию, почти все сказанное относительно AnsiString будет действительно и для WideString, естественно с учетом разницы в размере каждого символа.

Тоже самое касается и разницы между pChar и pWideChar.

Строковый тип AnsiString, обычно используется для представления строк в кодировке ANSI, или других (например OEM) в которых для кодирования одного символа используется один байт (8 бит). Такой способ кодирования называется single-byte character set, или SBCS. Но, очень многие не знают о существовании еще одного способа кодирования многоязычных строк (помимо UNICODE) используемого в системах Windows и Linux. Этот способ называется multibyte character sets, или MBCS. При этом способе, некоторые символы представляются одним байтом, а некоторые, двумя и более. В отличие от UNICODE, строки, закодированные таким способом, требуют меньше памяти для своего хранения, но требуют более сложной обработки. Так вот, строковый тип AnsiString может использоваться для хранения таких строк.

Также существуют еще типы pChar (pWideChar) и array [. ] of Char, они очень часто используются в сочетании со строковыми типами.

Итак, основные характеристики строковых типов:

Тип Максимальный размер строки Размер переменной Объем памяти, требуемый для хранения строки
String[n] где 0

Есть ещё – оператор @ (получение указателя) для переменной такого типа возвращает значение типа pChar. Это очень удобно, поскольку переменные этого типа очень часто используются как буфер при работе с функциями Windows API. Например:

var a :array[0..20] of Char;. GetModuleFileName(GetModuleFileName(HInstance,@a,SizeOf(a));

Здесь, функция GetModuleFileName возвращает результат в массив a.

PChar

Этот тип широко используется в языках C и C++. В Delphi, это не фундаментальный тип, а производный. Его определение выглядит так:

Т.е. переменные этого типа являются указателем, поэтому и имеют размер 4 байта. Формально, значение pChar может указывать как на один символ, так и на строку символов. Однако, общепринято что значения pChar указывают на строки, завершающиеся символом с кодом 0 (#0). В DOSе, такие строки назывались ASCIIZ, но чаще можно встретить название null-terminated string. Наличие такого «концевика» позволяет легко определить реальный размер строки на которую указывает значение pChar.

Не смотря на то, что формально pChar это указатель на Char (^Char), как это часто бывает в Delphi, тип pChar имеет несколько особенностей по сравнению с другими указателями. Таких особенностей несколько.

Первая, заключается в том, что константы, и глобальные переменные этого типа могут быть проинициализированы строковой константой. Вот пример:

const pc :pChar =’abc’;var pv :pChar =’abc’;

Эти строки, определяют константу pc и переменную pv типа pChar. При этом, и pc и pv указывают на разные области памяти, но содержащие одинаковые значения, состоящие из трех символов: ‘a’, ‘b’, ‘c’, и символа #0. Завершающий символ с кодом 0 компилятор добавил автоматически.

Вторая особенность в том, что к переменным типа pChar применимо обращение как к массиву символов. Например, если есть приведенные выше определения, тогда:

C := pv^; // C будет присвоен символ ‘a’. Это обычное обращениеC := pv[0]; // необычно, но С станет равным ‘a’C := pv[1]; // С станет равным ‘b’C := pv[2]; // С станет равным ‘c’C := pv[3]; // С станет равным #0C := pv[4]; // ОШИБКА!

Символ с индексом 3 отсутствует в строке, однако, там есть завершающий ее символ с кодом 0. Именно он будет результатом pv[3]. О pv[4] тоже стоит сказать особо. Дело в том, что компилятор не даст ошибки при компиляции, поскольку на этапе компиляции он, в общем случае, не известен реальный размер строки, на которую указывает переменная pv. Однако, на этапе выполнения программы, такое обращение может вызвать ошибку нарушения доступа к памяти (Access Violation). А может и не вызвать, но результатом будет неопределённое значение. Все зависит от «расклада» в памяти. Поэтому, при таком способе обращения необходимо быть внимательным, и выполнять все необходимые проверки, исключающие выход за размеры строки.

Третья и последняя особенность типа pChar в том, что к значениям этого типа применима так называемая адресная арифметика. Тем, кто программирует на C и C++ она хорошо знакома. Суть её в том, что значения pChar можно увеличивать, уменьшать, вычитать, и складывать. Для демонстрации использования этой особенности, приведем пример реализации функции подсчитывающей длину строки, указатель на которую передается в качестве параметра.

function StringLength (p :pChar) :Cardinal;begin Result := 0; if p = nil then Exit; while p^ <> #0 do begin Inc(Result); Inc(p); end;end;

Здесь важно обратить внимание на два нюанса.

Первый, это проверка переданного указателя на nil. Такая проверка необходима, поскольку очень часто, для обозначения пустой строки используется значение nil.

Второй, это оператор Inc(p) – он «продвигает» указатель на следующий символ. Можно было бы записать его и так: p := p + 1.

Что бы продемонстрировать вычитание указателей pChar, приведем еще один вариант реализации той же функции:

function StringLength (p :pChar) :Cardinal;var pp :pChar;begin Result := 0; pp := p; if pp <> nil then while pp^ <> #0 do Inc(pp); Result := (pp-p);end;

Здесь, выражение pp-p дает «расстояние» между указателями, т.е. число символов между символом, на который указывает указатель p (начало строки) и символом, на который указывает указатель pp (завершающий строку #0).

ShortString, и String[n]

ShortString является частным случаем String[n], а если быть более точным, он полностью эквивалентен String[255].

Если посмотреть на таблицу, где приведены характеристики переменных этого типа, то мы увидим что их размер на один байт больше чем размер хранимой в них строки. Но в данном случае, это не для завершающего символа, как в pChar. Здесь в дополнительном байте хранится текущий размер строки. Кроме того, этот байт располагается не в конце строки, а наоборот, в ее начале. К примеру, если имеется следующее определение:

Оно означает, что для переменной s будет статически выделена область памяти размером 5 байт.

Теперь, выполнение оператора s := ‘abc’, приведёт к тому, что содержимое этих пяти байт станет следующим: байт 1 = 3, байт 2 = ‘a’, байт 3 = ‘b’, байт 4 = ‘c’, а значение байта 5 будет неопределённо – оно будет зависеть от «расклада» в памяти. Т.е., первый символ строки будет находиться во втором байте. Это неудобно, поэтому к символам строк ShortString принято индексироваться, начиная с 1. Следовательно:

s[1] = ‘a’ – первый символ строкиs[2] = ‘b’ – второй символ строкиs[3] = ‘c’ – третий символ строкиs[4] = все что угодно :).

А как же байт длины? Да все очень просто, к нему можно обратиться как s[0]. Только вот есть маленькая проблемка. Поскольку элементами строки являются символы, то и тип значения s[0] тоже будет символ. Т.е., если Вы хотите получить длину строки в виде целого числа, как это принято у нормальных людей, то надо выполнить соответствующее преобразование типа: Ord(s[0]) = 3 – размер строки.

Почему для типа String[n] существует ограничение 0 0, поэтому то Delphi и не освобождает память, занятую строкой.

Еще, этот счётчик используется и для разрешения проблем, связанных со следующей ситуацией:

procedure ShowInteger;var s1 :AnsiString; s2 : AnsiString; n :Integer;begin n := 123; s1 := ‘abc’+IntToStr(n); s2 := s1; s2[1] := ‘X’;end;

Здесь, как мы уже знаем, после выполнения оператора s2 := s1, обе переменные указывают на один и тот же экземпляр строки ‘abc123’. Однако, что же произойдёт когда выполниться оператор s2[1] := ‘X’? Казалось бы, в единственном имеющимся в нашем распоряжении экземпляре строки первая буква будет заменена на ‘X’. И как следствие, обе строки станут равными ‘Xbc123’. s1 то за что «страдает»? Но, к счастью это не так. Здесь на помощь Delphi вновь приходит счетчик ссылок. Delphi, при выполнении этого оператора понимает, что строка на которую указывает s2 будет изменена, а это может повлиять на других. Поэтому, перед изменением строки, проверяется ее счётчик ссылок. Обнаружив, что на нее ссылается более одной строковой переменной, делается следующее: создается копия этой строки со счётчиком ссылок равным 1, и адрес этой копии, присваивается s2; У исходного экземпляра строки, счетчик ссылок уменьшается на 1 – ведь s2 на неё теперь не ссылается. И лишь после этого, происходит изменение первой буквы, теперь уже собственного экземпляра строки. Т.е., по окончанию выполнения этого оператора, в памяти будут находиться две строки: ‘abc123’ и ‘Xbc123’. Причем, s1 будет ссылаться на первую, а s2 на вторую.

При работе со строками определенными как константы, алгоритм работы несколько отличается.

Пример:

procedure ShowInteger;var s :AnsiString;begin s := ‘Вася’; ShowMessage(s);end;

Казалось бы, при завершении работы процедуры, экземпляр строки ‘Вася’ должен быть уничтожен. Но в данном случае это не так. Ведь, при следующем входе в процедуру, для выполнения присваивания нужно будет вновь где-то взять строку ‘Вася’. Для этого, ещё при компиляции, Delphi размещает экземпляр строки ‘Вася’ в области констант программы, где её даже невозможно изменить, по крайней мере, простыми методами. Но как же при завершении процедуры определить что строка ‘Вася’ – константная строка, и ее нельзя уничтожать? Все очень просто. Для константных строк, счётчик ссылок устанавливается равным -1. Это значение, «выключает» нормальный алгоритм работы со «счётчиком ссылок». Он не увеличивается при присваивании, и не уменьшается при уничтожении переменной. Однако, при попытке изменения переменной (помните s2[1]:=’X’), значение счётчика равное -1 будет всегда считаться признаком того, что на строку ссылается более одной переменной (ведь он не равен 1). Поэтому, в такой ситуации всегда будет создаваться уникальный экземпляр строки, естественно, без декремента счётчика ссылок старой. Это защитит от изменений экземпляр строки-константы.

К сожалению, этот алгоритм срабатывает не всегда.

Где же Delphi хранит «счётчик ссылок»? Причем, для каждой строки свой! Естественно, вместе с самой строкой. Вот что представляет собой эта область памяти, хранящая экземпляр строки ‘abc’:

Байты с 1 по 4 Счётчик ссылок равный -1
Байты с 5 по 8 Длина строки равная 3
Байт 9 Символ ‘a’
Байт 10 Символ ‘b’
Байт 11 Символ ‘c’
Байт 12 Символ с кодом 0 (#0)

Для удобства работы с такой структурой, когда строковой переменной присваивается ссылка на эту строку, в переменную заносится адрес не начала этой структуры, а адрес её девятого байта. Т.е. адрес начала реальной строки (прямо как pChar). Для того, что бы приблизиться к реальной жизни, перепишем приведённую структуру:

Смещение Размер Значение Назначение
-8 -1 Счётчик ссылок
-4 Длина строки
‘a’
‘b’
‘c’
#0

С полем по смещению -8, нам уже должно быть все ясно. Это значение, хранящееся в двойном слове (4 байта), тот самый счетчик, который позволяет оптимизировать хранение одинаковых строк. Значение этого счетчика имеет тип Integer, т.е. может быть отрицательным. На самом деле, используется лишь одно отрицательное значение – «-1», и положительные значения. 0 не используется.

Теперь, обратим внимание на поле, лежащее по смещению -4. Это, четырёхбайтовое значение длинны строки (почти как в ShortString). Думаю, Вы заметили, что размер памяти выделенной под эту строку не имеет избыточности. Т.е. компилятор выделяет под строку минимально необходимое число байт памяти. Это конечно хорошо, но, при попытке «нарастить» строку: s1 := s1 + ‘d’, компилятору, точнее библиотеке времени исполнения (RTL) придется перераспределить память. Ведь теперь строке требуется больше памяти, аж на целый байт. Для перераспределения памяти нужно знать текущий размер строки. Вероятно, именно для того, что бы не приходилось каждый раз сканировать строку, определяя её размер, разработчики Delphi и включили поле длины, строки в эту структуру. Длина строки, хранится как значение Integer, отсюда и ограничение на максимальный размер таких строк – 2 Гбайт. Кстати, именно потому, что память под эти строки выделяется динамически, они и получили ещё одно свое название: динамические строки.

Ещё немного о нескольких особенностях переменных AnsiString. Важнейшей особенностью значений этого типа является возможность приведения их к типу Pointer. Это впрочем, естественно, ведь в «душе» они и есть указатели, как бы они этого не скрывали. Например, если описаны переменные: s :AnsiString и p :Pointer. То выполнение оператора p := Pointer(s) приведет к тому, что переменная p станет указывать на экземпляр строки. Однако, при этом, очень важно знать: счетчик ссылок этой строки не будет увеличен.

Поскольку, переменные этого типа реально являются указателями, то для них и реально такое значение как Nil – указатель в «никуда». Это значение в переменной типа AnsiString по смыслу приравнивается пустой строке. Более того, чтобы не тратить память и время на ведение счётчика ссылок, и поля размера строки всегда равного 0, при присваивании пустой строке переменной этого типа, реально, присваивается значение Nil. Это не очевидно, поскольку обычно не заметно, но как мы увидим позже, очень важная особенность.

Преобразование строк из одного типа в другой

Преобразование между «настоящими» строковыми типами String[n], ShortString, и AnsiString выполняются легко, и прозрачно. Никаких явных действий делать не надо, Delphi все сделает за Вас. Надо лишь понимать, что в маленькое большое не влезает. Например:

var s3 :String[3]; s :AnsiString;. s := ‘abcdef’; s3 := s;

В результате выполнения этого кода, в переменной s3 окажется строка ‘abc’, а не ‘abcdef’. С преобразованием из pChar в String[n], ShortString, и AnsiString, тоже всё очень не плохо. Просто присваивайте, и все будет нормально.

Сложности начинаются тогда, когда мы начинаем преобразовывать «настоящие» строковые типы в pChar. Непосредственное присваивание переменным типа pChar значений строк не допускается компилятором. На оператор p := s где p имеет тип pChar, а s :AnsiString, компилятор выдаст сообщение: «Incompatible types: ‘String’ and ‘PChar'» — несовместимые типы ‘String’ и ‘PChar’. Чтобы избежать такой ошибки, надо применять явное приведение типа: p := pChar(s). Так рекомендуют разработчики Delphi. В общем, они правы. Но, если вспомнить, как хранятся динамические строки — с нулем в конце, как и pChar. А еще и то, что к AnsiString применимо преобразование в тип Pointer. Станет очевидным, что всего, возможно целых три способа преобразования строки в pChar:

var s :AnsiString; p1,p2,p3 :PChar;. p1 := pChar(s); p2 := Pointer(s); p3 := @(s[1]);

Все они, синтаксически правильны. И кажется, что все три указателя (p1, p2 и p3) будут в результате иметь одно и то же значение. Но это не так. Всё зависит от того, что находится в s. Если быть более точным, равно ли значение s пустой строке, или нет:

s <> »p1 = p2 <> p3s = »p1 <> p2 = p3

Чтобы была понятна причина такого явления, опишем, как Delphi выполняет каждое из этих преобразований. Переменные AnsiString представляющие пустые строки, реально имеют значение Nil. Так вот:

Для выполнения преобразования pChar(s), компилятор генерит вызов специальной внутренней функции @LstrToPChar. Эта функция проверяет – если строковая переменная имеет значение Nil, то вместо него, она возвращает указатель на реально размещенную в памяти пустую строку. Т.е. pChar(s) никогда не вернет указатель равный Nil.

Тут все просто, такое преобразование просто возвращает содержимое строковой переменной. Т.е. если она при пустой строке содержит Nil, то и результатом преобразования будет Nil. Если же строка не пуста, то результатом будет адрес экземпляра строки.

Здесь, все совсем по-другому. Перед выполнением такого преобразования, компилятор вставляет код, обеспечивающий ситуацию, когда указатель, хранящийся в s, будет единственным указателем на экземпляр строки в памяти. В нашем примере, если строка не пуста, то будет создана копия исходной строки. Вот ее-то адрес и будет возвращен как результат такого преобразования. Но, если строка пуста, то результатом будет Nil, как и во втором случае.

Теперь, интересно отметить, что если в приведенном примере, преобразование p3 := @(s[1]) выполнить первым, то при не пустой строке в s, все указатели (p1, p2, и p3), будут равны. И содержать они будут адрес «персонального» экземпляра строки.

Вот, теперь, зная, как выполняются те или иные преобразования, Вы сможете всегда выбрать подходящий способ.

Приведем пример. В нем, преобразование, рекомендуемое разработчиками, приводит к «странному» поведению программы:

procedure X1;var s :AnsiString; p :PChar;begin s := ‘abcd’; p := PChar(s); p^ := ‘X’; // 0 then PChar(S)[0] := ‘q’; //.

вызовет ошибку нарушения доступа при передаче в нее строки сrefCnt = -1.

Чтобы получить уникальную ссылку для строки, состоящей из некоторой последовательности символов, можно воспользоваться функцией UniqueString.Это позволяет ускорить вычисления со строками, так как при этом можно будет сравнивать строки, просто сравнивая указатели на них. У таких строк refCntвсегда равен 1.

Примеры

Пример 1.

В символьной строке подсчитать количество цифр, предшествующих первому символу «!»

+
+
Начало
s
K:=0; I:=1;
I ’!’
S(i)>=’0’ and s(i) ’!’) Do

If (S[I]>=’0’) And (S[i] а, тт -> т ).

1.Имеется строка, содержащая буквы латинского алфавита и цифры. Вывести на экран длину наибольшей последовательности цифр, идущих подряд.

2. Дана строка, содержащая текст, заканчивающийся точкой. Вывести на экран слова, содержащие хотя бы одну букву о.

1. Дан набор слов, разделенных точкой с запятой (;). Набор заканчивается двоеточием (:). Определить, сколько в нем слов, заканчивающихся буквой а.

2. Дана строка, заканчивающаяся точкой. Подсчитать, сколько запятых в строке.

Последнее изменение этой страницы: 2020-07-16; Нарушение авторского права страницы

Заметки о Pascal, Delphi и Lazarus

Следует ожидать переводов разделов справочной системы Delphi, компиляций из учебников, переводы статей, «путевые заметки» и прочие интересности. Блог прежде всего ориентирован на студентов, но опытных людей я тоже буду рад видеть;-)

четверг, 28 апреля 2011 г.

Строковые типы

О строковых типах

Строка представляет собой последовательность символов. Delphi поддерживает следующие предопределенные строковые типы:

2^31 символов

2^30 символов

2^30 символов

Тип Максимальная длина Требует памяти Используется для
ShortString 255 символов От 2 до 256 байт Обратной совместимости
AnsiString От 4 байт до 2GB 8-битные (ANSI) символы, DBCS ANSI, MBCS ANSI, символы Unicode и т.д.
UnicodeString От 4 байт до 2GB Символы Unicode, 8-битные (ANSI) символы, многопользовательские серверы и приложения с поддержкой нескольких языков
WideString От 4 байт до 2GB Символы Unicode; многопользовательские серверы и приложения с поддержкой нескольких языков. Использование UnicodeString обычно более предпочтительно, чем использование WideString (кроме приложений COM).

Замечание: Строковый тип по умолчанию — UnicodeString. Тип WideString предназначен для совместимости с типом COM BSTR. В общем случае следует использовать UnicodeString для не-COM приложений. Для большей части задач тип UnicodeString является предпочтительным. Тип string является псевдонимом для типа UnicodeString.

Строковые типы могут смешиваться при присваивании и построении выражений, — компилятор автоматически производит необходимые преобразования. Однако строки, передаваемые по ссылке в функцию или процедуру (как параметры var и out) должны соответствовать по типу формальным параметрам. Строки могут быть явным образом преобразованы в строки других типов. Тем не менее, преобразование мультибайтовой строки в однобайтовую строку может привести к потере данных.

Есть несколько особых строковых типов, о которых следует упомянуть:

  • Тип AnsiString зависит от кодовой страницы и объявлен следующим образом Это тип AnsiString, который имеет возможность обрабатывать свои внутренние данные в определенной кодовой странице.
  • Тип RawByteString это тип AnsiString($FFFF). RawByteString позволяет передавать строковые данные любой кодовой страницы без выполнения преобразований кодовой страницы. RawByteString должен использоваться только как параметр const, параметр-значение или значение, возвращаемое функцией. Никогда не следует передавать его по ссылке и создавать экземпляры этого типа путем объявления переменных.
  • UTF8String представляет собой строку, закодированную при помощи UTF-8 (переменное количество байт Unicode). Это тип зависит от кодовой страницы и представляет собой AnsiString с кодовой страницой UTF-8.

Зарезервированное слово string действует как общий идентификатор строковых типов. Например:

создает переменную S, которая содержит строку. На платформе Win32, компилятор интерпретирует тип string (когда он встречается в коде без числа в квадратных скобках) как UnicodeString.

На платформе Win32, вы можете использовать директиву <$H->для переключения от типа string к ShortString. Потенциально это полезный прием для работы со старым 16-битным кодом Delphi или Turbo Pascal в ваших новых программах.

Следует учесть, что ключевое слово string так же используется для объявления типов ShortString с заранее определенной длинной.

Сравнение строк выполняется путем определения порядковых номеров элементов стоящих на соответствующих позициях. При сравнении строк неравной длины, каждый символ в более длинной строке при отсутствии соответствующего символа в более короткой строке считается за большее значение. Например, ‘AB’ больше, чем ‘A’; то есть выражение ‘AB’ > ‘A’ возвращает значение True. Строки с нулевой длинной представляют собой наименьшие значения при сравнении.

Вы можете индексировать строковую переменную так, как будто вы обрабатываете массив. Если S – это строка типа, отличного от UnicodeString, а i – это целочисленное выражение, то S[i] представляет i-ый байт в S, который может не быть i-ым символом или вообще не быть целым символом в том случае, если вы вы работаете с мультибайтовой строкой (MBCS). Таким же образом индексирование переменной типа UnicodeString может не обеспечить доступ к конкретному символу. Если строка содержит символы в Базовом Многоязыковом Плане (Basic Multilingual Plane (BMP)), все символы являются двухбайтовыми, а индексирование обеспечит доступ к символам строки. Однако, если некоторые символы не присутствуют в BMP, индексируемый элемент может не оказаться суррогатной парой – целым символом.

Стандартная функция Length возвращает количество элементов в строке. Как было указано, количество элементов не обязательно равно количеству символов. Процедура SetLength устанавливает длину строки. Следует учесть, что функция SizeOf возвращает количество байт, необходимых для представления переменной или типа, и только для типов ShortString это число будет равно количеству символов. Для всех остальных типов строк функция SizeOf возвращает количество байт в указателе (поскольку эти типы являются указателями).

Для типов ShortString или AnsiString S[i] является значением типа AnsiChar. Для W второму символу в MyString. В следующем примере используется стандартная функция UpCase для преобразования значения MyString в заглавные буквы:

Будьте внимательны индексируя строки таким образом, поскольу перезапись конца строки может привести к ошибкам доступа (access violation). Кроме того, следует избегать передачи индексов строк как параметров var, поскольку это снижает эффективность кода.

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

Короткие строки

Переменные типа ShortString могут хранить от 0 до 255 однобайтовых символов. Поскольку длина ShortString может динамически изменяться, в памяти статически выделяется 256 байт, при этом первый байт содержит длину строки, а оставшиеся 255 доступны для хранения символов. Если переменная S имеет тип ShortString, то Ord(S[0]), как и Length(S), возвращаяет длину S; присваивание значения S[0], как и вызов SetLength, изменяет длину S. ShortString поддерживается только для обратной совместимости.


Язык Delphi поддерживает короткострочные типы — фактически, подтипы типа ShortString – в которых максимальная длина строки может быть от 0 до 255 символов. Количество символов определяется по числу, указываемому в квадратных скобках и идущему после зарезервированного слова string. Например:

создает переменную с именем MyString, максимальная длина строки у которой – 100 символов. Это эквивалентно объявлениям:

Переменные, объявленные таким образом, занимают в памяти столько места, сколько в точности требуется для хранения их значений. То есть указанная максимальная длина строки плюс один байт. В нашем примере MyString использует 101 байт по сравнению с 256 байтами для переменной предопределенного типа ShortString.

Если при присваивании значения переменной типа ShortString, длина значения превышает максимальную длину строки для типа, — значение обрезается.

Стандартные функции High и Low работают с идентификаторами короткострочных типов и переменными этих типов. High возвращает максимальную длину строки, а Low возвращает ноль.

Тип AnsiString

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

Переменная типа AnsiString – это структура, содержащая строковую информацию. Когда переменная пуста (то есть содержит строку нулевой длины), указатель имеет значение nil и строка не использует никакого дополнительной памяти для хранения. Когда переменная непуста, она указатель указывает на динамически выделенный блок памяти, содержащий строковое значение. Эта память динамически выделена в куче, но управление ею происходит полностью автоматически и не требует дополнительного пользовательского кода. Структура типа AnsiString содержит 32-битный индикатор, 32-битный счетчик ссылок, 16-битный индикатор для количества байт на символ и 16 бит на кодовую страницу.

Тип AnsiString представляет собой однобайтовую строку. С однобайтовыми наборами символов (single-byte character set (SBCS)) каждый байт в строке представляет собой один символ. В мультибайтовых наборах символов (multibyte character set (MBCS)) элементы являются однобайтовыми, при этом одни символы представляются одним байтом, а другие – более чем одним байтом. Мультибайтовые наборы символов (в особенности двухбайтовые (double-byte character sets (DBCS))) широко используются для азиатских языков. Строка типа AnsiString может содержать символы MBCS.

Доступ к символам AnsiString по индексу начинается с единицы. Индексирование многобайтовых строк ненадежно, поскольку S[i], представляет i-ый байт (не обязательно i-ый символ) в S. Тем не менее, стандартные функции для работы с AnsiString имеют мультибайтовые компоненты, которые так же реализуют сортировку символов по региональному стандарту.(Имена мультибайтовых функций обычно начинаются с префикса Ansi, например, мультибайтовая версия StrPos — AnsiStrPos.) Поддержка мультибайтовых символов зависит от операционной системы и базируется на локальных настройках.

Поскольку переменные типа AnsiString имеют указатели, две или более переменных может ссылаться на одно и то же значение без потребления дополнительной памяти. Компилятор функционирует таким образом для экономии ресурсов и более быстрого выполнения инструкций присваивания. При разрушении значения переменной или при выполнении присваивания, счетчик ссылок для предыдущего значения переменной уменьшается, а счетчик ссылок для нового значения – увеличивается (если таковое существует) – увеличивается. Когда счетчик ссылок для строки становится равным нолю, память высвобождается. Этот процесс называется подсчетом ссылок. Когда индексирование используется для изменения значения единичного символа в строке, производится копирование строки (но только в том случае, когда счетчик ссылок больше единицы). Это называется семантикой копирования при записи.

Тип UnicodeString

Тип UnicodeString представляет собой динамически изменяемые строки Unicode, максимальная длина которых ограничивается только доступной памятью.

В наборе символов Unicode каждый символ представлен одним или несколькими байтами. Unicode имеет несколько форматов преобразования (Unicode Transformation Formats) которые используют разные, но эквивалентные друг другу кодировки символов, которые могут быть легко преобразованы друг в друга.

  • В UTF-8, например, символы могут занимать от 1 до 4 байт. В UTF-8 первые 128 символов Unicode предназначены для символов US-ASCII.
  • UTF-16 – еще одна широко используемая кодировка Unicode, в которой символы занимают 2 или 4 байта.Большая часть мировых символов входит в Basic Multilingual Plane и может быть представлена 2 байтами. Остальные символы, которые требуют 2 байта, известны как суррогатные пары (surrogate pairs).
  • В UTF-32 для представления каждого символа требуется 4 байта.

Платформа Win32 поддерживает как однобайтовые и мультибайтовые наборы символов, так и Unicode. Операционная система Windows поддерживает UTF-16.

Тип UnicodeString имеет точно такую же структуру, как и AnsiString. Данные UnicodeString кодируются в UTF-16.

Поскольку UnicodeString и AnsiString имеют одинаковую структуру, они работают похожим образом. Когда переменная UnicodeString непуста, она указывает на динамически выделенный блок памяти, который содержит строковое значение, и работа с памятью для этого – прозрачна для пользователя. Для переменных UnicodeString ведется подсчет ссылок. Две и более переменные могут ссылаться на одно и тоже значение, не потребляя дополнительной памяти.

Экземпляры UnicodeString могут индексировать символы. Индексирование начинается с 1, так же как и для AnsiString.

Тип UnicodeString совместим по присваиванию с остальными строковыми типами. Тем не менее, при присваивание между AnsiString и UnicodeString выполняется соответствующие преобразования. Следует учесть, что присваивание значения типа UnicodeString переменной типа AnsiString не рекомендовано и может повлечь за собой потерю данных.

Delphi так же поддерживает символы и строки Unicode для типов WideChar, PWideChar и WideString.

Тип WideString

Тип WideString представляет собой динамически изменяемую строку, состоящую из 16-битных символов Unicode. В некотором смысле он схож с AnsiString. На платформе Win32, WideString совместим с типом COM BSTR.

WideString подходит для использования в приложениях COM. Тем не менее, WideString не имеет счетчиков ссылок, что для остальных типов приложений тип UnicodeString является более гибким и эффективным.

Индексирование WideString ненадежно, поскольку S[i] представляет iый элемент (не обязательно iый символ) в S.

Для Delphi типы Char и PChar аналогичны типам WideChar и PWideChar, соответственно.

Работа со строками, завершающимися нулевым символом

Множество языков программирования, включая C и C++, не имеют специальных типов для работы со строками. Эти языки и среды, которые разработаны с их помощью, работают со строками, заканчивающимися нулевым символом. Строки, завершающиеся нулевым символом – это массивы символов (индексация начинается с нулевого символа), последний элемент в которых — NUL (#0). Поскольку массив не имеет индикатора длины, первый символ NUL помечает конец строки. Вы можете воспользоваться конструкциями Delphi и специальными подпрограммы в модуле SysUtils для работы с такими строками в тех случаях, когда вы хотите работать с системами, которые их используют.

Следующие объявления типов могут быть использованы для хранения строк, завершающихся нулевым символом:

При включенной директиве расширенного синтаксиса (<$X+>) вы можете присваивать значения строковых констант статическим символьным массивам с индексацией от нуля. (Динамические массивы для этой цели не подойдут.) Если вы инициализируете константу-массив, содержащую строку, которая короче, чем объявленная длина массива, оставшиеся символы устанавливаются в #0.

Использование указателей, массивов и строковых констант

Для работы со строками, завершающихся нулевым символом, часто бывает необходимо использовать указатели. Строковые константы совместимы по присваиванию с типами PChar и PWideChar, которые представляются указателями на массивы значений Char и WideChar, завершающиеся нулевым символом. Например:

P указывает на область памяти, которая содержит завершающуюся нулевым символом копию ‘Hello world!’ Это эквивалентно:

Вы можете также передавать строковые константы в любую функцию, принимающую параметры-значения или параметры-константы типа PChar или PWideChar – например, StrUpper(‘Hello world!’). Как и при присваивании типу PChar, компилятор генерирует копию строки, оканчивающуюся нулевым символом, и передает функции указатель на эту копию. Наконец, вы можете инициализировать константы (отдельно взятые или в составе структурированных типов) типа PChar или PWideChar строковыми литералами. Примеры:

Индексируемые от ноля символьные массивы совместимы с типами PChar и PWideChar. Когда вы используете массив символов вместо указателя, компилятор преобразует массив в константу указательного типа, значение которой соответствует адресу первого элемента массива. Например:

Этот код дважды вызывает SomeProcedure, передавая ей одно и то же значение.

Символьный указатель может быть проиндексирован как массив. В предыдущем примере MyPointer[0] возвращает H. Индекс указывает на смещение, добавляемое к указателю перед получением значения. (Для переменных типа PWideChar индекс автоматически умножается на два.) Таким образом, если P – это символьный указатель, то P[0] эквивалентно P^ и определяет первый символ в массиве, P[1] определяет второй символ и так далее. P[-1] определяет ‘символ’ находящийся слева от P[0]. Компилятор не производит проверки диапазона на таких индексах.

Функция StrUpper иллюстрирует индексирование указателей для проходу по строке, заканчивающейся нулевым символом:

Смешивание строк Delphi и строк, оканчивающихся нулевым символом

Вы можете перемешивать строки (значения типов AnsiString и UnicodeString) и строки, оканчивающиеся нулевым символом (значения типа PChar) в выражениях и инструкциях присваивания, а так же можете передавать значения типа PChar в функции или процедуры, принимающие строковые параметры. Присваивание S := P, где S – строковая переменная, а P – выражение типа PChar, копирует строку, оканчивающуюся нулевым символом в обычную строку.

В двоичной операции, если один операнд является строкой, а второй – значением типа PChar, операнд типа PChar преобразуется в UnicodeString.

Вы можете преобразовывать значение PChar в UnicodeString. Это может быть полезно, если вы хотите выполнить строковую операцию с двумя значениями PChar. Например:

Вы можете также преобразовать строку типа UnicodeString или AnsiString в строку, оканчивающуюся на нулевой символ. При этом будут действовать следующие правила:

  • Если S – это UnicodeString, PChar(S) преобразует S в строку, оканчивающуюся нулевым символом и возвращает указатель на первый символ в S. Такие преобразования используются для Windows API. Например, если Str1 и Str2 – это UnicodeString, вы можете вызвать функцию Win32 API MessageBox следующим образом: Используйте PAnsiChar(S) в том случае, когда S – это строка типа AnsiString.
  • Вы также можете использовать Pointer(S) для преобразования строки в нетипизированный указатель. Но, если S пуста, такое преобразование вернет значение nil.
  • PChar(S) всегда возвращает указатель на блок памяти; если S пуста, возвращается указатель на символ #0.
  • Когда вы преобразуете значение переменной типа UnicodeString или AnsiString в указатель, он действует до тех пор, пока переменной не присваивается новое значение или не выходит из области видимости. Если вы преобразуете любое другое строковое выражение в указатель, он действует только внутри инструкции, в которой выполняется преобразование.
  • Когда вы преобразуете выражение типа UnicodeString или AnsiString в указатель, он должен рассматриваться как доступный только для чтения.

Аналогичные правила действуют при смешивании значений типов WideString и PWideChar.

WideString — Тип Delphi

цитата:
The representation of the classic AnsiString type was the following:
-8: Ref count
-4: length
String reference address: First char of string

In Delphi 2009 the representation for reference-counted strings becomes:
-12: Code page
-10: Elem size
-8: Ref count
-4: length
String reference address: First char of string

Так что вы неправы дважды — 1) что это было «в сильно раньшие времена»; 2) про ссылку на heap. Всё хранится вместе, никаких ссылок.

цитата: bislomet:
olivenoel
а какие из string (http://docwiki.embarcadero.com/RADStudio/XE3/en/String_Types) -ов Вы при этом имеете в виду?
Если речь идет о null-terminated — то они вполне себе user managable, а не automatic. И вполне могут утекать куда угодно.
Уточните!

FastMM (или кто там в ХЕ2), говорит unexpected memory leak UnicodeString x1.

Погуглила немного, оказывается такое имеет место быть, если строка является частью структуры (record). Но есть подозрение на одно место, где в массив AnsiChar присвается литеральное выражение (корое вроде по сути UnicodeString)

5. olivenoel , 26.06.2013 10:09
6. bislomet , 26.06.2013 11:46
Spirit-1
Вы заблуждаетесь. Лучше почитайте help к delphi xe3 (по ссылке, которю я давал выше)
Вы говорите об эпохе «Delphi до XE» — а это таки «сильно раньшие времена»

[pre|AnsiString represents a dynamically allocated string whose maximum length is limited only by available memory.

An AnsiString variable is a structure containing string information. When the variable is empty — that is, when it contains a zero-length string, the pointer is nil and the string uses no additional storage. When the variable is nonempty, it points to a dynamically allocated block of memory that contains the string value. This memory is allocated on the heap.
[/pre]

Добавление от 26.06.2013 11:47:

olivenoel
не путайте строку с array of char, это же не «C»

olivenoel
не путайте строку с array of char, это же не «C»

не буду Но вопрос остается, может ли «потеря» младших (старших) байтов при автоконвертации UnicodeString в AnsiString (такая конвертация тоже имеет место быть в виде

) вести к утечкам?

проблему вроде бы решила. Для одного из классов с Р. в поле и New (для поля) в конструкторе, не был переопределен деструктор, в котором должен быть Dispose для Р. -поля.

7. olivenoel , 26.06.2013 12:09
8. Spirit-1 , 26.06.2013 14:42
bislomet
Причём тут AnsiString? Я вам привёл кусок описания для нового юникодного (!) String. Обратите внимание, что на самом деле подход практически не поменялся. Добавились поля Code Page и Elem Size по сравнению со стрингами до D2009 (ссылаюсь на Канту опять же). Ну и если сравнивать с AnsiString, то, конечно, поле длинны стало четырёхбайтовым.
9. MBo , 26.06.2013 15:19
Spirit-1

Ну и если сравнивать с AnsiString, то, конечно, поле длинны стало четырёхбайтовым.

В Ansistring оно тоже было четырехбайтовым. Возможно, имелись в виду ShortString?

10. Spirit-1 , 26.06.2013 15:23
Да, насчёт длины ерунду сказал. Остальное всё в силе.
11. bislomet , 26.06.2013 15:52
Spirit-1
Вы опять-таки заблуждаетесь. Чуть дальше в том же help написано:
The UnicodeString type has exactly the same structure as the AnsiString type.

Т.е. все, что не является ShortString — устроено именно таким образом — по мнению разработчиков
12. Spirit-1 , 26.06.2013 16:14
bislomet
Попробуйте смотреть чуть дальше хэлпа от XE3.

Повторюсь, что начиная с версии D2009 эти два типа суть одно и то же, поскольку тип AnsiString определен как UnicodeString с фиксированной Code Page.
Я же сравниваю с AnsiString-ом, который был до 2009-ой Delphi. И привёл выдержку из книги — какая структура стринга была до 2009-й версии и какая стала после.

На всякий случай повторю:

цитата:
The representation of the classic AnsiString type was the following:
-8: Ref count
-4: length
String reference address: First char of string

In Delphi 2009 the representation for reference-counted strings becomes:
-12: Code page
-10: Elem size
-8: Ref count
-4: length
String reference address: First char of string

Читайте внимательно, если кроме дойча английским разумеете.

13. bislomet , 26.06.2013 17:38
Spirit-1
Повторюсь, что начиная с версии D2009 эти два типа суть одно и то же, поскольку тип AnsiString определен как UnicodeString с фиксированной Code Page.
Это утверждение никоим образом не противоречит моему и не подтверждает Ваше.
Они таки одинаковые — но обе структуры имеют указатель на heap..

Я как-то больше верю хелпу от разработчиков.
А вот с чтением проблемы скорее у Вас — иначе Вы бы заметили, что я Вам именно что английские ссылки приводил.

Добавление от 26.06.2013 17:50:

Spirit-1
таки вот что Вы имеете в виду: а вот то, что я имею в виду:Здесь: Long String Types (http://docwiki.embarcadero.com/RADStudio/XE4/en/Internal_Data_Formats)
Т.е. Вы говорите о том, как выглядит та структура, об указателе на которую говорю я

14. Imlb , 26.06.2013 19:32
bislomet
об указателе на которую говорю я

Так было всегда или с очень давних времен.

«перед строкой» было в сильно раньшие времена,

И продолжается до сих пор.

15. bislomet , 27.06.2013 08:50
Imlb
Так было всегда или с очень давних времен.

Не всегда, а только после появления в Delphi длинных строк
16. Spirit-1 , 27.06.2013 15:02
А разве в дельфях длинные строки не были с самой первой версии?
17. bislomet , 27.06.2013 15:05
Spirit-1
нет, долгое время макс. длина строки в delphi была 255 символов — то, что впоследствии стало называться ShortString. И вот там как-раз в нулевом байте хранилась длина строки.
Именно для совместимости с «теми еще» строками все нынешние строки в Delphi индексируются с 1.
18. Spirit-1 , 27.06.2013 17:37
bislomet
Посмотрел. Действительно, в первых строки были короткими. Длинные пошли со второй версии. Так что фраза «долгое время макс. длина строки в delphi была 255 символов» не соответствует действительности.
19. bislomet , 27.06.2013 17:54
Spirit-1
Не совсем так — дело в том, что тип string все еще оставался 255-байтным.
а появился длинный AnsiString, который не был синонимом string ни в коем случае!

А сам тип string перестал быть ShortString куда позже, где-то в районе 6й версии.

20. Spirit-1 , 27.06.2013 18:21
Ничего подобного. String в Delphi 2.0 стал = AnsiString, старый String (короткий) стал называться ShortString’ом.

цитата (Spirit-1): А разве в дельфях длинные строки не были с самой первой версии?

Нет, обычные борланд-паскалевской реализации (байт с индексом 0 — длинна строки).

bislomet
Меня больше интересует слово «ANSI», чем 32 битный «длинн». Анси типа означает АНСИ и не хухры-мухры ? Или же нормально в ногу со временем там реально живет UTF8 ?

А от это паноптикум.
-Труп умер 50 лет назад
-Да, но продлим ресурс его креслу еще на 1000 лет. А то и должность завхоза была вакантна.
-Только у нас ANSI завхоз в ANSI кресле.

21. vertur , 28.06.2013 03:49
22. Spirit-1 , 28.06.2013 09:52
vertur
Мы уже этот вопрос отработали . Короткие строки были в первой версии, начиная со второй пошли длинные (по дефолту).
23. AzikAtom , 05.07.2013 11:54
olivenoel

А не в этом ли дело? В помощи написано The New procedure creates a new dynamic variable and sets a pointer variable to point to it. P is a variable of any pointer type. The size of the allocated memory block corresponds to the size of the type that P points to. The newly created variable can be referenced as P^. If there isn’t enough memory available to allocate the dynamic variable, an EOutOfMemory exception is raised. For more information on handling run-time library exceptions, see Handling RTL Exceptions

When an application is finished using a dynamic variable created with New, it should dispose of the memory allocated for the variable using the Dispose standard procedure.
Т.е., при объявлении переменной типа TMyRecType память уже была выделена, а вы её ещё раз выделили и старый указатель потеряли.

24. Spirit-1 , 05.07.2013 13:13
AzikAtom
Не могу утверждать точно (проверить прямо сейчас не могу), но вроде бы если TMyRecType не является указателем, то New(FMyRec) не скомпилируется.

цитата: Spirit-1:
AzikAtom
Не могу утверждать точно (проверить прямо сейчас не могу), но вроде бы если TMyRecType не является указателем, то New(FMyRec) не скомпилируется.

скорее всего там таки был PMyRecType и я ошиблась при перепечатке кода

25. olivenoel , 05.07.2013 13:42
26. AzikAtom , 05.07.2013 14:03
Spirit-1
Написано:


Значит, это не указатель и вручную не надо память брать.

olivenoel
скорее всего там таки был PMyRecType и я ошиблась при перепечатке кода
Вот если бы у вас было:

тогда да, и NEW и последующий DISPOSE, а сейчас можно попробовать убрать этот NEW().
Правда, если вы эти переменные потом собираете в списки, то надо бы сначала сохранить указатель на самую первую переменную, которая создаётся автоматически, и затем восстановить его. Правда, тогда нет смысла объявлять саму переменную и лучше сделать изначально переменную типа ^TMyRecType.

27. CyberStorm , 05.07.2013 17:53
Утечка в Delphi может быть если используется string в структуре record.

например утечка будет:
TMyRecord=record
S:string;
End;

утечки не будет:
TMyRecord=record
S:shortstring; // или S:string[100]
End;

то же справедливо для размещенных в записях массивах,
утечка будет:
TMyRecord=record
A:array of integer;
End;

утечки не будет:
TMyRecord=record
A:array[0..1] of integer;
End;

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

28. olivenoel , 08.07.2013 19:03
что делает Move, кроме переноса данных? Возвращет память из-под перенесенных данных менеджеру памяти?
29. AzikAtom , 08.07.2013 21:31
olivenoel
Нет. Просто копирует.
30. olivenoel , 09.07.2013 10:26
тогда MoveMemory и CopyMemory делают одно и то же? Зачем тогда две ф-ции?
31. Spirit-1 , 09.07.2013 12:15
Посмотрите здесь (http://www.delphimaster.net/view/2-1167297116) , всё станет понятно.

нет, еще больше непонятно. В частности

куда смотрит Move?

32. olivenoel , 09.07.2013 12:30
33. AzikAtom , 09.07.2013 12:44
olivenoel
тогда MoveMemory и CopyMemory делают одно и то же? Зачем тогда две ф-ции?
Видать, для удобства программистов. Щелчок правой кнопкой мыши на MoveMemory и выбрать «Find Declaration».
Открывается реализация:

Добавление от 09.07.2013 12:47:

olivenoel
Кстати, вопрос с утечками закрыт? В чём было дело?

34. olivenoel , 09.07.2013 12:53
вроде закрыт. Где-то был непарный вызов New в конструкторе (без Dispose в диструкторе). Сейчас заменила все указатели на структуры на сами структуры. Осталась где-то одна утечка Unknown x1.

Я попыталась уйти вообще от неуправляемых строк внутри библиотеки. Они есть только в теле экспортируемых ф-ций, где преобразуются в нормальные (alias) string и дальше уже работается с ними. Надо только оттестировать с ComInterop из-под C#

35. Spirit-1 , 09.07.2013 13:07
olivenoel
куда смотрит Move?
По указанной ссылке не всё истина. Фильтруйте немного. Там просто топик обсуждения. Конкретно выражение «Ну как же — процедура Move смотрит, откуда ее вызывают, в зависимости от этого
выполняет разные действия.» в той теме — полный бред, конечно.

AzikAtom
Так что, пользуйтесь move() и не парьтесь — она написана на асме.
Я бы не был столь категоричен. Там под IFDEF либо паскалевская реализация, либо на асме под x86.

36. AzikAtom , 09.07.2013 14:08
Spirit-1
В Delphi 4 такая реализация:

В Delphi 7 ещё не смотрел.

olivenoel
куда смотрит Move?
Он только смотрит, чтобы копирование при наложении было корректным. Т.е., то, что было в источнике гарантированно оказывается в приёмнике.

цитата: Spirit-1:
olivenoel
куда смотрит Move?
По указанной ссылке не всё истина. Фильтруйте немного. Там просто топик обсуждения. Конкретно выражение «Ну как же — процедура Move смотрит, откуда ее вызывают, в зависимости от этого
выполняет разные действия.» в той теме — полный бред, конечно.

Добавление от 09.07.2013 14:13:

В Delphi XE2 реализация посложнее:

37. olivenoel , 09.07.2013 14:11
38. Spirit-1 , 09.07.2013 14:39
AzikAtom
Delphi2010:
39. olivenoel , 09.07.2013 16:49
как правильно организовать обмен строками в длл (с учетом СОМ)? Сигнатура метода такая

в метод приходим с именем xls-файла для конвертации. Уходим — с именем полученного после конвертации файла (т.е. фактически изменяется только расширение, потому длина строки не меняется).

сдается мне подобные методы первые кандидаты на утечки.

40. AzikAtom , 09.07.2013 17:00
Если я правильно понимаю концепцию, то в библиотеке память строки не освобождается, и передаётся в неё только указатель, значит, просто там же поменять символы и при выходе из процедуры строка будет изменённая.

цитата: CyberStorm:
Утечка в Delphi может быть если используется string в структуре record.

например утечка будет:
TMyRecord=record
S:string;
End;

утечки не будет:
TMyRecord=record
S:shortstring; // или S:string[100]
End;

так же посмотрела исходники FastMM — если тип утечки не ссылочный (т.е. класс или строка или юникодстрока), то она сообщается как Unknown.

41. olivenoel , 09.07.2013 19:07
42. CyberStorm , 09.07.2013 23:47
Я что-то не понял вашего термина «не зачищаются»
Перечислимые типы в Delphi по умолчанию имеют размер byte т.е. статический размер и утечек из-за них не может быть, при уничтожении класса память, выделенная под переменные при создании класса, высвобождается автоматически.
В конце Destroy я обычно всегда добавляю inherited — правило хорошего тона

цитата: CyberStorm:
Я что-то не понял вашего термина «не зачищаются»
Перечислимые типы в Delphi по умолчанию имеют размер byte т.е. статический размер и утечек из-за них не может быть, при уничтожении класса память, выделенная под переменные при создании класса, высвобождается автоматически.
В конце Destroy я обычно всегда добавляю inherited — правило хорошего тона

если остаются строки, то остается и енум-поле. Сообщение от FastMM гласит: UnicodeString x2 Unknown x1. Или кратно, если объектов было несколько.

Насчет inherited. Вот смотрю я на деструктор TObject (а вроде от него наследуют все классы, даже если это не официально прописано при объявлении класса)

Добавление от 10.07.2013 10:46:

будет ли утечка в случае

Добавление от 10.07.2013 10:47:

и изменится ли что-то, если стрки сделать короткими?

43. olivenoel , 10.07.2013 10:09
44. CyberStorm , 10.07.2013 10:58
Два раза вызов конструктора?

Инициализацию переменных надо вынести в отдельный метод класса, например Init и вызывать его из конструктора

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

цитата: CyberStorm:
Два раза вызов конструктора?

знаю, что тащить из других языков дурная привычка, но в шарпе, например, можно такое:

цитата: CyberStorm:
Инициализацию переменных надо вынести в отдельный метод класса, например Init и вызывать его из конструктора

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

Добавление от 10.07.2013 11:24:

как грамотно делать этот Init «сквозным» во всех классах иерархии? В базовом сделать его виртуальным, а потом переопределять во всех наследниках, вызывая при этом родительский? Он нигде не потеряется?

45. olivenoel , 10.07.2013 11:11
46. Spirit-1 , 10.07.2013 11:40
olivenoel
как грамотно делать этот Init «сквозным» во всех классах иерархии? В базовом сделать его виртуальным, а потом переопределять во всех наследниках, вызывая при этом родительский? Он нигде не потеряется?
В родительском классе virtual. В дочерних, при необходимости перекрытия метода, override. Нигде не потеряется.

цитата: CyberStorm:
Утечка в Delphi может быть если используется string в структуре record.

например утечка будет:
TMyRecord=record
S:string;
End;

утечки не будет:
TMyRecord=record
S:shortstring; // или S:string[100]
End;

если применить все это к моей Персоне:

создается три экземпляра Персоны. Утечки: UnicodeString x2 TPerson x3 UnicodeString x4

меняю длинную строку на короткую в

создаю опять три экземпляра. Получаю «чистые» утечки TPerson x3. Куда деваются строки, а точнее откуда они повляются в случае длинных строк?

47. olivenoel , 10.07.2013 12:09
48. Spirit-1 , 10.07.2013 14:03
Создание UnicodeString есть вызов GetMem (см. _NewUnicodeString в system.pas).
Ссылки на созданные строки хранятся в некоторой таблице, формат которой в D2010 примерно такой:

У меня есть подозрение, что в некоторый момент срабатывает что-то вроде GC, который проходит по этой таблице и освобождает память из-под тех строк, у которых RefCount == 0. В тестовом примере вполне может случиться так, что «строковой GC» не успевает сработать, а FastMM рапортует о неосвобождённой памяти. Для чистоты эксперимента я бы насоздавал несколько сотен тысяч объектов с большими/длинными строками и сравнил отчеты об утечках на выходе из программы с теоретически рассчитанными. Если «строковой GC» успел сработать, то количество UnicodeString утечек будет отличаться от общего количества созданных строк.

P.S. Тяжело разобраться, когда полных исходников внутреннуй кухни нет. Остаётся только гадать .

49. CyberStorm , 10.07.2013 17:00
olivenoel Взял ваш код, скомпилировал на XE2, запустил — утечек памяти нет.

Даже убрал из Destroy присвоение:
FName.FFName := »;
FName.FLName := »;
утечек все равно нет, компилятор Delphi достаточно интеллектуальный, чтобы освободить из памяти строки в record при уничтожении экземпляра класса

Давайте посмотрим как вы создаете 3 экземпляра класса вариантов других не вижу. И какая версия Delphi у вас?

PS. Это не .NET, освобождение памяти строки происходит сразу при выходи из области видимости переменной или при вызове деструктора для строк определенных в заголовке класса.

цитата: CyberStorm:
olivenoel Взял ваш код, скомпилировал на XE2, запустил — утечек памяти нет.

Даже убрал из Destroy присвоение:
FName.FFName := »;
FName.FLName := »;
утечек все равно нет, компилятор Delphi достаточно интеллектуальный, чтобы освободить из памяти строки в record при уничтожении экземпляра класса

Давайте посмотрим как вы создаете 3 экземпляра класса вариантов других не вижу. И какая версия Delphi у вас?

у меня тоже ХЕ2. Оно заработало без утечек сегодня после 3-ей или 4-ой перекомпиляции. То ли он компайлер старые дцу-шки подхватывал, то ли звезды не сошлись.

50. olivenoel , 10.07.2013 19:32
51. olivenoel , 11.07.2013 16:50
нашла езе одно место, где появляется Unknown утечка (а точнее строка опять же)

Добавление от 11.07.2013 16:58:

и еще, правильно ли я понимаю, что любое присваивание чего либо переменной типа WideString, автоматично вызывает ф-ции аллокации памяти в системном менеджере памяти? Т.е. после ф-ции

тоже надо вызывать StrDispose/SysFreeString?

52. AzikAtom , 11.07.2013 17:33
olivenoel
можно ли побороть утечку, вызывая StrDispose на стороне хост-приложения?
Да. А можно в функцию передавать готовый буфер для заполнения? В winapi так и делают. Тогда контроль за памятью принадлежит вызывающей стороне.

WideString
Так ведь, WideString это как string только для 16 битных символов.

53. Spirit-1 , 11.07.2013 17:38
olivenoel
Вызов Result := »; при типе функции PWideChar выглядит несколько подозрительно.

цитата: AzikAtom:
olivenoel
можно ли побороть утечку, вызывая StrDispose на стороне хост-приложения?
Да. А можно в функцию передавать готовый буфер для заполнения? В winapi так и делают. Тогда контроль за памятью принадлежит вызывающей стороне.

WideString
Так ведь, WideString это как string только для 16 битных символов.

так ведь WideString полностью совместима с BStr, которая управляется олешным (т.е. общим для всех приложений, системным так сказать) менеджером памяти.

По поводу буффера. Где-то читала, что компилятор дельфи передает Result в виде out параметра. Что-то вроде

эквивалентно (точнее преобразуется не то парсером кода, не то самим компайлером)

Добавление от 11.07.2013 17:56:

цитата: Spirit-1:
olivenoel
Вызов Result := »; при типе функции PWideChar выглядит несколько подозрительно.

чем? Надо что ли nil возвращать?

54. olivenoel , 11.07.2013 17:56
55. Spirit-1 , 11.07.2013 18:15
Я бы Nil возвращал. Все-таки результат есть указатель, и это было бы логичнее.

Добавление от 11.07.2013 18:17:

olivenoel
И тогда все пользователи этой длл должны будут сильно вдаваться в подробности реализации
Кстати, правильно ли я понимаю, что память выделяется в dll, а освобождаться должна вызывающей стороной?

56. olivenoel , 11.07.2013 18:22
правильно Кто-то же ее должен освобождать. Или?

может все же дешевле переименовать все PWideChar в BStr и не морочить разные места?

Добавление от 11.07.2013 18:32:

то что получится на стороне хоста? WideString будет существовать только в scope как временная переменная. При выходе и scope она уничтожится (освободится ли. ). Т.е. Result по выходе из scope будет показывать в небо?

57. Spirit-1 , 11.07.2013 18:44
olivenoel
правильно Кто-то же ее должен освобождать. Или?
Реально это сильно неправильно. Аллоцирование памяти внутри осуществляется средствами конкретного memory manager’а в пуле памяти самой DLL. Освободить такое может только программа с аналогичным MM, и то она будет рыться в чужом огороде. Попробуйте скомпилировать вызывающую программу из другой версии Delphi (чтобы она использовала другой memory manager). И удивитесь, поскольку нифига работать не будет. Или другой вариант — вызывающая программа вообще, к примеру, на MS VC. Как будете память освобождать? Какими вызовами? (За доп. информацией можно поискать, для чего раньше в дельфях использовали borlandmm.dll)
В Вашем случае правильно делать именно так, как посоветовал AzikAtom. Вызывающая программа сама выделяет буфет. Функции DLL заполняют его (буфер) данными. Освобождение памяти делает тоже вызывающая программа. Вот это будет кошерно.
58. AzikAtom , 11.07.2013 18:58
olivenoel
При выходе и scope она уничтожится (освободится ли. )
Да.


Т.е. Result по выходе из scope будет показывать в небо?
Не в небо, а на освобождённую область памяти, где ещё какое-то время будет находится результат, потому что не успел затереться новой переменной.
Лучше всего делать как в winapi: выделить память под строку и передать вместе с переменной, где содержится размер буфера. Функция заполняет этот буфер и в переменной возвращает реальный размер строки.

цитата: Spirit-1:
olivenoel
правильно Кто-то же ее должен освобождать. Или?
Реально это сильно неправильно. Аллоцирование памяти внутри осуществляется средствами конкретного memory manager’а в пуле памяти самой DLL. Освободить такое может только программа с аналогичным MM, и то она будет рыться в чужом огороде.

цитата: Spirit-1:
olivenoel
Попробуйте скомпилировать вызывающую программу из другой версии Delphi (чтобы она использовала другой memory manager). И удивитесь, поскольку нифига работать не будет. Или другой вариант — вызывающая программа вообще, к примеру, на MS VC. Как будете память освобождать? Какими вызовами? (За доп. информацией можно поискать, для чего раньше в дельфях использовали borlandmm.dll)
В Вашем случае правильно делать именно так, как посоветовал AzikAtom. Вызывающая программа сама выделяет буфет. Функции DLL заполняют его (буфер) данными. Освобождение памяти делает тоже вызывающая программа. Вот это будет кошерно.

это ВинАпи стиль. Есть еще СОМ стиль. И там все «играют в одной песочнице» вызовами SysAllocString/SysFreeString/Sys. . Для этого надо все вызовы строковые заставить работать с BStr. Или?

59. olivenoel , 11.07.2013 19:03
60. AzikAtom , 11.07.2013 20:14
olivenoel
ну так WideString и аллоцируется OS’ью в системном ММ
Так вы возвращаете не WideString, а PWideString — указатель на строку. Как вы его дальше используете?
61. Spirit-1 , 11.07.2013 21:52
Я вижу в Вашем коде StrAlloc. Он вызывает GetMem, который выделяет память из СВОЕЙ кучи.

цитата: Spirit-1:
Я вижу в Вашем коде StrAlloc. Он вызывает GetMem, который выделяет память из СВОЕЙ кучи.

Добавление от 12.07.2013 10:31:

цитата: AzikAtom:
olivenoel
ну так WideString и аллоцируется OS’ью в системном ММ
Так вы возвращаете не WideString, а PWideString — указатель на строку. Как вы его дальше используете?

Добавление от 12.07.2013 11:37:

Такой код компилируется и даже, за редким исключением, даёт ожидаемый результат. Но тем не менее, в этом коде грубая ошибка. Указатель, возвращаемый функцией, указывает на область памяти, которая считается свободной — после того как переменная S вышла за пределы области видимости, память, которую занимала эта строка, освободилась. Менеджер памяти может в любой момент вернуть эту память системе (тогда обращение к ней вызовет Access violation) или задействовать для других целей (тогда новая информация перетрёт содержащуюся там строку). Проблема маскируется тем, что обычно результат используется немедленно, до того как менеджер памяти что-то сделает с этим блоком. Тем не менее, полагаться на это и писать такой код не стоит. Под использованием PChar в комментарии имеется ввиду использование его таким образом, как он используется в API-функциях: программа выделяет память для буфера, указатель на этот буфер передаёт в DLL как PChar, а DLL только заносит в этот буфер требуемое значение.

с областями видимости возвращаемых переменных у меня все намного проще — они глобальные переменные в модуле library — т.е. существуют пока загружена длл. Многие ф-ции, возвращающие строки были описаны как:

и все работало (не знаю правда сколько процентов «повезло» в этом). Если поменять это на

Delphi W >

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

Delphi 2005 WideString точно такого же типа, как Delphi 2010 String

Символ Delphi 2005 WideString а также символ Delphi 2010 String гарантированно всегда будут иметь размер 2 байта.

Со всеми форматами Unicode, я не хочу, чтобы меня ударяли по одному из символов в моей строке, вдруг размером 3 байта или чем-то вроде этого.

Редактировать: Найдено это: «Я действительно сказал UnicodeString, а не WideString. WideString все еще существует и остается неизменным. WideString выделяется диспетчером памяти Windows и должен использоваться для взаимодействия с COM-объектами. WideString отображается непосредственно в тип BSTR в COM «. по адресу http://www.micro-isv.asia/2008/08/get-ready-for-delphi-2009-and-unicode/

Теперь я еще больше запутался. То есть Delphi 2010 WideString — это не то же самое, что Delphi 2005 WideString ? Должен ли я использовать вместо UnicodeString ?

Редактировать 2: в Delphi 2005 нет типа UnicodeString . FML.

6 ответов

Для вашего первого вопроса: WideString не совсем тот же тип, что и строка D2010. WideString — это тот же тип COM BSTR, которым он всегда был. Он управляется Windows без подсчета ссылок, поэтому он делает копию всего BSTR каждый раз, когда вы его где-то передаете.

UnicodeString , который является строковым типом по умолчанию в D2009 и далее, по сути является версией AnsiString в формате UTF-16, которую мы все знаем и любим. Он имеет счетчик ссылок и управляется компилятором Delphi.

Во-вторых, типом char по умолчанию теперь является WideChar , это те же символы, которые всегда использовались в WideString . Это кодировка UTF-16, 2 байта на символ. Если вы сохраняете данные WideString в файл, вы можете без проблем загрузить их в UnicodeString . Разница между этими двумя типами связана с управлением памятью, а не с форматом данных.

Как уже упоминалось, строковый (на самом деле UnicodeString) тип данных в Delphi 2009 и выше не эквивалентен типу данных WideString в предыдущих версиях, но формат содержимого данных тот же. Они оба сохраняют строку в UTF-16. Поэтому, если вы сохраняете текст с помощью WideString в более ранних версиях Delphi, вы сможете правильно его прочитать, используя строковый тип данных в последних версиях Delphi (2009 и выше).

Обратите внимание, что производительность UnicodeString намного выше, чем WideString. Поэтому, если вы собираетесь использовать один и тот же исходный код как в Delphi 2005, так и в Delphi 2010, я предлагаю вам использовать псевдоним строкового типа с условной компиляцией в вашем коде, чтобы вы могли получить лучшее из обоих миров:

Теперь вы можете использовать MyStringType в качестве типа строки в исходном коде. Если компилятор Unicode (Delphi 2009 и выше), тогда ваш тип строки будет псевдонимом типа UnicodeString, который введен в Delphi 2009 для хранения строк Unicode. Если компилятор не является Unicode (например, Delphi 2005), тогда ваш тип строки будет псевдонимом для старого типа данных WideString. И поскольку они оба имеют формат UTF-16, данные, сохраненные в любой из версий, должны быть правильно прочитаны другой.

  1. Delphi 2005 WideString точно такого же типа, как Delphi 2010 String

Это не так — ex строка 2010 Delphi имеет скрытое поле внутренней кодовой страницы — но, вероятно, это не имеет значения для вас.

  1. Символ Delphi 2005 WideString, а также символ Delphi 2010 String гарантированно всегда будут иметь размер 2 байта.

Это правда. В Delphi 2010 SizeOf (Char) = 2 (Char = WideChar).

Для строк Unicode не может быть другой кодовой страницы — поле кодовой страницы было введено для создания общего двоичного формата как для строк Ansi (для которых требуется поле кодовой страницы), так и для строки Unicode (для которых это не нужно).

Если вы сохраняете данные WideString в поток в Delphi 2005 и загружаете те же данные в строку в Delphi 2010, все должно работать нормально.

@Marco — Строки Ansi и Unicode в Delphi 2009+ имеют общий двоичный формат (12-байтовый заголовок).

Кодовая страница UnicodeString CP_UTF16 = 1200;

  • Если вы хотите работать со строками Unicode только внутри вашего модуля — используйте тип UnicodeString (*).
  • Если вы хотите общаться с COM или с другими кросс-модульными целями — используйте тип WideString .

Видите ли, WideString — это особый тип, так как это не нативный тип Delphi. Это псевдоним / оболочка для BSTR — системный тип строки, предназначенный для использования с COM или межмодульной связью. Быть юникодом — это просто побочный эффект.

С другой стороны, AnsiString и UnicodeString — это нативные типы Delphi, аналогов которым нет в других языках. String — это просто псевдоним AnsiString или UnicodeString .

Поэтому, если вам нужно передать строку в другой код — используйте WideString , в противном случае — используйте либо AnsiString либо UnicodeString . Просто.

(*) Для старого Delphi — просто место

где-то в вашем коде. Это исправление позволит вам написать одинаковый код для любой версии Delphi.

Хотя символ D2010 всегда равен 2 байтам, в символах UTF-16 присутствуют те же проблемы свертывания и объединения символов, что и в символах UTF-8. Вы не видите этого с узкими строками, потому что они основаны на кодовых страницах, но с помощью строк Юникода возможно (и в некоторых ситуациях часто) иметь аффективные, но невидимые символы. Примеры включают в себя метку порядка байтов (BOM) в начале файла или потока Юникода, символы индикатора слева направо / справа налево и огромный диапазон сочетания акцентов. Это в основном касается вопросов «сколько пикселей в ширине будет эта строка на экране» и «сколько букв в этой строке» (в отличие от «сколько символов в этой строке»), но также означает, что вы можете ‘ t случайно вырезать символы из строки и предполагать, что они пригодны для печати. Такие операции, как «удалить последнюю букву из этого слова», становятся нетривиальными и зависят от используемого языка.

Вопрос о том, что «один из символов в моей строке вдруг имеет длину 3 байта», отражает небольшое недоумение по поводу того, как работает UTF. Можно (и допустимо) взять три байта в строке UTF-8 для представления одного печатаемого символа, но каждый байт будет действительным символом UTF-8. Скажем, письмо плюс два сочетания акцентов. Вы не получите символ в UTF-16 или UTF-32 длиной 3 байта, но он может иметь длину 6 байтов (или 12 байтов), если он представлен с использованием трех кодовых точек в UTF-16 или UTF-32. Что приводит нас к нормализации (или нет).

Но при условии, что вы имеете дело только со строками как с целыми вещами, все очень просто — вы просто берете строку, записываете ее в файл, затем читаете ее обратно. Вам не нужно беспокоиться о мелком шрифте отображения строки и манипуляции, которые все обрабатываются операционной системой и библиотеками. Strings.LoadFromFile (name) и Listbox.Items.Add (string) работают точно так же в D2010, как и в D2007, все вещи в Unicode прозрачны для вас, как для программиста.

Я пишу класс, который будет сохранять широкие строки в двоичном файле.

Когда вы пишете класс в D2005, вы будете использовать Widestring. При переходе на D2010 Widestring все еще будет работать и работать правильно. Widestring в D2005 такой же, как WideString в D2010.

Тот факт, что String = WideString в D2010 не нужно учитывать, так как компилятор легко справляется с этими проблемами.

Ваша входная подпрограмма для сохранения с помощью (AString: String) требует только одной строки, входящей в процесс

Вопрос по delphi-2009, unicode, delphi &#8211 Является ли W >

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

В приложении Delphi 2009 приложение Widestring идентично String?

Нет, они не идентичны.

WideString это просто оболочка для ActiveX / COM BSTR тип. Это необходимо при работе со строками в ActiveX / COM.

String в Delphi 2009 и позже это псевдоним для UnicodeString , который может содержать символы Unicode, так же, как BSTR делает, но это не то же самое, что WideString . WideString распределяется диспетчером памяти COM и не считается счетчиком. UnicodeString распределяется диспетчером памяти RTL и подсчитывается, как AnsiString является.

Вы должны использовать (Unicode)String везде, где это возможно, и использовать только WideString для взаимодействия COM или работы с устаревшими библиотеками, которые используют WideString для поддержки Unicode.

Посмотрите эту статью Marco Cantu, которая описывает работу со строкой (т.е. UnicodeString) в Delphi 2009:

«Белая книга: Delphi и Unicode»

По сути, это то, что сказал Родди, но для подробностей требуется 27 страниц.

Еще одна важная вещь, которую следует отметить, это разница в производительности.

В «Белой книге» Марко Канту (о чем говорится в ответе moodforaday) говорится:

«WideString не был (и остается) не подсчитанным количеством ссылок и крайне плох с точки зрения производительности и гибкости (например, он использует глобальный распределитель памяти Windows, а не собственный FastMM4)».

Практически каждое руководство по обновлению Delphi 2009, которое я видел, рекомендует вам конвертировать все WideStrings в строки.

Наиболее существенным изменением в Delphi 2009 является то, что тип «string» теперь является псевдонимом UnicodeString вместо AnsiString.

Delphi WideString и Delphi 2009+

December 2020

4.9k раз

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

Delphi 2005 WideString является точно таким же типом , как Delphi 2010 String

Delphi 2005 WideString голец, а также Delphi 2010 String полукокс гарантированно всегда быть 2 байта.

Со всем Unicode форматирует там я не хочу, чтобы ударить с одним из символов в моей струне вдруг быть 3 байта в ширине или что-то подобное.

Изменить: Найдено это: «Я сказал UnicodeString, не WideString WideString до сих пор существует, и остается неизменным WideString выделяется менеджером памяти Windows , и следует использовать для взаимодействия с COM — объектами WideString карты непосредственно к типу BSTR в COM. «. в http://www.micro-isv.asia/2008/08/get-ready-for-delphi-2009-and-unicode/

Теперь я еще больше запутался. Таким образом, Delphi 2010 WideString это не то же самое , как Delphi 2005 WideString ? Должен ли я использовать UnicodeString вместо этого?

Изменить 2: Там нет UnicodeString типа в Delphi 2005. FML.

7 ответы

В то время как D2010 символ всегда и ровно 2 байта, то же складной характер и сочетающие проблемы присутствуют в UTF-16 символов, как в UTF-8 символов. Вы не видите это с узкими строками, потому что они основаны кодовым, но с юникод строками возможно (и в некоторых случаях общих) иметь аффективные, но не видимые символы. Примеры включают в себя метку порядка байтов (BOM) в начале файла Юникода или потока, слева направо / справа осталось символов индикатора, а огромный диапазон сочетания акцентов. Это главным образом влияет на вопросы «сколько пикселей будет эта строка будет на экране» и «сколько букв в этой строке» (в отличии от «сколько символов в этой строке»), но также означает, что вы можете» т случайным образом измельчить символы из строки и предположит, что они для печати.

Вопрос о «один из символов в моей строке внезапно будучи длиной 3 байта» отражает немного confustion о том, как работает UTF. Возможно (и действует) принять три байта в строке UTF-8, чтобы представить один печатный знак, но каждый байт будет допустимым UTF-8 символов. Скажем, письмо плюс два сочетающие акцентов. Вы не получите символ в UTF-16 или UTF-32 быть длиной 3 байта, но это может быть 6 байт (или 12 байт) длиной, если она представлена ​​с помощью трех кодовых точек в кодировке UTF-16 или UTF-32. Что приводит нас к нормализации (или нет).

Но если вы дело только со строками в виде целых вещей, все это очень просто — вы просто строку, записать его в файл, а затем прочитать его обратно в Вам не придется беспокоиться о тонкой печати струнного дисплея. и манипуляции, что это все обрабатывается операционной системы и библиотек. Strings.LoadFromFile (имя) и Listbox.Items.Add (строка) работают точно так же в D2010 как в D2007, то юникода материал все прозрачно для вас в качестве программиста.

Как уже упоминалось другие, строка (на самом деле UnicodeString) тип данных в Delphi 2009 и выше не является эквивалентом WideString типа данных в предыдущих версиях, но формат содержимого данных является то же самое. Оба из них сохранить строку в UTF-16. Так что если вы сохраните текст, используя WideString в более ранних версиях Delphi, вы должны быть в состоянии прочитать его правильно, используя строковый тип данных в последних версиях Delphi (2009 и выше).

Вы должны принять к сведению, что выполнение UnicodeString является способом выше, чем WideString. Так что если вы собираетесь использовать один и тот же исходный код как в Delphi 2005 и Delphi 2010, я предлагаю вам использовать тип строки псевдоним с помощью условной компиляции в коде, так что вы можете иметь лучшее из обоих миров:

Теперь вы можете использовать MyStringType в качестве типа строки в исходном коде. Если компилятор Unicode (Delphi 2009 и выше), то ваш тип строки будет псевдонимом типа UnicodeString, который вводится в Delphi 2009 для хранения строк Unicode. Если компилятор не Юникод (например, Delphi 2005), то ваш тип строки будет псевдонимом для старого типа данных WideString. И так как они оба являются UTF-16, данные, сохраненные на любой из версий должна быть прочитана другой правильно.

  • Если вы хотите работать с юникод строк внутри модуля только — использовать UnicodeString тип (*).
  • Если вы хотите общаться с COM или с другими целями кросс-модуль — использование WideString типа.

Вы видите, WideString это особый тип, так как это не родной тип Delphi. Это псевдоним / оболочка для BSTR — типа системы струн, intendent для использования с COM или кросс-модуля связи. Будучи юникода — это просто побочный эффект.

С другой стороны, AnsiString и UnicodeString — являются носителями типа Delphi, которые не имеют аналогов в других языках. String это всего лишь псевдоним либо AnsiString или UnicodeString .

Так что , если вам нужно передать строку в какой — то другой код — использование WideString , в противном случае — использовать либо AnsiString или UnicodeString . Просто.

(*) Для старого Delphi — просто место

где-то в вашем коде. Это исправление позволит вам написать один и тот же код для любой версии Delphi.

Я пишу класс, который будет сохранять широкие строки в двоичный файл.

Когда вы пишете класс в D2005 вы будете использовать WideString При переходе на D2010 WideString все еще будет действовать и работать должным образом. WideString в D2005 такое же, как WideString в D2010.

Тот факт, что String = WideString в D2010 нет необходимости рассматривать, так как компилятор имеет дело с этими вопросами легко.

Ваш вклад рутина, чтобы сохранить с (AString: String) нужна только одна линия, входящего в прок

В то время как D2010 символ всегда и ровно 2 байта, то же складной характер и сочетающие проблемы присутствуют в UTF-16 символов, как в UTF-8 символов. Вы не видите это с узкими строками, потому что они основаны кодовым, но с юникод строками возможно (и в некоторых случаях общих) иметь аффективные, но не видимые символы. Примеры включают в себя метку порядка байтов (BOM) в начале файла Юникода или потока, слева направо / справа осталось символов индикатора, а огромный диапазон сочетания акцентов. Это главным образом влияет на вопросы «сколько пикселей будет эта строка будет на экране» и «сколько букв в этой строке» (в отличии от «сколько символов в этой строке»), но также означает, что вы можете» т случайным образом измельчить символы из строки и предположит, что они для печати.

Вопрос о «один из символов в моей строке внезапно будучи длиной 3 байта» отражает немного confustion о том, как работает UTF. Возможно (и действует) принять три байта в строке UTF-8, чтобы представить один печатный знак, но каждый байт будет допустимым UTF-8 символов. Скажем, письмо плюс два сочетающие акцентов. Вы не получите символ в UTF-16 или UTF-32 быть длиной 3 байта, но это может быть 6 байт (или 12 байт) длиной, если она представлена ​​с помощью трех кодовых точек в кодировке UTF-16 или UTF-32. Что приводит нас к нормализации (или нет).

Но если вы дело только со строками в виде целых вещей, все это очень просто — вы просто строку, записать его в файл, а затем прочитать его обратно в Вам не придется беспокоиться о тонкой печати струнного дисплея. и манипуляции, что это все обрабатывается операционной системы и библиотек. Strings.LoadFromFile (имя) и Listbox.Items.Add (строка) работают точно так же в D2010 как в D2007, то юникода материал все прозрачно для вас в качестве программиста.

Для вашего первого вопроса: WideString не точно такой же тип , как D2010 в строке . WideString тот же тип COM BSTR , что он всегда был. Это управляется ОС Windows, без подсчета ссылок, поэтому он делает копию всей BSTR каждый раз , когда вы передаете его куда — нибудь.

UnicodeString , Который по умолчанию строка типа в D2009 и, в основном версия UTF-16 из AnsiString все мы знаем и любим. У него есть счетчик ссылок и управляется компилятором Delphi.

Во — вторых, по умолчанию char тип теперь WideChar , что одни и те же символы , которые всегда были использованы в WideString . Это UTF-16 кодирования, 2 байта на символ. Если сохранить данные WideString в файл, вы можете загрузить его в UnicodeString без проблем. Разница между этими двумя типами имеет дело с управлением памятью, а не формат данных.

  1. Delphi 2005 WideString точно такой же тип, как Delphi 2010 строки

Это не так — бывший Delphi 2010 строки имеет скрытую внутреннее поле кодовой страницы — но, вероятно, это не имеет значения для вас.

  1. Delphi 2005 WideString голец, а также Delphi 2010 Строка полукокс гарантированно всегда 2 байта.

Это правда. В Delphi 2010 SizeOf (Char) = 2 (Char = WideChar).

Там не может быть другими кодовой юникод строк — поле кодового было введено, чтобы создать общий двоичный формат для обоего строк ANSI (что поле необходимо кодовое) и строку Unicode (которые не нужно).

Если сохранить данные WideString на поток в Delphi 2005 и загружать одни и те же данные в строку в Delphi 2010 все должно работать нормально.

@Marco — Ansi и Unicode строки в Delphi 2009+ имеют общий двоичный формат (заголовок 12 байт).

Илон Маск рекомендует:  Что такое код ini_restore
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL
62. olivenoel , 12.07.2013 10:03