PAnsiChar — Тип Delphi

Содержание
Илон Маск рекомендует:  Что такое код ldap_read

Несовместимые типы: «PAnsiChar» и «PWideChar»

Я очень новичок в delphi XE8. У меня есть следующий код, который из моей версии delphi 6, и я хочу запустить его в delphi XE8.

когда я пытаюсь запустить его, ошибка указывает на строки 8 и 25 в коде с сообщением об ошибке

У меня есть поиск везде для решения, но я просто не могу решить проблему. Пожалуйста, помогите.. Спасибо.

В Delphi 2007 и ранее PChar является псевдонимом для PAnsiChar . В Delphi 2009 и более поздних версиях PChar является псевдонимом для PWideChar . Поэтому, изменяя компилятор, вы меняете смысл кода.

Вы можете решить это просто, заменив PChar на PAnsiChar , и код будет иметь свое первоначальное значение.

В современном Unicode Delphi было бы более естественно использовать string (псевдоним UnicodeString ) вместо COM WideString . Вы также можете использовать одну из многих подпрограмм библиотеки для преобразования UTF-8.

У вас наверняка будут другие проблемы. Я рекомендую вам читать технический документ Marco Cantpaper на Unicode в Delphi в качестве следующего шага.

LPCSTR
Указатель на постоянную строку с нулевым завершением 8-битных символов Windows (ANSI).
Этот тип объявляется в WinNT.h следующим образом:
typedef __nullterminated CONST CHAR *LPCSTR;

LPSTR
Указатель на строку с нулевым завершением 8-битных символов Windows ( ANSI).
Этот тип объявляется в WinNT.h следующим образом:
typedef CHAR *LPSTR;

Илон Маск рекомендует:  Стохастические фракталы

Проблема, связанная с вашим кодом, состоит в том, что аргумент lpMultiByteStr для каждой функции является PAnsiChar , и вы передаете PChar в качестве параметра.
PChar является псевдонимом PAnsiChar в Delphi 6 и означает PWideChar в Delphi XE8.

Вы можете решить эту проблему в строке # 8, объявив, что вы работаете (и вызываете ее соответственно) следующим образом:
function UTF8ToStringLen(const src: PAnsiChar; const Len: Cardinal): WideString;

Чтобы решить проблему в строке 25, измените объявление функции следующим образом:
function StringToUTF8Len(const src: PAnsiChar; const Len: Cardinal): string; и «печально известная» строка:
bsiz := WideCharToMultiByte(CP_UTF8, 0, PWideChar(Temp), -1, PAnsiChar(Result), bsiz, nil, nil);

Несовместимые типы: «PAnsiChar» и «PW >

Я очень новичок в delphi XE8. У меня есть следующий код, который из моей версии delphi 6, и я хочу запустить его в delphi XE8.

когда я пытаюсь запустить его, ошибка указывает на строки 8 и 25 в коде с сообщением об ошибке

У меня есть поиск везде для решения, но я просто не могу решить проблему. Пожалуйста, помогите.. Спасибо.

    18 2
  • 11 авг 2020 2020-08-11 05:36:28
  • ErenRavenHeart

2 ответа

LPCSTR
Указатель на постоянную строку с нулевым завершением 8-битных символов Windows (ANSI).
Этот тип объявляется в WinNT.h следующим образом:
typedef __nullterminated CONST CHAR *LPCSTR;

LPSTR
Указатель на строку с нулевым завершением 8-битных символов Windows ( ANSI).
Этот тип объявляется в WinNT.h следующим образом:
typedef CHAR *LPSTR;

Проблема, связанная с вашим кодом, состоит в том, что аргумент lpMultiByteStr для каждой функции является PAnsiChar , и вы передаете PChar в качестве параметра.
PChar является псевдонимом PAnsiChar в Delphi 6 и означает PWideChar в Delphi XE8.

Вы можете решить эту проблему в строке # 8, объявив, что вы работаете (и вызываете ее соответственно) следующим образом:
function UTF8ToStringLen(const src: PAnsiChar; const Len: Cardinal): WideString;

Чтобы решить проблему в строке 25, измените объявление функции следующим образом:
function StringToUTF8Len(const src: PAnsiChar; const Len: Cardinal): string; и «печально известная» строка:
bsiz := WideCharToMultiByte(CP_UTF8, 0, PWideChar(Temp), -1, PAnsiChar(Result), bsiz, nil, nil);

  • 11 авг 2020 2020-08-11 05:36:29
  • fantaghirocco

В Delphi 2007 и ранее PChar является псевдонимом для PAnsiChar . В Delphi 2009 и более поздних версиях PChar является псевдонимом для PWideChar . Поэтому, изменяя компилятор, вы меняете смысл кода.

Вы можете решить это просто, заменив PChar на PAnsiChar , и код будет иметь свое первоначальное значение.

В современном Unicode Delphi было бы более естественно использовать string (псевдоним UnicodeString ) вместо COM WideString . Вы также можете использовать одну из многих подпрограмм библиотеки для преобразования UTF-8.

У вас наверняка будут другие проблемы. Я рекомендую вам читать технический документ Marco Cantpaper на Unicode в Delphi в качестве следующего шага.

Преобразование строк в PAnsiChar в Delphi 2009

Я конвертирую свои приложения в Delphi 2009 и столкнулся с интригующей проблемой с некоторыми вызовами, которым нужно преобразовать строку (широкую) в AnsiString.

вот пример, чтобы продемонстрировать проблему я:

С Delphi 2007 и предыдущими версиями, s: = PChar (Application.ExeName) вернет путь exe приложения.

С Delphi 2009, s: = PAnsiChar(приложение.ExeName) возвращает только ‘E’.

Я думаю, это потому, что Я конвертирую строку unicode в строку ansi, но как ее преобразовать, чтобы PAnsiChar получил полную строку?

6 ответов

У меня нет Delphi 2009 здесь, поэтому я не могу проверить его. Но может быть, вы должны попробовать:

Как уже указал Габр, это не очень хорошая практика, и вы будете использовать ее, только если вы уверены на 100%. Строка содержит только символы, имеющие прямое сопоставление с диапазоном ANSI.

вот почему вы должны получить предупреждение, потому что вы конвертируете Unicode в ANSI.

вместо использования типа String используйте RawByteString :

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

Я использую следующую функцию для извлечения даты компиляции приложения:

MapAndLoad требует PAnsiChar для параметра ImageName, поэтому мне нужно преобразовать строку unicode. Есть ли другая альтернатива для явного преобразования в AnsiString?

у меня была точно такая же проблема. The PAnsiChar указывает только на первый символ. Я написал следующую функцию для обработки старой функциональности.

Несовместимые типы: «PAnsiChar» и «PWideChar»

Я очень новый в Дельфах X Е8. У меня есть этот следующий код, который с моей DELPHI версии 6 и я хочу, чтобы запустить его в Дельфах X Е8.

когда я пытаюсь запустить его точки ошибок в линии 8 и 25 в коде с сообщением об ошибке, говоря

У меня есть искать везде для решения, но я просто не могу решить эту проблему. Пожалуйста, помогите .. Спасибо.

В Delphi 2007 и более ранние версии, PChar является псевдонимом PAnsiChar . В Delphi 2009 и более поздних версий, PChar является псевдонимом PWideChar . Таким образом, изменяя компилятор изменить значение кода.

Вы можете решить эту проблему путем простой замены PChar с PAnsiChar и код будет иметь свое первоначальное значение.

В современной Unicode Delphi было бы более естественно использовать string (псевдоним класса UnicodeString ) вместо COM WideString . Вы также можете использовать один из многих библиотечных подпрограмм для выполнения преобразования UTF-8.

Там наверняка будет другие вопросы перед вами. Я рекомендую вам прочитать официальный документ Марко Канта на Unicode в Delphi , как ваш следующий шаг.

LPCSTR
Указатель на постоянную строку с нулевым символом в конце 8-битных ОС Windows ( ANSI символов).
Этот тип объявлен в WinNT.h следующим образом :
typedef __nullterminated CONST CHAR *LPCSTR;

LPSTR
Указатель на строку с нулевым символом в 8-битных ОС Windows ( ANSI символов).
Этот тип объявлен в WinNT.h следующим образом :
typedef CHAR *LPSTR;

Проблема , связанная с вашим кодом является то , что аргумент lpMultiByteStr каждой функции есть PAnsiChar и вы передаете в PChar качестве параметра. Является псевдонимом в Delphi 6 и обозначает в Delphi X Е8 .
PChar PAnsiChar PWideChar

Вы можете решить эту проблему на линии № 8, объявляя вы действуете (и назвав его соответствующим образом), как это:
function UTF8ToStringLen(const src: PAnsiChar; const Len: Cardinal): WideString;

Для решения этой проблемы на линии № 25, изменить объявление функции , как:
function StringToUTF8Len(const src: PAnsiChar; const Len: Cardinal): string;
и « печально известной » линии:
bsiz := WideCharToMultiByte(CP_UTF8, 0, PWideChar(Temp), -1, PAnsiChar(Result), bsiz, nil, nil);

В каждом конкретном случае решение Дэвида Хеффернэн в относится.

PAnsiChar — Тип Delphi

Группа: Пользователи
Сообщений: 559
Пол: Мужской
Реальное имя: Бруно

Группа: Пользователи
Сообщений: 601
Пол: Мужской
Реальное имя: Артем

Группа: Пользователи
Сообщений: 559
Пол: Мужской
Реальное имя: Бруно

Группа: Пользователи
Сообщений: 601
Пол: Мужской
Реальное имя: Артем

Группа: Пользователи
Сообщений: 559
Пол: Мужской
Реальное имя: Бруно

Группа: Пользователи
Сообщений: 10
Пол: Мужской
Реальное имя: yuriy

У меня компилятор Delphi 7 ругается
Incompatible types: ‘String’ and ‘PAnsiChar’
на строку :
stpost:=StrCat(stpost,’XML_Query= ‘);

Переменная stpost имеет в проге тип String.
Как быть?

Преобразовать строку в PAnsiChar в Delphi 2009

Я конвертирую свои приложения в Delphi 2009 и столкнулся с интригующей проблемой с некоторыми вызовами, которые должны преобразовать строку (всю ширину) в AnsiString.

Вот пример, демонстрирующий мою проблему:

В Delphi 2007 и предыдущих версиях s: = PChar (Application.ExeName) возвращает путь к exe-приложению.

в Delphi 2009 s: = PAnsiChar (Application.ExeName) возвращает только «E».

Я думаю, это потому, что я преобразую строку в юникоде в строку ANSI, но как я могу преобразовать ее, чтобы PAnsiChar получил полную строку?

Несовместимые типы: «PAnsiChar» и «PWideChar»

Я очень новичок в delphi XE8. У меня есть следующий код, который из моей версии delphi 6, и я хочу запустить его в delphi XE8.

когда я пытаюсь запустить его, ошибка указывает на строки 8 и 25 в коде с сообщением об ошибке

У меня есть поиск везде для решения, но я просто не могу решить проблему. Пожалуйста, помогите.. Спасибо.

В Delphi 2007 и ранее PChar является псевдонимом для PAnsiChar . В Delphi 2009 и более поздних версиях PChar является псевдонимом для PWideChar . Поэтому, изменяя компилятор, вы меняете смысл кода.

Вы можете решить это просто, заменив PChar на PAnsiChar , и код будет иметь свое первоначальное значение.

В современном Unicode Delphi было бы более естественно использовать string (псевдоним UnicodeString ) вместо COM WideString . Вы также можете использовать одну из многих подпрограмм библиотеки для преобразования UTF-8.

У вас наверняка будут другие проблемы. Я рекомендую вам читать технический документ Marco Cantpaper на Unicode в Delphi в качестве следующего шага.

LPCSTR Указатель на постоянную строку с нулевым завершением 8-битных символов Windows ( ANSI ). Этот тип объявляется в WinNT.h следующим образом: typedef __nullterminated CONST CHAR *LPCSTR; LPSTR Указатель на строку с нулевым завершением 8-битных символов Windows ( ANSI ). Этот тип объявляется в WinNT.h следующим образом: typedef CHAR *LPSTR;

Проблема, связанная с вашим кодом, состоит в том, что аргумент lpMultiByteStr для каждой функции является PAnsiChar , и вы передаете PChar в качестве параметра. PChar является псевдонимом PAnsiChar в Delphi 6 и означает PWideChar в Delphi XE8.

Вы можете решить эту проблему в строке # 8, объявив, что вы работаете (и вызываете ее соответственно) следующим образом: function UTF8ToStringLen(const src: PAnsiChar; const Len: Cardinal): WideString;

Чтобы решить проблему в строке 25, измените объявление функции следующим образом: function StringToUTF8Len(const src: PAnsiChar; const Len: Cardinal): string; и «печально известная» строка: bsiz := WideCharToMultiByte(CP_UTF8, 0, PWideChar(Temp), -1, PAnsiChar(Result), bsiz, nil, nil);

Как перевести из String в Pansichar.

12 ответов

String в делфи — это по сути указатель на строку символов, заканчивающуюся нуль-символом, поэтому применять обычное приведение типа String в PChar не возбраняется компилятором.

procedure SomeProc(l: PAnsiChar);
begin
.
end;

begin
S:=’this is some text’
SomeProc(PAnsiChar(S));
end;

[QUOTE=verybadbug]глюк возникнет, если в PAnsiChar-строку значение передаётся. [/QUOTE]
Может быть. Головой надо думать. А в остальном неправ ты, брателло.

В Дельфи есть три способа превращения AnsiString в PChar и два способа обратного превращения.

AnsiString -> PChar:
— StrCopy() — выделяет память, копирует строку, возвращает указатель выделенную память надо освобождать вручную
— PChar() — увеличивает счетчик ссылок AnsiString, возвращает указатель на буфер AnsiString, память финализируется автоматически
— Pointer() — возвращает указатель на буфер, ничего более не делая — самый небезопасный вариант

Примеров приведения к Pointer до фига в исходниках Borland — вдумчиво курим последние.

PChar -> AnsiString:
— StrNew() — аналогично StrCopy, только возвращается AnsiString; надо ли освобождать память, не знаю, скорее всего, нет
— AnsiString() — проверяет, является ли буфер указателем на строку PChar/AnsiString (в памяти они хранятся совершенно одинаково), если нет, формирует новый буфер; увеличивает счетчик ссылок, после использования память финализируется автоматически

К слову сказать, функции вроде StrPas и StrNew устарели и используются только для работы с ShortString в целях обратной совместимости. Я в Дельфи никогда их не пользовал, потому что изврат.

я их тож не использую. разве что в крайнем случае . эти функции появились ещё в pascal. PChar() и Pointer() тогда ещё и впомине небыло. и в чём я не прав?

а по части StrNew. она просто делает копию строки PChar и возвращает указатель на новую копию PChar, после использования надо память освобождать. и никакого отношения к ansistring не имеет.

>..на строку PChar/AnsiString (в памяти они хранятся совершенно одинаково).
ничего подобного. AnsiString содержит в себе длину строки. а PChar нет и заканчивается #0

прежде чем спорить, предлагаю Вам, уважаемый Freeman ознакомица с Help.

вот тебе небольшой примерчик, доказывающий, что ты не прав.

Delphi 2006 сделал из него такое:

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

Блог GunSmoker-а (переводы)

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.

четверг, 3 сентября 2009 г.

PChars: сами строки не включены

The string is a stark data structure and everywhere it is passed there is much duplication of process. It is a perfect vehicle for hiding information. — Alan Perlis

В общедоступных newsgroup-ах на сервере Embarcadero я часто вижу, что по-прежнему есть много проблем с пониманием как типа PChar , так и типа String . В этой статье я хотел бы обсудить общие моменты и различия между обоими типами, а также вещи, которые вы можете и которые вы не должны делать с ними.

Общие принципы, описанные в этой статье, применимы ко всем Win32 версиям Delphi, включая Delphi 2009 и выше. В конце, однако, есть специальный раздел для тех, кто использует Delphi 2009 и выше.
Примечание: поскольку PChar является указателем, а String — ссылочным типом данных, то первым делом вам нужно бы разобраться с понятием указателя.

Содержание

PChar

Trying to outsmart a compiler defeats much of the purpose of using one. — Kernighan and Plauger, The Elements of Programming Style .

Идея типа PChar взята из строк языка C. Большинство функций Windows API имеют интерфейс в стиле C и принимают строки в стиле C. Чтобы можно было использовать эти API функции, Borland пришлось ввести тип, который бы подражал им, ещё в предшественнике Delphi: Turbo Pascal-е.

В C на самом деле не существует специального типа для строк, как это имеет место в Delphi. Строки в C – это просто массивы символов, а конец текста отмечается символом, ASCII код которого равен 0 . Это позволяет строкам быть большими (по сравнению со строками в Turbo Pascal-ле, где они были ограничены 255 символами из-за счётчика длины в виде байта – теперь это тип ShortString в Delphi), но несколько неудобными в использовании. Начало массива просто отмечается указателем на символ, который и стал определением типа в PChar в Delphi. Чтобы пройтись по строке в C, вы должны использовать этот указатель, как указатель на массив символов (вообще, это общее правило для всех указателей в C), и использовать s[20] для указания 21 -го символа (отсчёт начинается с 0 ). Но арифметика указателей в C позволяет делать не только инкременты и декременты, но также допускает сложение указателя с числом или вычисление разницы между двумя указателями. В C, *(s + 20) эквивалентно s[20] ( * в C является оператором разыменования, это аналог ^ в Delphi). Для типа PChar Borland сделала возможным практически тот же самый синтаксис.

Итак, PChar – это просто указатель, ровно как в C. И, снова как и в C, вы можете использовать его, как если бы это был массив (т.е. указатель, указывающий на первый элемент массива). Но на самом деле он им не является! Тип PChar не имеет автоматически управляемого хранилища данных, как это есть у обычных строк в Delphi. Если вы копируете текст в PChar -«строку», вы должны всегда быть уверенными, что этот ваш PChar действительно указывает на допустимый массив символов, и что массив достаточно велик, чтобы вместить весь текст.
Код выше не выделяет никакого хранилища для строки, поэтому он пытается сохранить символы в какое-то случайное место в памяти (адрес S неопределён и содержит какой-то мусор, см. мою статью про указатели). Это вызовет проблемы или даже вылет программы. Это ваша ответственность за гарантию наличия массива (прим. пер.: для типа String в Delphi ответственность за хранилище лежит на библиотеке поддержки языка Delphi). Простейший способ сделать это – использовать локальный массив: Код выше записывает символы в массив. Но если вы попробуете показать строку S на экране, вы, вероятно, увидите много другого мусора или даже вылет программы (*). Это потому что мы не завершили нашу строку символом #0 . OK, тогда мы можем добавить ещё одну строку: и тогда при выводе S вы получите текст » D6 «. Но записывать символы по-одному – это весьма неудобно. Чтобы показать текст через PChar , можно поступить проще: вы просто указываете PChar -ом на уже готовый массив символов с текстом в нём. К счастью, строковые константы типа ‘Delphi’ также являются такими массивами, поэтому они могут быть использованы как PChar : Вам только следует понимать, что код выше просто меняет указатель S . Сам текст никуда не копируется и не перемещается. Текст строковых констант хранится где-то в программе (и имеет терминатор #0 ), а S теперь указывает на его начало – вот и всё. Если вы сделаете: это не скопирует текст ‘Delphi’ в массив A . Первая строка после begin нацеливает указатель S на массив A , но тут же мы, во второй строке, изменяем S так, что он теперь указывает на строковую константу. Если вы хотите скопировать текст в массив, вам нужно указать это явно, используя, например, StrCopy или StrLCopy : или В конкретно этом случае нам очевидно, что строка ‘Delphi’ влезет в массив ( 101 символ как-никак), так что использование StrLCopy выглядит немного перебором, но в других случаях, когда вы не знаете наперёд размера строки, вы должны использовать StrLCopy для избежания переполнения буфера (да, ТОГО самого переполнения буфера – прим.пер.).

Массив типа A полезен как буфер для небольших строк или строк, ограниченных сверху (т.е. для которых известен максимальный потенциальный размер), но часто у вас будут строки, размер которых вам неизвестен во время компиляции. В этом случае вам нужно использовать динамическое выделение буфера для текста. Вы можете использовать, например, StrAlloc или StrNew для создания буфера, или же GetMem (а также динамические массивы или даже строки – прим.пер.), но тогда вы должны не забывать освобождать память, когда она вам станет не нужна, используя StrDispose или FreeMem . Вы также можете использовать тип String в Delphi в качестве буфера, но, прежде чем я опишу, как это сделать, я бы сперва хотел обсудить этот тип.

String

A world without string is chaos — Randolf Smuntz, Mouse Hunt

Позвольте мне вас запутать: String или, более точно, AnsiString (в Delphi 2009 и выше: UnicodeString ) фактически является PChar -ом. Точно так же, как и PChar , строка представляет собой указатель на массив символов, заканчивающихся символом #0 . Но есть одно большое отличие: обычно вам не нужно думать, как работают строки. Их можно использовать не задумываясь, почти как любую другую переменную. Компилятор сам заботится о вызове кода для выделения, копирования и освобождения текста строк. Поэтому вместо ручного вызова подпрограмм типа StrCopy , вы просто позволяете компилятору сделать это за вас.

Но это ещё не всё. Хотя текст, несомненно, всегда заканчивается символом #0 – сделано это только для того, чтобы сделать строки Delphi совместимыми со строками C, сам компилятор не нуждается в терминаторе. Перед текстом строки в памяти, по отрицательному смещению указателя, хранится длина строки, как число Integer . Так что, чтобы узнать длину строки, компилятор просто читает этот Integer , экономя на поиске первого #0 в строке. Это означает, что вы можете хранить и сам символ #0 в середине строки, и это будет работать. Но некоторые подпрограммы, которые работают с терминатором, воспримут только часть строки.

Обычно, каждый раз, когда вы присваиваете одну строку другой, компилятору надо бы выделять память и копировать текст из одной переменной в другую. Поскольку строки в Delphi могут быть очень большими (теоретически до 2 Гб максимум в 32 -х разрядных приложениях), это может быть весьма медленно. Чтобы избежать лишнего копирования, Delphi использует концепцию, которая известна под названием «копирование по требованию» («copy on demand»). Каждая строка имеет, помимо длины, и другое служебное поле: счётчик ссылок (reference count). Он содержит количество строковых переменных, которые ссылаются на конкретно эту строку в памяти. Если счётчик опускается до 0 , то это значит, что на текст строки никто больше не ссылается, и он может быть удалён из памяти.

Компилятор гарантирует вам, что счётчик ссылок всегда будет содержать правильное значение (но вы можете и обмануть компилятор: приведениями типов – подробнее об этом ниже). Если строка объявлена в секции var или как поле класса или записи, она начнёт свою жизнь как nil – внутреннее представление пустой строки ( » ). Когда текст строки только создаётся и присваивается переменной, счётчик ссылок становится равным 1 . Каждое дополнительное присваивание этой строки другой переменной будет увеличивать счётчик ссылок (никаких данных при этом не копируется). Если строковая переменная покидает область видимости (когда заканчивается функция или удаляется объект), или же ей присваивают другую строку, то счётчик ссылок уменьшается.

Простой пример: Теперь S1 указывает на текст ‘123456’ и имеет счётчик ссылок равный 1 . Текст не копируется, S2 просто указывает на тот же адрес, что и S1 , но только счётчик ссылок текста ‘123456’ теперь равен 2 . Теперь выделяется новый, больший буфер, в него копируется текст ‘The number is ‘ и туда же добавляется текст ‘123456’ . Но т.к. S2 более не указывает на текст ‘123456’ , то счётчик ссылок этого текста снова уменьшается до 1 . Result теперь указывает на тот же адрес, что и S2 , а счётчик ссылок текста ‘The number is 123456’ увеличивается до 2 . Теперь S1 и S2 выходят из области видимости. Счётчик ссылок текста ‘123456’ будет уменьшен до 0 , поэтому буфер этого текста освобождается (**). Счётчик ссылок текста ‘The number is 123456’ также уменьшается на единицу, становясь равным 1 . Сам буфер не удаляется, поскольку счётчик ссылок не равен 0 (у нас на него ещё указывает Result ).

Сложно? Да, весьма запутанно. И это становится ещё более запутанным с введением в игру var , const и out параметров. Но, к счастью, обычно вам не нужно заморачиваться этими вопросами. Такие вещи важно понимать только если вы обращаетесь к строкам из ассемблера, напрямую через PChar или с помощью подпрограмм прямого доступа к памяти. Но использование строк с приведением к PChar не является чем-то необычным. Тем не менее, если вы хотите заглянуть чуть дальше под капот языка — вы можете прочитать эту статью.

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

  • что текст копируется в новый буфер только при изменении строки;
  • что счётчик ссылок и длина текста не привязаны к строковой переменной, а только к конкретному тексту, на который могут указывать несколько (равноправных) строковых переменных;
  • что счётчик ссылок всегда верен, если только вы не наврёте компилятору, используя приведения типов;
  • что присваивание чего-то строковой переменной уменьшит счётчик ссылок текстового буфера, на который она указывала до присваивания;
  • что, если счётчик ссылок опускается до 0 , то строковый буфер удаляется.

Использования вместе String и PChar

If you can’t be a good example, then you’ll just have to be a horrible warning. — Catherine Aird

PChar и символьные массивы довольно тяжело использовать. В большинстве случаев вы должны выделять память и не забывать её освобождать. Если вы хотите добавить текст, то вы должны сперва посчитать длину получающейся строки, затем увеличить буфер, если он слишком мал, и использовать StrCat или StrLCat , чтобы, наконец, добавить текст. Вы должны использовать StrComp или StrLComp для сравнения строк и т.д. и т.п.

Строки ( String ), с другой стороны, намного проще использовать. Большинство вещей выполняется автоматически, само собой. Но множество API функций Windows (или Linux) требуют нуль-терминированных строк PChar , а не строк Delphi String . К счастью, тип String специально устроен так, что они фактически тоже являются указателями на строки, завершающиеся нулём (этот терминатор никак не используется Delphi и компилятор постоянно таскает его со строками только на этот случай). Так что любую строку String вы можете использовать и как PChar – просто приводя тип: Не забывайте, что переменная AnsiString является указателем на текст, а не самим текстовым буфером. Если текст изменяется, то он будет скопирован в другое место, а адрес в переменной соответствующим образом изменится. Это означает, что вы не должны использовать PChar для указания на строки, а затем изменять строку. Лучше всего избегать таких вещей: Если S изменяется на ‘Something else’ , указатель P не будет изменён и будет продолжать указывать на ‘C:\Test.exe’ . Поскольку P не является строковой (в смысле String ) ссылкой на этот текст, и у нас нет никакой другой переменной, ссылающейся на него, то его счётчик ссылок станет равным 0 , и текст будет удалён из памяти. Это означает, что P теперь указывает на недоступную память (invalid memory).

Будет мудрым решением не путать компилятор смешением переменных PChar и String , только если вы точно не знаете, что вы делаете. Компилятор не воспринимает PChar как String , поэтому он не будет менять счётчик ссылок строковых буферов, если вы нацелите на них PChar . Часто наилучшим решением будет отказ от подобного использования PChar . Просто используйте обычные строки, и делайте приведение типов только в последний момент. Функции, принимающие параметр PChar , должны копировать текст параметра в свои собственные переменные.

Обычно строковые буферы имеют размер достаточный только для того, чтобы вместить лежащий в них (присвоенный им) текст. Но используя SetLength , вы можете установить произвольный размер строки (в символах). Это делает строки полезными для использования в качестве буферов. Например, вызов функции Windows API, которая возвращает текст в символьном массиве, может выглядеть так: Альтернативно, вы можете присвоить PChar -у String , и в результате у вас получится новая строка с копией текста. Поэтому вы можете установить длину строки с помощью такого эквивалентного кода: Последняя строка в функции устанавливает размер строки равный размеру нуль-терминированной C-строке, записанной в ней (длина C-строки всегда меньше или равна длине её String -хранилища – прим.пер.). Если вам нужен результат как PChar -строка для дальнейших действий (например для передачи в другие API-функции), вы могли бы попробовать использовать такой код: Однако, это не будет работать. Потому что Buffer является локальной переменной, хранящейся целиком в локальной памяти (процессорном стеке). Как только вы покидаете эту функцию, память, которая была выделена под Buffer , начинает использоваться другими подпрограммами, так что текст, который лежал в буфере, становится мусором. Вы никогда не должны использовать локальные переменные для возвращаемых значений PChar .

Вы могли бы обойти это, делая динамическое выделения памяти для возвращаемого PChar , с помощью, например, StrAlloc , но тогда вызывающему пришлось бы руками освобождать выделенный вами буфер. Обычно, это не самый лучший подход. Лучше следовать примеру GetWindowsDirectory , и позволить вызывающему указать свой буфер и его размер. Тогда вы просто заполните уже готовый буфер (используя StrLCopy ) своими данными до допустимого размера.

Есть и альтернативная реализация функции WindowsDirectory , которая может использовать локальный буфер. Она основывается на том, что вы можете присваивать PChar строке String напрямую. Чтобы сделать текст именно строкой Delphi (с служебными полями длины и счётчиком ссылок), будет создан строковый буфер требуемой длины, а текст будет скопирован в него. Поэтому, даже если локальный буфер будет удалён, то текст в строковом буфере всё ещё будет с нами: Но как вам написать функцию, например в DLL, которая должна возвращать данные как PChar ? Я думаю, что вы снова можете следовать примеру GetWindowsDirectory . Вот пример простой функции из DLL, возвращающей строку версии: Как вы можете видеть, строка просто копируется в предоставленный вызывающим буфер с помощью StrLCopy . Поскольку вызывающий обязан подготовить буфер, вы избегаете любых проблем с управлением памятью. Если вы предоставляете буфер, то вы и знаете, как его удалять. FreeMem не будет работать через границу DLL (в общем случае). Но даже если бы работала (например, с использованием общего менеджера памяти – прим.пер.), то пользователь вашей DLL, работающий в C или Visual Basic, не знал бы, как освободить буфер в своём языке, т.к. управления памятью индивидуально в каждом языке. Позволяя вызывающему указывать свой буфер, вы делаете его или её независимыми от вашей реализации.

Прим.пер.: а вот ещё обсуждение этого вопроса с несколькими альтернативными решениями.

Delphi 2009 и выше

В Delphi 2009 строки были значительно изменены. До Delphi 2009 (т.е. с Delphi 2 по Delphi 2007) строки были, фактически, типом AnsiString , а каждый символ был однобайтовым AnsiChar . Тип PChar был псевдонимом для PAnsiChar . Но в Delphi 2009 строки стали использовать Unicode, а ещё точнее – UTF-16, что означает, что потребовался новый тип строк: UnicodeString . Этот тип строк состоит уже из двух-байтовых WideChar . Он стал умалчиваемым типом строк, что означает, что String теперь псевдоним для UnicodeString , Char для WideChar , а PChar для PWideChar .

Delphi for Win32 уже имела строковый тип WideString (также состоящий из WideChar ), но это всегда лишь псевдоним для системного типа строк BSTR , используемом в основном в COM. Этот тип управляется ОС (и поэтому является идеальным средством для обмена строками между границами модулей – прим.пер.) и не имеет счётчика ссылок и “копирования по требованию”, так что каждое присваивание означает создание новой уникальной строки с полным копированием текстового буфера. Поэтому тип WideString не отличается особой производительностью – вот почему был введён новый тип UnicodeString .

Кроме длины и счётчика ссылок, каждый строковый тип данных (т.е. AnsiString и UnicodeString ) теперь имеют дополнительные служебные поля: Word , содержащий кодировку (encoding) строки (в основном используется в однобайтовых строках типа AnsiString ), и Word , содержащий размер символа в байтах. Кодировка строки AnsiString управляет интерпретацией и конвертацией символов с кодами от 128 до 255 , а размер символа в основном используется для взаимодействия с кодом на C++.

Кроме того, также было введено несколько вспомогательных типов строк: RawByteString ( = AnsiString($FFFF) ) и UTF8String ( = AnsiString(65001) ) (а также огромного количества любых других пользовательских типов строк на базе AnsiString , например, Win1251String = AnsiString(1251) – прим.пер.). Подразумевается, что строки UTF8Strings используются для хранения данных в формате UTF-8, что означает, что каждый элемент строки является AnsiChar -ом, но каждый «символ» может быть представлен несколькими элементами AnsiChar . Заметьте, что я поставил слово «символ» в кавычки, потому что в контексте Unicode более правильно будет говорить о кодовых позициях (code point) (а также это не приведёт к путанице с символом в смысле один Char — прим.пер.).

Как вы можете видеть из статьи Википедии о UTF-16, также возможно, что некоторые кодовые позиции UTF-16 требуют нескольких WideChar -ов – так называемых «суррогатных пар» (surrogate pairs). Так что длина UnicodeString или UTF8String не обязательно напрямую соответствует числу кодовых позиций в них. Однако если в UnicodeString суррогатные пары относительно редки (суррогатные пары используются только для записи символов вне базовой плоскости: Basic Multilingual Plane (BMP) – именно там находятся все символы для всех современных языков и множество специальных символов – прим.пер.), то в то же время редкая UTF8-строка обходится без мультибайтовых символов.

Ещё одним новым типом является RawByteString . Если вы присвоите AnsiString с одним типом кодировки другой AnsiString с другой кодировкой, то будет выполнена автоматическая конвертация (потенциально: с возможной потерей данных, если символы из одной кодировки не имеют эквивалента в другой кодировке). Собственно AnsiString используют кодировку по-умолчанию, управляемую системой (“Кодировка для не-Unicode приложений” в региональных настройках системы – прим.пер.). Пользовательские типы строк на базе AnsiString всегда имеют фиксированную кодировку (например, UTF8String всегда имеет кодировку 65001 ). А RawByteString – это специальная строка без кодировки, так что вы можете быть уверены, что при присваивании ей другой AnsiString -строки (собственно AnsiString или пользовательской типа UTF8String ), никакой конвертации не будет, а все текстовые буфера будут скопированы “как есть”.

Справка Delphi 2009 так говорит о RawByteString :

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

Так что же с этим делать?

Как вы можете видеть из текста статьи, я ни разу не делал ссылок на размер Char . Так что всё, что я написал выше, полностью применимо и к Delphi 2009 и выше без изменений. Большинство кода, использующего эти техники, может быть просто перекомпилировано в Delphi 2009 и выше, и будет работать корректно, но теперь уже используя Unicode, т.е. вместо AnsiString , код будет работать с UnicodeString , WideChar и PWideChar .

Функции Win32 API часто поставляются в двух вариантах: один из которых принимает Ansi (т.е. однобайтовые) символы и (C-) строки, а второй принимает Wide (Unicode, двухбайтовые) символы и (C-) строки. Эти варианты обычно отличаются друг от друга окончаниями A или W в имени функции соответственно. Заголовочные модули Delphi для таких API функций, типа Windows.pas , обычно также определяют третий вариант, без окончаний A или W (точно так же поступает Microsoft в своих заголовочниках C), и отождествляет этот вариант с Ansi вариантом. Вот один пример из Windows.pas до Delphi 2009: Как вы можете видеть, GetShortPathName проецируется на функцию ‘GetShortPathNameA’ . Вы также можете видеть, что -A версия объявляется с PAnsiChar -ами, а -W версия принимает строки PWideChar . Нейтральная же работает с нейтральным типом PChar .

В Delphi 2009 и выше, такие нейтральные функции теперь ассоциируются с -W вариантом, так что вторая часть вышеприведённого кода теперь становится: Это означает, что в Delphi 2009 и выше, даже если вы вызываете функцию из Windows API, а также если вы вызываете функцию RTL или VCL, то в большинстве случаев вам не нужно беспокоиться о размере символов в строке. Строки теперь у нас Unicode, функции API стали тоже Unicode, так что если вы продолжите использовать нейтральные типы данных String , Char и PChar , то вам не придётся изменять свой код. А если у вас есть код, который работает с неверным размером символа (некоторые функции API, типа GetProcAddress , существуют только в ANSI варианте), то вы получите красивое предупреждение или ошибку от компилятора, так что вы тут же сможете решить, как вам реагировать (прим.пер.: вообще-то, у GetProcAddress есть перегруженный Unicode-вариант, который делает преобразование строк к ANSI-варианту).

SizeOf или Length?

Конечно же, вам нужно быть осторожным с кодом, особенно если этот код использует низко-уровневые подпрограммы типа Move или FillChar (которая теперь по-хорошему должна была бы называться FillByte , т.к. работает она с байтами, но старое название было сохранено по соображениям совместимости кода – прим.пер.), которые предполагают, что символы имеют размер в один байт (ну, на самом деле они просто меряют размеры в байтах, а не в символах – прим.пер.). Так, чтобы очистить массив из Char , не делайте так: потому что теперь буфер состоит из WideChar , что означает размер в 2 * (MAX_PATH + 1) байт. Намного проще при этом использовать SizeOf : Заметьте, что SizeOf можно применять только к статическим массивам. Для динамических массивов она всегда возвращает размер указателя (см. статью про указатели). В этом случае вам надо писать: Для ситуаций же, когда важно число символов, используйте просто Length :

Информация для дальнейшего чтения

Тут есть whitepaper от Marco Cantù, который обширно и ясно описывает различные новые строковые типы и расширения. Я рекомендую скачать её и прочитать хотя бы один раз.

Заключение

The open secrets of good design practice include the importance of knowing what to keep whole, what to combine, what to separate, and what to throw away. — Kevlin Henny

Хотя оба типа String и PChar являются строковыми типами, они весьма различны. String проще использовать, а для PChar вам нужно всё делать вручную. Вы можете использовать их вместе и приводить String к PChar , и присваивать PChar строке, но поскольку String меняет свой адрес при изменениях, вы не должны долго держаться за этот адрес при использовании PChar . Присваивание PChar -а строке намного безболезненней.

Как показано в тексте выше, выделение текста в функции и возвращение PChar на новый буфер не является очень хорошей идеей. Это ещё хуже в ситуациях межмодульного обмена данными, поскольку вызывающий может даже не сумеет освободить возвращаемую память – DLL и вызывающий, скорее всего, будут иметь различные менеджеры памяти – каждый со своей кучей. Также не очень хорошей идеей является использование локального буфера для возвращаемого текста.

Если вам обязательно нужно использовать PChar , потому что этого требует функция, вы должны использовать String настолько, насколько это возможно. И приводить строку к PChar только когда вы готовы передать строку параметром. Использование строк намного проще и менее подвержено ошибкам, чем использование функций строк в C-стиле.

A little inaccuracy sometimes saves a ton of explanation. — H. H. Munro (Saki)

Я надеюсь, что я немного приподнял завесу тумана над PChar . Я не рассказал всё, что известно, и даже, возможно, немного переврал правду (например, не любая Delphi-строка типа String управляется счётчиком ссылок – к примеру, строковые константы всегда имеют счётчик ссылок равный –1 (***)), но эти мелкие детали не столь важны для общей, большой картины и не оказывают влияния на использование и взаимодействие между String -ми и PChar -ми.

Дополнительный материал для чтения: тонкости работы со строками.

Примечания переводчика:
(*) Вот ещё интересный пример.
(**) Чтобы гарантировать обязательное выполнение очистки строк (уменьшения счётчика ссылок), компилятор неявно вставляет в подпрограмму скрытый try/finally. Поэтому, фактически, код любой подпрограммы со строковой переменной (а также любой другой авто-финализируемой переменной) выглядит примерно так: (***) См. также пример ситуации, где это имеет значение.

Несовместимые типы: «PAnsiChar» и «PWideChar»

Я очень новичок в Delphi XE8. У меня есть следующий код из моей версии 6 Delphi, и я хочу запустить его в Delphi XE8.

когда я пытаюсь запустить его, ошибка указывает на строку 8 и 25 в коде с сообщением об ошибке, говорящим

У меня везде поиск решения, но я просто не могу решить проблему. Пожалуйста, помогите .. Спасибо.

В Delphi 2007 и более ранних версиях PChar является псевдонимом для PAnsiChar . В Delphi 2009 и более поздних версиях PChar является псевдонимом для PWideChar . Таким образом, изменяя компилятор, вы меняете смысл кода.

Вы можете решить эту проблему, просто заменив PChar на PAnsiChar , и код будет иметь свое первоначальное значение.

В современном Unicode Delphi было бы более естественно использовать string (псевдоним UnicodeString ) вместо COM WideString . Вы также можете использовать одну из многих библиотечных подпрограмм для выполнения преобразования UTF-8.

У вас наверняка будут другие проблемы. Я рекомендую вам прочитать документ Марко Канто по Unicode в Delphi как ваш следующий шаг.

LPCSTR
Указатель на постоянную строку с нулевым символом в конце, состоящую из 8-битных Windows (ANSI) символов.
Этот тип объявлен в WinNT.h следующим образом:
typedef __nullterminated CONST CHAR *LPCSTR;

LPSTR
Указатель на завершающуюся нулем строку из 8-битных Windows (ANSI) символов.
Этот тип объявлен в WinNT.h следующим образом:
typedef CHAR *LPSTR;

Проблема, связанная с вашим кодом, состоит в том, что аргумент lpMultiByteStr каждой функции является PAnsiChar , и вы передаете PChar в качестве параметра.
PChar является псевдонимом PAnsiChar в Delphi 6 и ​​обозначает PWideChar в Delphi XE8 .

Вы можете решить эту проблему в строке № 8, объявив свою функцию (и вызвав ее соответственно) следующим образом:
function UTF8ToStringLen(const src: PAnsiChar; const Len: Cardinal): WideString;

Чтобы решить проблему в строке № 25, измените объявление функции следующим образом:
function StringToUTF8Len(const src: PAnsiChar; const Len: Cardinal): string;
и строка » печально известная «:
bsiz := WideCharToMultiByte(CP_UTF8, 0, PWideChar(Temp), -1, PAnsiChar(Result), bsiz, nil, nil);

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL