delphi (XE8 x64, 10, 7)

Описание: Разработка и отладка приложений. Упор на 3D-графику.

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#19 dyvniy » Чт, 9 июня 2016, 08:25:05

Кодировки
TEencoding, RawBytesStrng, TBytes.
http://www.webdelphi.ru/2010/04/3-varianta-raboty-s-kodirovkami-veb-stranic-v-delphi/
Спойлер
3 варианта работы с кодировками веб-страниц в Delphi.
03/04/2010
Vlad
10 Комментарии
Delphi в Web

Сколько постов написано в блогах, сколько вопросов задано на форумах о кодировках и ещё большее количество подобных вопросов осталось не отвеченными или ответом было что-то наподобие «Поиском пользовались?» или «Сто раз обсуждалось!!!». Честно говоря, никогда не понимал таких «ответчиков», ИМХО не хочешь отвечать — лучше жуй и молчи, ответят те, кто захочет.
Понятное дело, что обучать иногда приходится не только с помощью пряника, но и с помощью кнута, но, раз уж такие вопросы всё время всплывают, следовательно они остаются актуальными.
Сегодня я попробую рассказать Вам всё, что мне известно о работе с кодировками в тексте. Вполне возможно, что эта статья поможет Вам при разработке своих проектов в Delphi да и у меня уже пару раз возникало желание немного систематизировать ту информацию. которая накопилась за время существования блога.
1. Delphi и Unicode

Если говорить о работе с Unicode в Delphi, то начать стоит с того, что полноценная поддержка unicode в Delphi стала возможна лишь после выхода Delphi 2009. Такое событие не могло пройти незамеченным, так как благодаря поддержке Unicode и, соответственно, для облегчения работы с кодировками текста в Delphi были реализованы новые возможности, методы, типы данных, о которых написано большое количество статей. На мой взгляд одной из лучших публикаций на эту темя является цикл из трех статей «Delphi и кодировка Unicode» где достаточно чётко и доступно рассказано о нововведениях Delphi 2009 для работы с unicod’ом. Думаю, что нет смысла подробно останавливаться на всех новшествах при наличии ссылки на целых три статьи на эту тему. Остается только упомянуть о том, с чем мы сегодня будем работать для представления веб-страницы в нормальном читаемом виде.
Для первого способа работы с кодировкой мы воспользуемся:
1.Класс TEncoding, который и дает нам возможность без лишних хлопот работать с кодировками
2. Тип данных TBytes — массива байтов строк
3.RawByteString — тип для передачи строковых данных для любой кодовой страницы без каких-либо преобразований последней.
В одной из статей блога рассматривалась работа с MLang и сегодня, в качестве второго способа, я продемонстрирую Вам пример её применения при работе с кодировками.
Ну и в качестве третьего способа работы с кодировками, воспользуемся «штатными» методами модуля system. Все три варианта работы с кодировками приведут к одному и тому же результату — текст веб-страницы будет читаемым, без «кракозябров» и вопросительных знаков. Какой способ лучше — решать только Вам.
2. Подготовка исходных данных для работы

Прежде, чем начать что-то кодировать и перекодировать, необходимо это «что-то» получить. В нашем случае — текст веб-страницы. Чтобы не залезать слишком глубоко в вопросы, касающиеся новых типов данных будем сохранять все данные из Сети не в строки или TStringList’ы, как мы к этому привыкли, а воспользуемся типом TBytes.
Используя Synapse исходник любой страницы можно получить, например так: with THTTPSend.Create do
begin
if HTTPMethod('GET',URL)then
begin
//в свойстве Document объекта THTTPSend содержится исходник страницы
//обрабатываем Document
end;
end;


Теперь, получив данные (свойство Document:TMemoryStream) скопируем эти данные в массив байтов строки TBytes, например так (способ достаточно медленный, приведен исключительно для примера):var B: TBytes;
i:integer;
begin
//загружаем контент страницы
with THTTPSend.Create do
begin
if HTTPMethod('GET',URL) then
begin
Document.Position:=0;
SetLength(B,Document.Size);
for i := 0 to Document.Size - 1 do
Document.Read(B[i],1);
end;
end;


Первый этап подготовки можно считать законченным — у нас есть массив байтов строки, которые необходимо представить в виде текста в правильной кодировке. А как узнать, что наш массив TBytes надо перевести в новую «правильную» кодировку? Естественно узнать в какой кодировке была исходная страница. Здесь можно пойти двумя путями:
1. Узнать кодировку из мета-тегов. Обычно кодировка страницы описывается мета-тегом следующего содержания:META http-equiv="content-type" content="text/html; charset=windows-1251


. Пример того как узнать кодировку из мета-тегов страницы Вы можете посмотреть в модуле HtmlCPConvert, который я выкладывал в блоге, когда рассматривал работу с MLang.
2. Узнать кодировку текста из заголовков сервера. Если рассматривать этот пример, используя для работу с HTTP-протоколом библиотеку Synapse, то заголовки сервера будут содержаться в свойстве Headers:TStringList. Найти элемент, содержащий сведения о кодировке и получить имя кодовой страницы можно, например так:function GetCharset(Headers: TStringList): string;
var i:integer;
begin
if Headers.Count=0 then Exit;
for I := 0 to Headers.Count - 1 do
begin
//Content-Type: text/html; charset=UTF-8
if Pos('content-type',LowerCase(Headers[i]))>0 then
if pos('=',Headers[i])>0 then
Result:=LowerCase(Copy(Headers[i],pos('=',Headers[i])+1,Length(Headers[i])-pos('=',Headers[i])))
else
Result:=DefCharset;
end;
end;


Так как в заголовке может отсутствовать сведения о кодировке, то переменная DefCharset должна содержать имя какой-либо дефолтной кодовой страницы, например windows-1251.
Теперь у нас есть все исходные данные:
1. Массив байтов строки B:TBytes;
2. Сведения о кодировке.
Приступим к преобразованию байтов в читабельный текст.
3. Массив байтов — в нормальный текст
3.1. Работа с TEncoding

Разработчики Delphi предусмотрели использование двух взаимопротивоположных метода:
1. function BytesOf(const Val: RawByteString): TBytes; — получение байтов из строки
2. function StringOf(const Bytes: TBytes): UnicodeString; — получение строки из массива байтов
Пусть Вас не пугает то, что StringOf возвращает UnicodeString, т.к., начиная с Delphi 2009
type
UnicodeString = string

Теперь, что касается непосредственно работы с TEncoding. Во-первых, от нас не требуется создавать отдельный экземпляр класса, наподобие такого:var Enc: TEncoding;
begin
Enc:=TEncoding.Create;
end;


Во-вторых, с помощью TEncoding мы можем менять кодовую страницу не преобразовывая массив байтов в строку. Для этого класс содержит следующие классовые методы:class function Convert(Source, Destination: TEncoding; Bytes: TBytes): TBytes; overload;
class function Convert(Source, Destination: TEncoding; Bytes: TBytes; StartIndex, Count: Integer): TBytes; overload;


Одним из этих методов мы и воспользуемся. Для того, чтобы продемонстрировать работу с TEncoding попробуем получить текст прямо из этого блога. Кодировка UTF-8, именно она наиболее часто является «проблемной» и возвращает в TMemo или ещё куда-либо «кракозябры».
Создадим простое приложение как показано на рисунке:

В ComboBox сразу занесем все варианты работы с текстом:
1. Без преобразования
2. TEncoding (модуль SysUtils)
3. MLang
4. Utf8ToAnsi (модуль System)
По нажатию кнопки TButton будем грузить страницу в массив байтов и, зависимости от выбранного варианта работы с текстом, преобразовывать массив в строку и выводить в Memo. Для первого варианта (без преобразования) код может быть следующий:with THTTPSend.Create do
begin
if HTTPMethod('GET',Edit1.Text)then
begin
Document.Position:=0;
SetLength(B,Document.Size);
for i := 0 to Document.Size - 1 do
Document.Read(B[i],1);
label4.Caption:=GetCharset(Headers);
end;
end;
Label6.Caption:=IntToStr(Length(B))+' байт текста';
case ComboBox1.ItemIndex of
0:Memo1.Text:=StringOf(B);
[...]
end;


Запускаем приложение и видим:

Как и ожидалось — вместо русских букв кракозябры. Преобразуем их с помощью классового метода TEncoding.Convert:var astr: String;
B: TBytes;
[...]
case ComboBox1.ItemIndex of
0:Memo1.Text:=StringOf(B);
1:begin
B:=TEncoding.Convert(TEncoding.UTF8,TEncoding.GetEncoding(1251),B);
astr:=StringOf(B);
Memo1.Lines.Add(astr);
end;
[...]
end;


Здесь следует отметить, что TEncoding.GetEncoding(1251) возвращает кодовую страницу кириллического текста. Если необходимо получить другую кодовую страницу, то можете либо воспользоваться классовыми свойствами TEncoding либо определить кодовую страницу как я, используя данные с MSDN о доступных в Windows кодовых страницах.
Проверим, что получилось в итоге. Грузим ту же самую страницу:

Как видите — кракозябры преобразовались в нормальный русский текст.
Какие плюсы дает нам использование TEncoding? Думаю, что ответ вполне очевиден — у нас появляется под рукой инструмент, позволяющий перекодировать строки из любых кодировок в любые, т.е. фактически возможности работы с текстом ограничиваются количеством доступных в Windows кодовых страниц, а их по данным MSDN 152 штуки. Прикиньте сколько вариантов получится, если в Convert используется пара кодовых страниц. Много :).
Но, наряду с таким большим и жирным плюсом существует и минус — TEncoding есть только в версиях Delphi 2009 и выше, что исключает его использование в более ранних версиях. Как быть?
Если хотите получить не менее впечатляющие возможности работы с кодировками — используйте возможности MLang. Вот как раз и пример его использования.
3.2. Использование возможностей MLang для работы с кодовыми страницами

Позволю себе ещё раз напомнить, что скачать всё необходимое для реализации возможностей MLang Вы можете с этой страницы или, перейдя по этой ссылке. В архиве содержатся всего два модуля: MLang.pas и HtmlCPConvert.pas. Подключаем оба модуля к проекту и для получения читабельного текста пишем:
Memo1.Text:=RawHTMLToHTML(StringOf(B));

RawHTMLToHTML из модуля HtmlCPConvert переводит текст в кодировку windows-1251. Также есть и обратный метод для перевода текста в RawByteString и называется этот метод HTMLToRawHTML.
Результат преобразования текста с помощью MLang абсолютно идентичен предыдущему варианту:

Если Вам необходимо работать со множеством кодировок, как при использовании TEncoding, то придётся самостоятельно немного доработать модуль HtmlCPConvert. В целом возможности TEncoding и MLang вполне сопоставимы.
И, наконец, третий вариант — использование методов модуля System.pas.
3.3. Использование методов System.pas для перекодирования текста

В модуле System.pas можно найти следующие полезные функции для работы с кодировками:Utf8Encode(const US: UnicodeString): RawByteString;
function UTF8EncodeToShortString(const WS: WideString): ShortString;
function Utf8Decode(const S: RawByteString): WideString;
function UTF8ToUnicodeString(const S: RawByteString): UnicodeString;
function UTF8ToString(const S: RawByteString): string;
function AnsiToUtf8(const S: string): RawByteString;
function Utf8ToAnsi(const S: RawByteString): string;


Как видите, методы завязаны на работе с конкретными кодовыми страницами UTF-8, ANSI, а также на работе с новым типом данных RawByteString. Поэтому возможности третьего способы по сравнению с первыми двумя резко ограничены. Хотя в реалиях нынешнего Рунета их вполне достаточно, т.к. веб-страницы в кодировке koi8-r уже больше раритет, чем обыденность. Использовать методы достаточно просто. Например так:var RS: RawByteString;
[...]
begin
RS:=StringOf(B);
Memo1.Text:=Utf8ToAnsi(RS);
end;


я перевел массив байтов в стоку RawByteString и затем представил её в кодировке Ansi, получив в Memo читабельный русский текст.
Вот три способа работы с различными кодировками текста, которые могут применяться где угодно. И это только часть всех возможностей, которые предоставляет нам Delphi. Какой способ использовать — решать Вам.
Кстати, при работе с кодировками следует обратить внимание на то, что «кракозябры» в тексте — это следствие представления строки с использованием неправильной кодовый страницы. Если же вместо русских букв появляются знаки вопроса, то это следствие того, что в текущей кодовой странице текста нет тех символов, которые вы пробуете показать. Например, представляя текст ANSI в кодировке UTF-8 мы получаем кракозябры, а если попробовать представить текст ANSI Cyrillic (windows-1251) с кодовой страницей ANSI Latin 1 (windows-1252), то вместо русских букв получим знаки вопроса, т.к. в windows-1252 отсутствуют русские буквы и т.д.

Похожие публикации:
MLang в Delphi. Работа с кодировками Web-страниц. Материалы предоставлены автором блога «Переводы от GunSmoker« Небольшая аннотация к...
Google Calendar API в Delphi. Начало работы. Наконец-то разобрался со своим провайдером и доказал тех.поддержке, что проблемы...
Простой компонент Delphi 2010, для работы со Шкурами. Привлекательный и необычный интерфейс, играет не самую последнюю роль в...
Очистка HTML-документа от тегов. Пишу сейчас небольшое дополнение к «Блевантону» — включение в анализ...
Основы работы с тезаурусом Word в Delphi. Как я упоминал вчера, есть идея немного поразбираться с тезаурусом...
Теги: Synapse, кодировки, строки
|
10 Comments · Leave Comment
Александр
04 / 04 / 10

1. По последнему методу: использование функций — это скорее устаревший способ, оставленный для обратной совместимости.

В новых Delphi нужно использовать RawByteString + SetCodePage для произвольной кодировки (задаваемой в run-time) или AnsiString(CodePage) для фиксированной (например, type TString1251 = AnsiString(1251)).

Например, если мы точно знаем, что целевая кодировка — UTF-8, то мы объявляем S типа UTF8String, загружаем в неё контент, а дальше — простое присвоение. Вся конвертация выполнится автоматически и под капотом.

Если же кодировка не фиксирована и определяется в run-time, то мы объявляем S типа RawByteString и грузим в неё контент, после чего делаем, например, SetCodePage(S, 1251, False) — понятно, что у вас будет не константа 1251, а значение из переменной. После чего, как и ранее, обычное присвоение сделает всю работу.

2. Вообще, тут приведены несколько не сравнимые вещи. В том смысле, что способ 1 и 3 — это обёртки над одной и той же функцией (MultiByteToWideChar). Эти способы решают задачу перевода из одной кодировки в другую. Но они не говорят нам, как из ‘windows-1251’ получить 1251, а из ‘UTF-8’ получить 65001 для передачи в TEncoding.GetEncoding или SetCodePage.

Строго говоря, способ 2 тоже должен использовать какой-либо из способов 1 и 2 (или аналогичный) для конвертации кодировок (предложенная реализация вызывает MultiByteToWideChar напрямую). Поэтому, на самом деле, способ 2 состоит из двух частей: определение кодировки по заголовкам и конвертации кодировок.

Просто этот момент как-то размыто сказан. Непонятно, как предлагается осуществлять выбор кодовой страницы в первом и последнем способах.
Ответить
Vlad
04 / 04 / 10

Про SetCodePage хотел написать, а потом чё-то решил, что трех способов будет достаточно :) Цель поста была не то, чтобы сравнить способы, скорее показать как можно вообще проводить конвертацию кодировок между собой. Кому-то хватит использовать те же UTF8ToAnsi и он будет жить спокойно, а кто-то решит перевести KOI8-R в UTF-8 и тут уже дело будет обстоять по другому.

Но они не говорят нам, как из ‘windows-1251? получить 1251, а из ‘UTF-8? получить 65001 для передачи в TEncoding.GetEncoding или SetCodePage.

Подобные функции видел пока ещё не встречал, хотя не может быть, чтобы за 2 года кто-нибудь не додумался написать хотя бы обёртку над TEncoding. Надо будет на досуге погуглить эту тему. Если что — поделюсь поисками :)
Ответить
Алексей (Минск)
06 / 01 / 10

Странно у меня для ANSI пишет 932 со 100%
А для dos 866 но вероятность маленькая
Ответить
Аленсандр
10 / 07 / 10

А зачем Вы во втором варианте получали getcharset(headers), если потом явно преобразововали utf8 в 1251? Не подскажите, как получить getcharset, а потом преобразовать ее в 1251 не зная кодировки страници. Спасибо. Статьи Ваши очень познавательны.
Ответить
Fr0sT
10 / 26 / 10

Код: Выделить всё

 Document.Position:=0; SetLength(B,Document.Size); for i := 0 to Document.Size - 1 do Document.Read(B[i],1);
=>

Код: Выделить всё

 Document.Position:=0; SetLength(B,Document.Size); Document.Read(B[0],Length(B));
?
>существует и минус – TEncoding есть только в версиях Delphi 2009 и выше, что исключает его использование в более ранних версиях. Как быть?
Ну, во-первых, вполне можно выдрать код TEncoding и после небольших подпиливаний заюзать его в D<2009, либо не мучиться с обёртками и юзать самую основу — WideCharToMultiByte/MultiByteToWideChar, на которой, собственно, и сидят все остальные реализации. Только придётся ещё самому обрабатывать случаи utf16/32: эти функции с ними не работают. Но там всё просто:
utf16le = widechar
utf16be = swapbytes(widechar)
ну и с utf32 то же.
>Но они не говорят нам, как из ‘windows-1251″ получить 1251, а из ‘UTF-8? получить 65001 для передачи в TEncoding.GetEncoding или SetCodePage.
Всё просто: массив структур
TCPInfo = record
CPNum: Word;
CPCode: string;
end
и поиск перебором. Строковые коды берутся из RFC по HTTP/XML.
Если интересует, я делал такое для библиотеки SimpleXML
Ответить
wait
02 / 04 / 11

от себя добавлю еще один удобный способ, когда кодировка содержимого известна (в данном случае UTF8), но естественно способ можно переделать и с динамическим определением:
var
Response: TStringStream;
<…>
begin
Response := TStringStream.Create(», TEncoding.UTF8);
if HttpGetBinary(‘путь_до_сайта’, Response) then
Memo1.Text:=Response.DataString;
end;
Ответить

Pingback: Парсинг JSON в Delphi под Windows и MacOS. | Delphi в Internet

Pingback: Криптография в Delphi средствами Indy и Synapse. | Delphi в Internet
Vitus
02 / 19 / 15

Спасибо за статью, очень помогла!!!
Ответить
Макс
11 / 02 / 15

Собственно, вопрос: каким образом можно всё это действо запустить обратно?

То есть мне необходимо перегнать из делфи запросом в URL через idHTTP. Третий день бьюсь, в одну сторону работает, в другую — нет.
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#20 dyvniy » Пн, 27 июня 2016, 17:23:29

Регулярные выражения в Delphi
http://www.webdelphi.ru/2010/09/delphi-xe-rabota-s-regulyarnymi-vyrazheniyami/
XE и выше RegularExpressions.pas

Код: Выделить всё

 if RegEx.IsMatch('Any text','Any regular expression')then
   ShowMessage
('Есть свпадения')

Спойлер
Delphi XE. Работа с регулярными выражениями.
15/09/2010 Vlad 21 Комментарии Основы Delphi
Уже почти год назад я затрагивал тему о работе с регулярными выражениями VBScript в Delphi 2010. Сегодня наконец-то докачал iso-образ диска с RAD Studio XE, установил Delphi XE для более пристального ознакомления с новинками и обнаружил несколько новых модулей, цель которых — обеспечение работы с регулярными выражениями в Delphi. Я решил вспомнить прошлое и написать небольшую программку, демонстрирующую работы с регулярными выражениями с использованием «родных» модулей в Delphi XE.


Вначале немного о том, что за новые модули появились в версии XE и вкратце, что в них содержится.

RegularExpressionsAPI.pas -обёртка для библиотеки PCRE. PCRE — это библиотека содержащая ряд функций для работы с регулярными выражениями синтакис которых максимально предлижен к синтаксису регулярных выражений в Perl 5.

RegularExpressionsCore.pas — как становится поняным из названия — модуль представляет собой некое ядро вокруг которого и крутится вся работа с регулярками. Так, например, в модуле RegularExpressionsCore содержася такие «говорящие» классы как TPerlRegEx, TPerlRegExOptions и т.д. Соответственно этот модуль опирается в своей работе на функции из RegularExpressionsAPI.pas.

RegularExpressionsConsts.pas — содержит ресурсные строки, используемые при выводе сообщений об ошибках при работе с регулярными выражениями.

RegularExpressions.pas — модуль в котором содержаться всё необходимое для работы с регулярными выражениями в Delphi XE. Естественно модуль опирается на все три предыдущих.

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

Работа с TRegEx в Delphi XE

В TRegEx содержаться все необходимые методы и свойства для работы с регулярными выражениями. Практически все методы имеют «двойников» в виде классовых функций. Поэтому можно работать с регулярками или так:

...
var RegEx: TRegEx;
begin
RegEx:=TRegEx.Create('Any regular expression');
if RegEx.IsMatch('Any text')then
ShowMessage('Есть свпадения')
...
А можна и без создания нового экземпляра TRegEx:

...
begin
if RegEx.IsMatch('Any text','Any regular expression')then
ShowMessage('Есть свпадения')
...
Как Вам будет удобнее в той или иной ситуации. Рассмотрим методы TRegEx.
Конструкторы:

constructor Create(const Pattern: string); overload;
constructor Create(const Pattern: string; Options: TRegExOptions); overload;
где Pattern — регулярное выражение, а Options — множество, содержащее опции, используемые при поиске совпадений.

function IsMatch(const Input: string): Boolean; overload;
function IsMatch(const Input: string; StartPos: Integer): Boolean; overload;
class function IsMatch(const Input, Pattern: string): Boolean;overload; static;
class function IsMatch(const Input, Pattern: string; Options: TRegExOptions): Boolean; overload; static;
IsMatch возвращает True, если в тексте Input найдено хотя бы одно совпадение. При этом парсинг можно начать не с начала строки, а с позиции StartPos.

function Match(const Input: string): TMatch; overload;
function Match(const Input: string; StartPos: Integer): TMatch; overload;
function Match(const Input: string; StartPos, Length: Integer): TMatch; overload;
class function Match(const Input, Pattern: string): TMatch; overload; static;
class function Match(const Input, Pattern: string; Options: TRegExOptions): TMatch; overload; static;
Match возвращает запись TMatch, которая содержит результаты одного совпадения с регулярным выражением.

function Matches(const Input: string): TMatchCollection; overload;
function Matches(const Input: string; StartPos: Integer): TMatchCollection; overload;
class function Matches(const Input, Pattern: string): TMatchCollection; overload; static;
class function Matches(const Input, Pattern: string; Options: TRegExOptions): TMatchCollection; overload; static;
Matches возвращает все совпадения с регулярным выражением. При этом TMatchCollection представляет собой запись, которая содержит список всех найденых совпадений TMatch.

function Replace(const Input, Replacement: string): string; overload;
function Replace(const Input: string; Evaluator: TMatchEvaluator): string; overload;
function Replace(const Input, Replacement: string; Count: Integer): string; overload;
function Replace(const Input: string; Evaluator: TMatchEvaluator; Count: Integer): string; overload;
class function Replace(const Input, Pattern, Replacement: string): string; overload; static;
class function Replace(const Input, Pattern: string; Evaluator: TMatchEvaluator): string; overload; static;
class function Replace(const Input, Pattern, Replacement: string; Options: TRegExOptions): string; overload; static;
class function Replace(const Input, Pattern: string; Evaluator: TMatchEvaluator; Options: TRegExOptions): string; overload; static;
Заменяет все вхождения строки Replacement в строке Input. Напоминает всем известный метод StrinReplace, но предоставляет более широкие возможности по замене. При этом замена текста в исходной строке может осуществляться с использованием регулярного выражения для этого необходимо определить метод:

TMatchEvaluator = function(const Match: TMatch): string of object;
возвращающий в результате строку, исходя из содержащихся в TMatch данных. Также, в параметре Count можно указать максимальное количество замен. Рассморим возможности этой функции немного по-подробнее. Напишем простую программку, которая будет искать в строке текста все латинские символы A и менять их на B. Ничего особенного — просто демонстрация возможностей метода. Внешний вид приложения следующий:



В Edit будем записывать регулярное выражение, в Memo — текст для парсинга, а в нижний ListBox — все найженные совпадения и результаты работы метода Replace. С помощью CheckBox’ов будем устанавливать необходимые опции (об опциях чиайе ниже).
Вначале определим метод TMatchEvaluator следующим образом:

function TForm8.Evaluator(const Match: TMatch): string;
begin
Result:='';
if pos('A',Match.Value)&gt;0 then
Result:='B'
end;
То есть при использовании этого метода в Replase замена будет происходить следующим образом: если найдена заглавная буква, то она заменяется на заглавную B, иначе — на пустую строку.

Теперь пишем обработчик OnClick «Проверить»

procedure TForm8.Button1Click(Sender: TObject);
var RegEx: TRegEx;
Options: TRegExOptions;
M: TMatchCollection;
i,j:integer;
begin
//определяем необходимые опции
Options:=[];
if CheckBox1.Checked then
Include(Options, roIgnoreCase);
if CheckBox2.Checked then
Include(Options, roMultiLine);
if CheckBox3.Checked then
Include(Options, roExplicitCapture);
if CheckBox4.Checked then
Include(Options, roCompiled);
if CheckBox5.Checked then
Include(Options, roSingleLine);
if CheckBox6.Checked then
Include(Options, roIgnorePatternSpace);

ListBox1.Items.Clear;
//создаем объект
RegEx:=TRegEx.Create(Edit1.Text,Options);
//Если найдено хотя бы обно совпадение продолжаем работу
if RegEx.IsMatch(Memo1.Text)then
begin
M:=RegEx.Matches(Memo1.Text,Edit1.Text,Options);//получаем коллекцию совпадений
for i:=0 to M.Count-1 do
begin
ListBox1.Items.Add(M.Item[i].Value);//выводим совпадение
for j:= 0 to M.Item[i].Groups.Count-1 do //если в регулярном выражении есть группы
ListBox1.Items.Add(' Группа '+IntToStr(j)+' '+M.Item[i].Groups[j].Value)
end;
//Выводим результаты работы Replace
ListBox1.Items.Add(RegEx.Replace(Memo1.Text,Evaluator));
ListBox1.Items.Add(RegEx.Replace(Memo1.Text,'B'));
end;
end;
Теперь проверим работу. Запустим приложение и запишем в Memo, например, такую строку:

Evaluator - это метод, который "заставляет" заменять в исходной строке все символы "A" на "B" с использованием особых правил замены
А регулярное выражение зададим, например так:

.?A.?
Вначале проверяем без дополнительных опций. Результат поиска совпадение:

1 совпадение
Replace с Evaluator: Evaluator — это метод, который «заставляет» заменять в исходной строке все символы B на «B» с использованием особых правил замены
Replace без Evaluator: Evaluator — это метод, который «заставляет» заменять в исходной строке все символы B на «B» с использованием особых правил замены

То есть оба раза метод сработал идентично. Теперь включим опцию IgnoreCase (поиск буз учёта регистра) и повторим замену. Результат:

3 совпадеия («A», uat, val)
Replace с Evaluator: Eor — это метод, который «заставляет» заменять в исходной строке все символы B на «B» с использованием особых правил замены
Replace без Evaluator: EBBor — это метод, который «заставляет» заменять в исходной строке все символы B на «B» с использованием особых правил замены

То есть, как видно из замен в слове «Evaluator», мы не просто провели замену подстрок, но и, в первом случае (с использованием метода Evaluator) определили свои собственные правила замены подстрок. Соответственно, включив в параметры Replace третье значение (Count) мы помимо всего прочьего можем определить м количество замен в строке.

И, наконец, последний метод TRegEx:

function Split(const Input: string): TArray; overload; inline;
function Split(const Input: string; Count: Integer): TArray; overload; inline;
function Split(const Input: string; Count, StartPos: Integer): TArray; overload;
class function Split(const Input, Pattern: string): TArray; overload; static;
class function Split(const Input, Pattern: string; Options: TRegExOptions): TArray; overload; static;
Split делит строку на подстроки с использованием регулярного выражения и помещает эти подстроки в масиив. При этом совпадения с регулярным выражением выступают в качестве разделителя и выбрасываются из результатов деления.
Чтоб далеко не ходить — применим этот метод с использованием рассмотренного выше регулярного выражения и строки для поиска. Допишем наш обработчик onClick следующим образом:

procedure TForm8.Button1Click(Sender: TObject);
var
...
Mass: TArray;
begin
...
ListBox1.Items.Add(RegEx.Replace(Memo1.Text,'B'));
//делим строку
Mass:=RegEx.Split(Memo1.Text);
for i := 0 to length(Mass)-1 do
ListBox1.Items.Add(Mass[i])
end;
end;
Результат деления строки с использованием опции IgnoreCase получился следующим:

3 совпадеия (“A”, uat, val)
Части строки:
E
» (пустая строка)
or — это метод, который “заставляет” заменять в исходной строке все символы
на “B” с использованием особых правил замены

Пустая строка является в приведенномм примере результатом того, что два совпадения в строке идут один за другим — соответственно split возвращает символы между ними, т.е. строку нулевого размера.
Вот собственно те методы, используя которые можно использовать регулярные выражения в Delphi XE с использованием TRegEx. Осталось только упомянуть про опции, используемые при поиске и замене текста.

Опции TRegEx

Всего можно использовать шесть различных опций:
roIgnoreCase — поиск без учёта регистра символов
roMultiLine — включает многострочный режим парсинга. Имеет смысл использовать, когда в тексте встречаются символы перевода на новую строку.
roExplicitCapture — указывает на то, что в результаты работы будут включаться только именованныегруппы вида (…)
roCompiled — использоваие этой опции даёт более быстрое выполнение операци парсинга, но вызов методов TRegEx будет выполняться медленнеее.
roSingleLine — однострочный режим. Парсинг будет осуществляться в строке до символа перевода /n
roIgnorePatternSpace — устраняет из паттерна неэкранированные пробелы и делает возможным использование в регулярных выражениях комментариев.

Вот, в принципе, краткий обзор возможностей Delphi XE по работе с регулярными выражениями. И в конце статьи пара ссылок на информацию по регулярным выражениям в Perl:
1. Регулярные выражения в Perl
2. WDH: Perl — регулярные выражения

Похожие публикации:

Беглый обзор модуля IOUtils.pas. Часть 2: работа с файлами в Delphi 2010. Продолжаем рассматривать возможности IOUtils.pas — нового модуля Delphi 2010. Сегодня...
Беглый обзор модуля IOUtils.pas. Часть 3: работа с TPath в Delphi 2010. Заключение. Итак, сегодня третья заключительная часть краткого обзора нового модуля Delphi...
Беглый обзор модуля IOUtils.pas. Часть 1: работа с директориями в Delphi 2010. С выходом версии 2010 в Delphi появился ещё один модуль...
Модуль Character — работа с символами строки. Начиная с версии 2009, в RTL Delphi появился ряд новых...
WordPress. Работа с XML-RPC в Delphi. Комментарии (продолжение). В прошлый раз я рассматривал процедуру получения комментариев из блога...
Теги: Delphi XE, TRegEx, парсинг, регулярные выражения |
21 Comments · Leave Comment

Keeper
09 / 16 / 10
А как по части багов/производительности/удобства в сравнении со старыми сторонними решениями?
Ответить
Vlad
09 / 16 / 10
2Keeper, проверил регулярки, которые раньше использовал с VBScript — повезло, всё работает :) Удобство…это понятие сугубо личное, но по мне так предложенная новинка в XE вполне удобна и понятна, можно работать. Производительность пока не проверял.
Ответить
Дмитрий
09 / 19 / 10
Поздновато они это
Во всех проектах использовал библиотеку Андрея Сорокина, из-за совместимости и нежелания тратить время на освоение новой библиотеки буду продолжать дальше пользоваться ею.
А вот если бы родная либа уже была когда я только начинал работать с регулярками…
Ответить
Akella
09 / 21 / 10
Да… поздно они регулярки прикрутили к среде.
Ответить
Vlad
09 / 21 / 10
Лучше поздно, чем никогда :) Прикрепили и слава Богу — лишними не будут точно
Ответить
GlooK
09 / 23 / 10
Давно пора было регулярку приерктить.
А я как работал на легальной Turbo Delphi (Portable) вместе с компонентом TRegExpr, так и буду работать.
Ответить
Keeper
04 / 02 / 11
Кстати интересно, почему они не работают с кириллицей…
Ответить
Vlad
04 / 02 / 11
Хм…странно, активно юзаю их сейчас как раз на русскоязычных сайтах для парсинга. Вроде как с кирилицей проблем вообще не встречал
Ответить
Keeper
04 / 02 / 11
А у меня

RegEx:=TRegEx.Create(‘\w’);
if RegEx.IsMatch(‘Маша мыла раму
‘)then
в упор не работают.

Ответить
Vlad
04 / 02 / 11
Проверил. С русскими буковками работает, т.к.
RegEx:=TRegEx.Create('Маша');

RegEx.IsMatch('Маша мыла раму ')
Возвращает True, а вот с метасимволами беда, некотрые не пашут, в т.д. и \w — почему-то считается, что w — обычный символ, а вот \s работает :)
Ответить
Keeper
04 / 03 / 11
Вот с метасимволам как раз и интересно, как заставить это все работать )
Ответить
Treigol
11 / 24 / 12
по-моему действие опций roMultiLine и roSingleLine перепутано, почему-то у меня многострочный парсинг работает только при опции roSingleLine
Ответить
Isaev
03 / 20 / 13
Сдаётся мне, что
If RegEx.IsMatch(S) Then….
и потом Param:=RegEx.Matches(S)
Это двойная работа, что очень замедляет процесс при больших объёмах данных… Не уж то после проверки нельзя было сразу сохранять результат?
или может делать
Try
Param:=RegEx.Matches(S)
сразу лучше?
Ответить
werhad
05 / 13 / 13
а как сделать так чтобы находил слово и перед ним вставлял нужный текст не изменяя само слово?
Ответить
Андрей
01 / 20 / 14
Достаточно давно использую библиотеку Андрея Сорокина, и до сих пор ее функционала вполне хватало, но сейчас захотелось большего. Встроенные регулярные выражения (Delphi XE5) оказались гораздо медленнее, и у меня так и не получилось заставить их воспринимать символы русского алфавита, как буквы. Нашел еще пару бесплатных сторонних решений, но они также основаны на PCRE, и заставить их воспринимать русский текст в метасимволах не удалось.
Что касается производительности, ниже приведены замеры скорости и код (дабы не быть голословным). В некоторых случаях вполне возможно поступиться производительностью, если бы не русский язык. Может кто-нибудь найдет решение…
Текст: ’10’
Регулярка: ‘\d’
[1 итерация] [1000 итераций] [100 000 итераций] [1 000 000 итераций]
TRegExpr (Андрей Сорокин): 0.033 мс 0.7 мс 62 мс 605 мс
TJclRegEx (JCL): 0.068 мс 2.1 мс 189 мс 1 869 мс
TRegex (Erik Van Bilsen): 7.893 мс 10.5 мс 211 мс 2 038 мс
TRegEx (XE5): 0.122 мс 27.6 мс 2 666 мс 26 551 мс
Код:
uses
RegularExpressions, uRegularExpressions, jclPCRE, RegExpr;
//Замер скорости
procedure TForm2.FormCreate(Sender: TObject);
var
rj: TJclRegEx;
r: TRegExpr;
re: RegularExpressions.TRegEx;
m: RegularExpressions.TMatch;
re2: IRegex;
m2: IMatch;
i, j, cnt, p: Integer;
res, s, txt, rxpr: String;
begin
txt := ’10’;
rxpr := ‘\d’;
cnt := 1;
res := »;
//TRegExpr
s := »;
CounterStart;
r := TRegExpr.Create;
r.ModifierStr := ‘I’;
r.Expression := rxpr;
for i := 1 to cnt do
if r.Exec(txt) then
repeat
s := r.Match[0];
until not r.ExecNext;
r.Free;
CounterStop;
res := res + ‘TRegExpr (Андрей Сорокин): ‘ + CounterValueAsString + #13#10;
//JCL
s := »;
CounterStart;
rj := TJclRegEx.Create;
rj.Options := [roIgnoreCase];
rj.Compile(rxpr, false);
for i := 1 to cnt do
begin
p := 1;
while rj.Match(txt, p) do
begin
s := rj.Captures[0];
p := rj.CaptureRanges[0].LastPos + 1;
end;
end;
rj.Free;
CounterStop;
res := res + ‘TJclRegEx: ‘ + CounterValueAsString + #13#10;
//Bilsen
s := »;
CounterStart;
re2 := uRegularExpressions.TRegex.Create(rxpr, [uRegularExpressions.TRegExOption.roIgnoreCase]);
for i := 1 to cnt do
begin
m2 := re2.Match(txt);
while m2.Success do
begin
s := m2.Groups[0].Value;
m2 := m2.NextMatch;
end;
end;
CounterStop;
res := res + ‘TRegEx (Erik Van Bilsen): ‘ + CounterValueAsString + #13#10;
//Build-in XE+
s := »;
CounterStart;
re := RegularExpressions.TRegEx.Create(rxpr);
for i := 1 to cnt do
begin
m := re.Match(txt, rxpr, [RegularExpressions.TRegExOption.roIgnoreCase]);
while m.Success do
begin
s := m.Groups[0].Value;
m := m.NextMatch;
end;
end;
CounterStop;
res := res + ‘TRegEx (XE5): ‘ + CounterValueAsString + #13#10;
ShowMessage(res);
end;
//Проверка поддержки русского языка
procedure TForm2.bCheckRussianClick(Sender: TObject);
const
RA = ‘АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя’;
var
rj: TJclRegEx;
r: TRegExpr;
re: RegularExpressions.TRegEx;
re2: uRegularExpressions.TRegex;
s, txt, rxpr: String;
i, p: Integer;
res: String;
begin
txt := ‘Ю’;
rxpr := ‘[\w]’; //А-Яа-я
res := »;
//TRegExpr
r := TRegExpr.Create;
r.Expression := rxpr;
r.ModifierR := true;
r.WordChars := r.WordChars + RA;
if r.Exec(txt) then
res := res + ‘TRegExpr: OK’ + #13#10
else
res := res + ‘TRegExpr: ERROR’ + #13#10;
//JCL
rj := TJclRegEx.Create;
rj.Compile(rxpr, false, true);
if rj.Match(txt) then
res := res + ‘TJclRegEx: OK’ + #13#10
else
res := res + ‘TJclRegEx: ERROR’ + #13#10;
rj.Free;
//Build-in XE+
if RegularExpressions.TRegEx.IsMatch(txt, rxpr) then
res := res + ‘TRegEx (XE5): OK’ + #13#10
else
res := res + ‘TRegEx (XE5): ERROR’ + #13#10;
//Bilsen
if uRegularExpressions.TRegex.IsMatch(txt, rxpr) then
res := res + ‘TRegEx (Bilsen): OK’ + #13#10
else
res := res + ‘TRegEx (Bilsen): ERROR’ + #13#10;
ShowMessage(res);
end;
P.S. Ссылка «You can use these HTML tags» пустая и предпросмотра нет, так что оформить пост красиво не получилось.
Ответить
Харитонов
07 / 11 / 14
Андрей, спасибо за анализ скорости… наткнулся на те же самые грабли когда кириллица и ‘\n’ в одном шаблоне не работает.
Нашёл временное решение, заменил \n на [^.][^.]
Я так понял, ошибка возникает когда русские символы и одновременно слеш ‘\’ присутствует. Вобщем это баг 100% (Embarcadero или кто там не знаю). Пока удалось заменить ‘\’ на аналог, но нет гарантий, что в будущем это спасёт. А поскольку преимуществ у стандартной библиотеки Delphi перед RegExpr Андрея Сорокина нет, тогда буду потихонечку переходить на неё.
Моя конфигурация:
— Delphi XE 3
— Win 7, 32 bit
Если у кого получится решить эту проблему — напишите здесь или дайте ссылку. Спасибо.
Ответить
Максим
11 / 28 / 14
Сообщаю о том, что возможное (приемлемое с некоторыми оговорками) решение найдено.
Говорю сразу, я — не профессиональный программист, поэтому решение, которое я нашел, кому-то из профессионалов может показаться идиотским. Тем не менее, опишу, так как оно работает: вдруг кому-то будет полезно.
Я запилил две функции. Одна — кодирует русские буквы в последовательность из трех символов (в моем случае: латинская буква и две цифры). Другая — декодирует эти последовательности обратно в русские буквы. Обе функции в качестве аргумента получают строку и возвращают строку. При этом, кодирующая функция любые символы, кроме русских букв ([‘а’..’Я’]) в исходной строке (в полученном аргументе) переносит в строку-результат без изменений. Декодирующая же функция только в случае полного совпадения трехсимвольной последовательности в исходной строке (в аргументе) с принятой мною кодировкой русских букв, выполняет декодирование, а в противном случае, — опять-таки, простое копирование в строку-результат.
Кодирующая функция у меня называется RuToTrn, декодирующая — TrnToRu. Для использования в «родном» Дельфийском TRegEx, понятное дело, кодируется как строка, в которой нужно искать, так и строка, содержащая само регулярное выражение (паттерн).
Выражение TRegEx.IsMatch(RuToTrn(‘наглая и глазастая’), RuToTrn(‘\b(гла)+’)) возвращает True, и находит именно слово «глазастая», то есть в шаблоне (паттерне) — русские буквы совместно с метасимволами, и в то же время, все нормально работает.
Понятно, что в случае, если в исходной строке будут присутствовать (среди прочих символов) такие последовательности, которые у меня являются кодами русских букв, — то это приведет к ошибкам. Но я использую такие последовательности, вхождение которых в обычные англоязычные тексты, не являющиеся программным кодом, маловероятно. Поэтому такое решение считаю приемлемым для моей задачи.
Ответить
Харитонов
07 / 11 / 14
Андрей, если не трудно — в двух словах напишите, каких возможностей вам стало не хватать в библиотеке Андрея Сорокина. Спасибо.
Ответить
Андрей
07 / 11 / 14
Метасимвол \b (граница слова), пассивные группы и просмотр вперед/назад.
\b не заработал с русскими символами, на StackOverflow нашел ему замену, но с помощью как раз просмотров. Пассивные группы просто облегчают разбор результата при большом количестве скобок.
А вообще можно сравнить библиотеки Сорокина и, например, by Dave Child.
P.S. Ха, библиотека Сорокина поддерживает метасимвол «граница слова» — оказывается, у меня была старая версия демки + распечатка описания синтаксиса библиотеки без описания этого метасимвола. Что ж Вы раньше то не спросили… :)
Ответить
google.com Bogdan Sirenko
02 / 04 / 16
Уважаемые программисты на Delphi. Я вас очень прошу, когда вы пишете статью, пожалуйста, и про модули, которые вы используете в проекте, не забывайте написать!!!!!
Ответить
Vlad
02 / 04 / 16
Что непонятно?
RegularExpressionsAPI.pas -обёртка для библиотеки PCRE. PCRE — это библиотека содержащая ряд функций для работы с регулярными выражениями синтакис которых максимально предлижен к синтаксису регулярных выражений в Perl 5.
RegularExpressionsCore.pas — как становится поняным из названия — модуль представляет собой некое ядро вокруг которого и крутится вся работа с регулярками. Так, например, в модуле RegularExpressionsCore содержася такие «говорящие» классы как TPerlRegEx, TPerlRegExOptions и т.д. Соответственно этот модуль опирается в своей работе на функции из RegularExpressionsAPI.pas.
RegularExpressionsConsts.pas — содержит ресурсные строки, используемые при выводе сообщений об ошибках при работе с регулярными выражениями.
RegularExpressions.pas — модуль в котором содержаться всё необходимое для работы с регулярными выражениями в Delphi XE. Естественно модуль опирается на все три предыдущих.
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#21 dyvniy » Чт, 30 июня 2016, 11:34:37

Файловые потоки
http://pro.dtn.ru/delphi-net/file3.html
Спойлер
Класс TFileStream
Самым лучшим инструментом для записи данных в файлы и чтения данных из них является класс TFileStream (этот класс объявлен в модуле Classes). Класс TFileStream позволяет выполнять следующие действия:

Читать или записывать любую разновидность данных.
Читать или записывать компоненты.
Копировать данные в другой поток.
Чтобы начать чтение или запись данных в файл, вы должны создать экземпляр класса TFileStream. Конструктор TFileStream принимает два параметра:

constructor Create(const FileName: string; Mode: Word);

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

Обычные режимы открытия TFileStream
Константа Описание
fmCreate

Создание нового файла или открытие файла для записи, если он уже существует.
fmOpenRead Открытие файла только для чтения.
fmOpenWrite Открытие файла только для записи.
fmOpenReadWrite Открытие файла для чтения и записи.
Чтение и запись выполняются с помощью методов Read и Write. Оба метода принимают два параметра:

function Read(var Buffer; Count: Longint): Longint;
function Write(const Buffer; Count: Longint): Longint;

Метод Read считывает байты Count из файла и помещает данные в переменную Buffer. Метод Write записывает байты Count из переменной Buffer в файл. Оба метода обновляют свойство Position, которое представляет позицию потока.

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

procedure TForm1.Button1Click(Sender: TObject);
var
fs: TFileStream;
n: Integer;
s: string;
begin
fs := TFileStream.Create('sample.txt', fmCreate);
try
fs.WriteComponent(Label1);
n := 5650;
fs.Write(n, SizeOf(Integer));
{ При записи строк необходимо сообщить методу Write о том,
что запись должна начинаться с первого символа }
s := 'Some text';
fs.Write(s[1], Length(s));
finally
fs.Free;
end; // завершение конструкции try (fs)
end;
Большое количество компонентов VCL умеют загружать данные из потока (метод LoadFromStream) или сохранять данные в поток (метод SaveToStream). В следующем примере демонстрируется применение метода SaveToStream для сохранения содержимого двух компонентов ТМеmо в одном текстовом файле.

procedure TForm1.Button2Click(Sender: TObject);
var
fs: TFileStream;
begin
fs := TFileStream.Create('sample2.txt', fmCreate);
try
Memo1.Lines.SaveToStream(fs);
Memo2.Lines.SaveToStream(fs);
finally
fs.Free;
end;
end;

Копирование данных из одного потока в другой выполняется с помощью метода CopyFrom. Метод CopyFrom принимает два параметра: исходный поток и количество байтов, которые будут скопированы из одного потока в другой.

Следующий пример показывает, насколько просто копировать файл с помощью класса TFileStream.

procedure TForm1.Button3Click(Sender: TObject);
var
src, dest: TFileStream;
begin
src := TFileStream.Create('sample.txt', fmOpenRead);
try
dest := TFileStream.Create('dest.txt', fmCreate);
try
// копирование файла
dest.CopyFrom(src, src.Size)
finally
dest.Free;
end; // завершение конструкции try (dest)
finally
src.Free;
end; // завершение конструкции try (src)
end;
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#22 dyvniy » Чт, 30 июня 2016, 15:46:31

Про выделение памяти
http://www.sql.ru/forum/887028/tmemorystream-d7-chto-za-ogranichenie
Спойлер
Есть такая ситуация: формируется большой отчет, страницы отчета засовываются в TMemoryStream (каждая страница в один поток), сами потоки добавляются в список. На ~65520-ой странице вываливается "Out of memory while expanding memory stream" хотя памяти еще полно. vmmap показывает кучу свободной памяти любых размеров блоков (т.е. фрагментации вроде особо нет).
Ошибку вызывает метод TMemoryStream.Realloc когда не получает указатель от GlobalAllocPtr (или GlobalReallocPtr - не суть)
Смоделировал ситуацию в примере:
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
p8,p2:pointer;
list :TList;
tc:cardinal;
begin
list := TList.create;
try
tc:=GetTickCount;
hh:=GetProcessHeap;
for i:=0 to 100000 do
begin
p8 := GlobalAllocPtr(2,8192);
p2 := GlobalAllocPtr(2,2048);

if p2 <> nil then
list.Add(p2);
GlobalFreePtr(p8);
if p2 = nil then
raise Exception.Create('p2 error '+inttostr(i));
if p8 = nil then
raise Exception.Create('p8 error '+inttostr(i));
end;
tc:=GetTickCount-tc;
caption:=inttostr(tc);
finally
for i := 0 to list.Count-1 do
GlobalFreePtr(list[i]);
list.Free;
end;
end;
В реальном приложении память выделяется именно такими блоками и именно в такой последовательности.
Тестовый пример также падает на 65529 проходе.

В Delphi XE2 в методе TMemoryStream.Realloc вызовы GlobalAllocPtr и GlobalReallocPtr заменены на
GetMem и ReallocMem. Попробовал сделать так-же в Delphi7 - получил сильные тормоза. В MSDN сказано что в новых приложениях положено юзать функции HeapAlloc... Переписал тест:
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
p8,p2:pointer;
list :TList;
tc:cardinal;
hh:THandle;
begin
list := TList.create;
try
tc:=GetTickCount;
hh:=GetProcessHeap;
for i:=0 to 100000 do
begin
p8 := HeapAlloc(hh,0,8192);
p2 := HeapAlloc(hh,0,2048);

if p2 <> nil then
list.Add(p2);
HeapFree(hh,0,p8);
if p2 = nil then
raise Exception.Create('p2 error '+inttostr(i));
if p8 = nil then
raise Exception.Create('p8 error '+inttostr(i));
end;
tc:=GetTickCount-tc;
caption:=inttostr(tc);
finally
for i := 0 to list.Count-1 do
HeapFree(hh,0,list[i]);
list.Free;
end;
end;
Работает даже быстрее чем GlobalAllocPtr и не валится.
Можно ли смело поменять вызовы в TMemoryStream.Realloc на Heap... ? (Могут ли быть "нестыковки" на счет потокобезопасности и ...?)
Что скажете ?
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#23 dyvniy » Пт, 8 июля 2016, 13:15:01

Пространства имён
unit MyCompany.MyWidgets.MyUnit;
http://www.delphiplus.org/programirovanie-v-srede ... rostranstva-imen-v-delphi.html
Спойлер
В Delphi 8 файлы проекта (программы, библиотеки или пакеты) неявно входят в пространство имен, называемое пространством имен проекта по умолчанию. Модуль может быть или членом пространства имен по умолчанию, или для него можно явно объявить пространство имен. Во втором случае пространство имен объявляется в заголовке модуля, unit MyCompany.MyWidgets.MyUnit;

Обратите внимание, что отдельные пространства имен разделяются точками. Но в данном случае точки не являются разделителями между именами, а являются частью имени модуля. Имя исходного файла для данного модуля должно быть MyCompany.MyWidgets.MyUnit.pas, a имя вылодного файла, созданного компилятором, будет MyCompany. MyWidgets .MyUnit .deuil. Точки в данном случае показывают вложенность пространств имен, а модуль MyUnit является членом пространства имен MyWidgets, которое само включено в пространство имен MyCompany.

По умолчанию всегда создаются пространства имен для всех модулей проекта. Рассмотрим следующие объявления.

Program MyCompany.Programs.MyProgram; Library MyCompany.Libs.MyLibrary; Package MyCompany.Packages.MyPackage ;

Таким образом создается пространство имен по умолчанию для проекта. Имя пространства имен получается при удалении имени модуля (правая часть) вместе с точкой. Модуль, в котором нет явного объявления пространства имен, называется групповым модулем, он автоматически становится членом пространства имен (членом группы) проекта. Например, если создать модуль как unit MyUnit;

то компилятор автоматически включит его в пространство имен MyCompany. Programs.

При этом пространство имен по умолчанию не влияет на имена исходных файлов групповых модулей. В предыдущем примере имя файла может быть MyUnit.pas, но при создании файла с расширением .deuil компилятор будет использовать пространство имен по умолчанию и будет создан файл с именем MyCompany.Programs.MyUnit.deuil.

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

Поиск пространства имен

В модуле могут объявляться (подключаться) другие модули, от которых этот модуль зависит. Как и для платформы Win32, компилятор Delphi 8 для .NET должен идентифицировать эти модули. В случае с явным объявлением имен путь всегда известен, но если это групповой модуль, то компилятор должен определить область поиска подключенных модулей. Рассмотрим следующий пример модуля с явным объявлением пространства имен и подключенными модулями (для чего используется директива uses).

unit MyCompany.Programs.Units.MyUnitl; ■Jses MyCompany.Libs.Unit2, Unit3, Unit4;

"Здесь модуль MyUnitl является членом пространства имен MyCompany. Programs .Units. Модуль MyUnitl зависит от трех других модулей: модуля Unit2 в пространстве имен MyCompany.Libs и двух групповых модулей Unit3 и Unit4.

Компилятор может легко установить полное имя модуля Unit2, но для нахождения полных имен модулей Unit3 HUnit4 компилятор должен произвести поиск среди соответствующих пространств имен. Для поиска используются источники: опции компилятора, объявленные в файле проекта пространства имен, пространства имен по умолчанию и, наконец, текущее пространство имен для данного модуля.

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

Компилятор будет производить поиск в следующем порядке:

■ текущее пространство имен модуля;

■ пространство имен по умолчанию;

■ список пространства имен проекта;

■ пространства имен, указанные в опциях компилятора.

Ниже приведен пример файла проекта и модуля с объявлением списка пространств имен для поиска файла.

// Файл проекта.

program MyCompany.Programs.MyProgram;

namespaces MyCompany.Libs.Uiwictgets, MyCompany.Libs.Network; // Файл модуля.

unit MyCompany.Programs,Units.MyUnitl;

Используя эти объявления, компилятор будет производить поиск в следующем порядке:

■ MyCompany.Programs.Units;

■ MyCompany.Programs;

■ MyCompany,Libs.Network;

■ MyCompany.Libs.UlWidgets;

■ Пространство имен, указанное в опциях компилятора.

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

В примере ниже показано, как можно подключить модули, unit MyCompany.Libs.MyUnitl

uses MyCompany.Libs.Unit2, // Полностью указанное имя.

UnitX; // ГрупПОВОе ИМЯ.

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

uses MyCompany.Libs.Unit2;

begin

writeln(MyCompany.Libs.Unit2.SomeString),-writeln(SomeString); end.

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

writeln(Unit2.SomeString); // Ошибка !

writeln(Libs.Unit2.SomeString) ; // Ошибка !

writeln (MyCompany .Libs .Unit2 . SomeString) ,- / / Правильно.

writeln(SomeString); // Правильно.

Ссылка только на часть пространства имен при подключении модулей также будет ошибкой.

uses MyCompany; // Ошибка!

Чтобы не набирать очень длинные имена, можно объявлять псевдонимы и использовать их локально в модуле. Сделать это можно так.

uses MyCompany. AVeryLongNamespaceDesignation. VeryDesoript iveUni tName as aUrtit ;

После чего на имя можно ссылаться с помошью псевдонима. Ниже приведены два эквивалентных утверждения, writeln(aUnit.SomeString);
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 6 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#24 dyvniy » Пн, 11 июля 2016, 13:59:23

Ресурсы
http://delphiru.ru/resources/285-assign-resources
Спойлер
В этой статье я попытаюсь объяснить как включать файлы в приложения Delphi как различные виды ресурсов и как управлять ими.

Можно включить любой файл в исполнимый файл для использования как ресурс. Некоторые виды ресурсов признаются API и могут использоваться непостредственно. Другие просто принимаются как двоичные данные. В этой статье мы рассмотрим примеры обоих видов.

Чтобы создать файл ресурса, мы начинаем с исходного файла *.RC, например, по имени Resources.rc, который содержит виды ресурсов (имя, класс и файл).


sample_bmp BITMAP sample.bmp
sample_ico ICON sample.ico
sample_cur CURSOR sample.cur
sample_ani ANICURSOR sample.ani
sample_jpg JPEG sample.jpg
sample_wav WAVE sample.wav
sample_txt TEXT sample.txt
Имена ресурсов (sample_bmp, sample_ico и т.д.) произвольны. Вид ресурса может быть поддерживаемый API (BITMAP, ICON, CURSOR) или произвольный (JPEG, WAVE, TEXT). Имена файла определяют файлы, которые будут включены в .RES, а позже .EXE.

Теперь мы должны скомпилировать .RC файл, чтобы получился .RES файл. Для этого мы можем использовать Borland Resource Compiler (brcc32.exe).

Набрав в командной строке, мы получим:


C:\DELPHI\P0025>brcc32 resources
Borland Resource Compiler Version 5.40
Copyright (c) 1990, 1999 Inprise Corporation.
All rights reserved.

C:\DELPHI\P0025>_
Для того, чтобы присоединить файл ресурсов к нашему проекту, мы используем директиву {$R} или {$RESOURCE}:


{$R resources.res}
Загрузка поддерживаемых ресурсов (BITMAP, CURSOR, ICON) проста, так как Windows API предоставляет нам функции LoadBitmap, LoadCursor, LoadIcon соответственно для получения дескрипторов этих элементов.


Image1.Picture.Bitmap.Handle := LoadBitmap(hInstance, 'sample_bmp');
Icon.Handle := LoadIcon(hInstance, 'sample_ico');
Screen.Cursors[1] := LoadCursor(hInstance, 'sample_cur');
Другие ресурсы использовать немного сложнее. Давайте начнем с изображения JPEG. Мы будем использовать функцию TResouceStream, чтобы загрузить ресурс как поток, который будет загружен в объект TJpegImage.


function GetResourceAsJpeg(const resname: string): TJPEGImage;
var
Stream: TResourceStream;
begin
Stream := TResourceStream.Create(hInstance, ResName, 'JPEG');
try
Result := TJPEGImage.Create;
Result.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
Пример:


var
Jpg: TJPEGImage;
begin
// ...
Jpg := GetResourceAsJpeg('sample_jpg');
Image2.Picture.Bitmap.Assign(Jpg);
Jpg.Free;
// ...
end;
Для WAV файлов мы нуждаемся в указателе на ресурс, загруженный в память, а для текстового файла мы должны загрузить ресурс в строку. Мы можем сделать это, используя TResourceStream, но давайте посмотрим пример, который использует API:


function GetResourceAsPointer(ResName: pchar; ResType: pchar;
out Size: longword): pointer;
var
InfoBlock: HRSRC;
GlobalMemoryBlock: HGLOBAL;
begin
InfoBlock := FindResource(hInstance, resname, restype);
if InfoBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
size := SizeofResource(hInstance, InfoBlock);
if size = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
GlobalMemoryBlock := LoadResource(hInstance, InfoBlock);
if GlobalMemoryBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
Result := LockResource(GlobalMemoryBlock);
if Result = nil then
raise Exception.Create(SysErrorMessage(GetLastError));
end;

function GetResourceAsString(ResName: pchar; ResType: pchar): string;
var
ResData: PChar;
ResSize: Longword;
begin
ResData := GetResourceAsPointer(resname, restype, ResSize);
SetString(Result, ResData, ResSize);
end;
Примеры вызовов:


var
sample_wav: pointer;

procedure TForm1.FormCreate(Sender: TObject);
var
size: longword;
begin
{ ... }
sample_wav := GetResourceAsPointer('sample_wav', 'wave', size);
Memo1.Lines.Text := GetResourceAsString('sample_txt', 'text');
end;
Как только мы загрузим WAV ресурс в память, мы можем его проигрывать столько раз, сколько нужно, используя sndPlaySound, объявленную в модуле MMSystem.


procedure TForm1.Button1Click(Sender: TObject);
begin
sndPlaySound(sample_wav, SND_MEMORY or SND_NODEFAULT or SND_ASYNC);
end;
Есть некоторые ресурсы (типа шрифтов, анимированных курсоров), которые не могут использоваться из памяти. Мы обязательно должны сохранить эти ресурсы во временном файле на диске и загружать их оттуда. Следующая функция сохраняет ресурс в файл:


procedure SaveResourceAsFile(const ResName: string; ResType: pchar;
const FileName: string);
begin
with TResourceStream.Create(hInstance, ResName, ResType) do
try
SaveToFile(FileName);
finally
Free;
end;
end;
Следующая функция использует предыдущую, чтобы сохранить ресурс во временном файле:


function SaveResourceAsTempFile(const ResName: string;
ResType: pchar): string;
begin
Result := CreateTempFile;
SaveResourceAsFile(ResName, ResType, Result);
end;
Следующая функция использует SaveResourceAsTempFile, чтобы сохранить ресурс анимированного курсора во временном файле, затем загризить этот ресурс из файла при помощи LoadImage и затем удалить временный файл. Функция возвращает дескриптор, возвращенный функцией LoadImage.


function GetResourceAsAniCursor(const ResName: string): HCursor;
var
CursorFile: string;
begin
CursorFile := SaveResourceAsTempFile(ResName, 'ANICURSOR');
Result := LoadImage(0, PChar(CursorFile), IMAGE_CURSOR, 0,
0, LR_DEFAULTSIZE or LR_LOADFROMFILE);
DeleteFile(CursorFile);
if Result = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
Изображение


Название раздела: Программирование (под Desktop и Android)
Описание: Разработка и отладка приложений. Упор на 3D-графику.

Быстрый ответ


Введите код в точности так, как вы его видите. Регистр символов не имеет значения.
Код подтверждения
:) ;) :hihi: :P :hah: :haha: :angel: :( :st: :_( :cool: 8-| :beee: :ham: :rrr: :grr: :* :secret: :stupid: :music: Ещё смайлики…
   

Вернуться в «Программирование (под Desktop и Android)»

Кто сейчас на форуме (по активности за 15 минут)

Сейчас этот раздел просматривают: 104 гостя