На главнуюВ раздел программ

     
 

Обмен опытом


Мои программные модули
1) MyStrUnit.pas (версия 1.11)
2) MemRich.pas

Методы Online
Процедура DelNilObjs.
Функция MyExtendedToSTr.





1) MyStrUnit.pas версия 1.11 (Скачать). Описание: Содержит функции работы со строками (формата ANSI): сравнение строк, сравнение частей строк, поиск подстроки в строке начиная с определённой позиции в прямом или обратном направлении. Почти все написаны на ассемблере (т.е. работают быстрее, чем при написании на паскале). Кроме того, важной особенностью моих функций является отсутствие каких-либо промежуточных преобразований переданных в них строк (например, приведения к одному регистру), причём даже в тех из них, которые работают в режиме независимости от регистра. Вот пример, насколько экономичней выполняются мои функции по сравнению со сходными в дельфи :

Моя AnsiCompareTextAsm(s1,s2) = выполняется за 46 процессорных команд.
Delphi7 AnsiCompareText(s1,s2) = выполняется за 734 процессорных команды.
(При подсчёте в s1 была записана строка из 2-х символов, в s2 - та же строка, но в противоположном регистре).

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

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


Перечень методов, содержащихся в MyStrUnit.pas:

AnsiPosTextExAsm - Не чувствительный к регистру поиск подстроки subs в строке s начиная с заданной позиции.
AnsiPosTextUpExAsm - Не чувствительный к регистру обратный поиск подстроки subs в строке s начиная с заданной позиции (то же, что и AnsiPosTextExAsm, но ищет подстроку subs от конца строки s к её началу).
AnsiPosStrExAsm - Поиск с учётом регистра подстроки subs в строке s начиная с заданной позиции.
AnsiPosStrUpExAsm - Обратный поиск (от конца строки s к её началу) с учётом регистра подстроки subs в строке s начиная с заданной позиции.
NextStrPosAsm - Быстрый поиск конца строк в многострочном тексте начиная с заданной позиции.
AnsiSameTextAsm - Сравнение двух строк на полное совпадение без учёта регистра.
AnsiSameStrAsm - Сравнение двух строк на полное совпадение с учётом регистра.
AnsiSameCharTextAsm - Сравнение частей двух строк без учёта регистра. Позволяет сравнить не строки целиком, а только их части, заданные начальными позициями в обеих строках и числом сравниваемых символов. Благодаря этому можно избежать копирования подстрок в отдельные строки перед их сравнением - соответственно не тратится время на выделение памяти под эти подстроки, на пересылку данных в них.
AnsiSameCharStrAsm - Сравнение частей двух строк с учётом регистра.
AnsiCompareTextAsm - Сравнение двух строк без учёта регистра. Отличается от AnsiSameTextAsm тем, что сравнивает строки, даже если они не равны по длине, и возвращает результат типа integer, а не Boolean, что позволяет использовать данную функцию в процедурах сортировки строк.
AnsiCompareStrAsm - Сравнение двух строк с учётом регистра.

Функции, добавленные в версию 1.04
IsItDigitsSAsm - Возвращает True, если строка не пустая и содержит только символы цифр.
DigitsNumCharAsm - Возвращает число цифр, содержащееся в указанной позиции строки (т.е. от указанной позиции до первого символа не цифры).
ReflectStrAsm - Посимвольно отражает данные в строке (например, если было "12345", станет "54321"). Внимание!! Перед использованием данной процедуры обязательно прочтите инфо к ней в юните.
LengthOnTrimRightAsm - Возвратит, какая у строки будет длина, если выполнить для неё TrimRigth.
TrimRightAsm - Аналог дельфёвой TrimRight, только это процедура, а не функция, написана она, сказать по правде, не на ассемблере, но базируется на ассемблерной функции LengthOnTrimRightAsm.

Функции, добавленные в версию 1.05
ThisStrPosAsm - Возвращает позицию начала строки по позиции любого символа, принадлежащего этой строке в многострочном тексте.
FindSymbolPosCSAsm - Зависимый от регистра поиск символа Symb в строке S начиная с заданной позиции.

Функции, добавленные в версию 1.06
AnsiUpperCaseAsm - Ассемблерный аналог дельфёвой AnsiUpperCase.
AnsiPosTextExAsmEx - Расширенный вариант AnsiPosTextExAsm. Позволяет искать подстроку в тексте от указанной позиции до указанной позиции.

Методы, добавленные в версию 1.10
SetBigCaseCahrAsm, SetFirstBigCase - Изменяют соотвественно заданый символ или первый символ стоки на заглавный.
TrimRightAsmSp - Удаляет символы пробелов справа, в отличие от Delphi7 TrimRight не создаёт копию строки, а возвращает её саму (не вызывает UniqueString) если пробелов справа не было.
LengthOnTrimLeftAsm - Возвратит, какая у строки будет длина, если ей выполнить TrimLeft.
TrimLeftAsm, TrimAsm - Выполняют TrimLeft/TrimAsm только если в этом есть необходимость, иначе ничего не делают, т.е. не создают копию строки.
TrimDelWSpace - Удаляет пробелы из начала и конца и все сдвоенные и более пробелы внутри строки, если такие есть, заменяя их одиночными. Возвращает True, если строка была изменена.
MoveSDataSp - Перемещает заданное число байт данных в строке S из позиции с индексом FromInd в позицию ToInd. Перемещение производится в памяти, поэтому не приводит к вызову UniqueString.
MovsSChar - Заменяет символ с указаным индексом в строке на заданный. Делает это прямо в памяти, поэтому не приводит к разделению строк, если одна и та же строка хранится в нескольких переменных (не приводит к вызову UniqueString).
ReplaceStrSp - Заменяет в строке S подстроку, находящуюся в промежутке между подстроками Marker, на подстроку sub. Можно заменять одну подстроку с определённым индексом или все маркированные подстроки разом.
FindFirstDigitAsm - Ищет символ цифры в строке S. Возвращает позицию первой от начала строки цифры или 0, если цифр в строке нет.
FindFirstDigitPosAsm - Ищет символ цифры в строке S начиная с позиции wPos. Возвращает позицию цифры в строке или 0, если не найдено.
DigitsNumPosAsm - Возвращает число цифр начиная с позиции wPos в строке S до первого символа не цифры.

Функции, добавленные в версию 1.11
ReplaceSymbFor - Заменяет все символы Symb в строке S на символ SymbFor, возвращает число заменённых симоволов или 0, если ничего не заменено.
GetThisPosStr - Возвращает из многострочного текста S строку из позиции wPos, смещая wPos на начало следующий строки.







2) MemRich.pas (Скачать) (посмотреть Online). Описание: этот юнит - скорее справка, чем что-то ещё. Он содержит примеры функций, использующих некоторые виды EM_ сообщений для работы с TMemo и TRichRdit.



Методы online

Процедура DelNilObjs

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

//Удаляет из массива объектов класса TObject все элементы, равные nil
//и урезает массив, чтобы в нём остались только существующие элементы массива
Procedure DelNilObjs(var wObjAr:TObjAr);//TObjAr = array of TObject
var Lok1,LCnt:Integer;

  Function LLDelNilItems(const wObjAr:TObjAr; const size:Integer):Integer;assembler;
  // EAX - указатель на первый элемент массива
  // EDX - параметр size
  asm
  PUSH ESI
  PUSH EDI
  cld
  MOV EDI, EAX //Записываем в EDI указатель на первый элемент массива
  OR EAX, EAX
  JZ @@EXIT //если EAX = 0 (если указатель = nil), то выходим

  MOV ECX,EDX //пересылаем size в ECX
  XOR EAX,EAX //EAX := 0
  REPNZ SCASD //Ищем первый nil-элемент
  JNZ @@EXIT

  //adrA - адрес точки, куда нужно будет перемещать данные
  //adrB - адрес точки начала данных, которые нужно будет перемещать
  //adrC - точка конца (последнего байта) данных, которые нужно будет переместить

  MOV EDX,EDI //adrA+4
  SUB EDX,4       //запоминаем adrA в EDX
  @@2:
  OR ECX,ECX
  REPZ SCASD   //Ищем первый не nil-элемент после nil-элемента
  JNZ @@1

  @@EXIT1:       //Если до конца массива всё заполнено nil
  MOV EAX,EDI //adrC+4
  SUB EAX,EDX //adrA от adrC+4 (т.е., получаем суммарное количество всех равных nil элементов)
  JMP @@EXIT

  @@1:
  PUSH EDI          //adrB+4
  REPNZ SCASD //Ищем первый nil-элемент после цепочки не Nil-элементов
  POP ESI             //adrB+4
  JNZ @@3          //Если до конца массива нет nil-элементов

  PUSH ECX        //запоминаем счётчик для возобновления цикла после перемещения данных
  MOV ECX,EDI //adrC+4
  SUB ECX,ESI   //adrC+4 - adrB+4
  SHR ECX,2      //Получаем число элементов, которые нужно будет переместить
  SUB ESI,4        //adrB
  XCHG EDI,EDX //EDI = adrA, EDX = адрес возобновления цикла после перемещения данных
  REP MOVSD   //Осуществляем перемещение данных

  POP ECX //восстанавливаем счётчик
  XCHG EDI,EDX //EDX = новывй adrA, EDI = адрес возобновления цикла после перемещения данных
  JMP @@2
  @@3:
  SUB ESI,4          //adrB
  MOV ECX,EDI  //adrC+4
  SUB ECX,ESI    //adrC+4 - adrB
  SHR ECX,2        //Получаем число элементов, которые нужно будет переместить
  MOV EDI,EDX  //adrA
  REP MOVSD     //Осуществляем перемещение данных

  MOV EAX,ESI
  SUB EAX,EDI
  @@EXIT:
  POP EDI
  POP ESI
  SHR EAX,2 //Делим результат на 4, чтобы получить результат в указателях, а не в байтах
  end;

begin
  lcnt:= Length(wObjAr);
  Lok1:= LLDelNilItems(wObjAr,Lcnt);
  if Lok1 > 0 then setLength(wObjAr,Lcnt-Lok1);
end;

 

Функция MyExtendedToSTr

//Преобразовывает Float в строку гарантированно нормального числового вида, всякие форматы типа научного не используются
//wPrecision - задаёт "точность числа", т.е. число значащих цифр (незначащими являются нули с той или иной стороны от числа, например в числе 0.000012301 значащими цифрами являются только 12301, а в числе 120000 только 12)
//если число не умещается в заданную точность, оно округляется: если у числа есть дробная часть, она укорачивается путём округления, если не вмещается сама целая часть, она округляется к числу с меньшим количеством значащих цифр, например из 12237 к 12240
//wDecimals - задаёт максимальное допустимое число цифр после запятой. Если дробная часть числа не вмещается в заданное число цифр, она округляется до заданного
//wSepar задаёт символ, разделяющий целую и дробную части. Если wSepar = 0, в качестве разделителя используется значение глобальной переменной винды DecimalSeparator
//Если не удалось конвертировать число в строку, возвращает пустую строку
//Максимальное число, которое может быть конвертировано, может иметь 18 значащих цифр и плюс к этому фиг знает сколько незначащих. Это кстати, соответствует формату чисел Extended - число Extended может содержать максимум 18 значащих цифр и очень много (возможно 9999) незначащих
//таким образом, если число состоит только из 18 значащих цифр, Extended может хранить его точное значение, даже если оно, к примеру 40-цифреное (1234567890123456780000000000000000000000 или 0.0000000000000000000000123456789012345678 - как можно видеть, точное дробное значение может быть более 18-тициферным, только если его целая часть равна 0)
//иначе Extended хранит округлённое значение, где целые числа округляются до 18 значащих цифр справа - остальные цифры становятся незначащими (нулями), а у дробных чисел соответственно дробная часть сокращается округлением, чтобы всё число целиком (целая и дробная части) имело не более 18 значащих цифр

Function MyExtendedToSTr(const wExt:Extended; const wPrecision:integer = 18; const wDecimals:integer = 9999; Const wSepar:Char = #0):String;
var Lrec:TFloatRec;
Ls:String;
lSepar:Char;
Ldecimals:integer;
begin
  if wSepar = #$0 then LSepar:=DecimalSeparator else LSepar:= wSepar;
  if ((wDecimals > 9999) or (wDecimals < 0)) then Ldecimals:= 9999 else Ldecimals:= wDecimals;
  FloatToDecimal(Lrec,wExt,fvExtended,wPrecision,LDecimals);
  result:= Lrec.Digits;
  if ((Lrec.Exponent = 32767) or (Lrec.Exponent = - 32768)) then result:=''
  else if ((Lrec.Exponent > 9999) or (Lrec.Exponent < -9999)) then result:= ''//Это на всякий случай - теперь числа, длиннее 9999 разрядных будут считаться ошибочными
  else if result = '' then result:= '0' //FloatToDecimal не возвращает ноль, поэтому его надо писать самому
  else if Lrec.Exponent <= 0 then begin
    SetLength(Ls,Lrec.Exponent*-1);
    if Length(Ls) > 0 then FillChar(Ls[1],Length(Ls),'0');
    result:='0'+LSepar+Ls+result;
  end else if Lrec.Exponent > 0 then begin
    if Lrec.Exponent > length(result) then begin
      SetLength(Ls,Lrec.Exponent-Length(result));
      FillChar(Ls[1],Length(Ls),'0');
      result:= result+Ls;
    end else if Lrec.Exponent < length(result) then insert(LSepar,result,Lrec.Exponent+1);
  end;
  if ((result <> '') and (result <> '0')) then begin
    if Lrec.Negative then result:='-'+result;
    case result[length(result)] of
      '0'..'9':asm nop end;
      else result:= '';
    end;
  end;

end;