Страница 1 из 1

Добавлено: 23 Август 2004, 17:02
Гость
Привет, всем!

Никто не реализовывал на кларе функцию поиска и замены в строке?
Скажем, берем на входе строку, а на выходе получаем строку, в которой все вхождения некоторой подстроки заменены на другую строку.
Типа как в PERL
$line =~ s/A/Б/g;
А то опять велосипед изобретать приходится.

---
С уважением, Власов Никита


(Добавление)

Присоединяюсь. Но у меня интерес такой - надо выполнить замены в большой (от 100К до 20М) строке. А в этом случае передача параметров в Instring и формирование вырезок из строки крайне тормозит выполнение. Может быть, кто разработал нечто подходящее?

---------------------------------------
C уважением,
Юрий Философов,
Главный программист
Корпорация "Диполь", Саратов
E-mail yufil@tacis-dipol.ru (служ)
yufil@mail.ru (дом)
ICQ#75924439

Привет.

Возьми RegExp на http://clarionlife.net

С уважением, Михаил

loop + matсh/instring + sub/[]
У меня такого полно, но смысла выделять это в СОВСЕМ отдельную процедуру не вижу.
Задачка для начинающих
Присоединяюсь. Но у меня интерес такой - надо выполнить замены в большой (от 100К до 20М) строке.
Это уже серьезно. Файл что-ли обрабатываешь?
Единственное, что приходит в голову - сбрасывай в файл и вызывай внешнюю задачу поиска и замены.
Правда на чтении/записи можешь потерять больше чем выиграешь.

Был-бы исходный текст нового match...
(У меня переведен на Clarion очень старый, еще времен TopSpeed C)

------------------------------------------------------------
Igor Gubin (igor@quantor.com)
Quantor-Soft Metal
Phone/Fax: (+7 095) 234 4905
WEB: http://www.metaldata.info
http://www.metaldata.ru
Файл что-ли обрабатываешь?
Да. И не один, много. Обработка в файле исключается - замен около десятка. Поэтому файл вычитывается, производится замена и пишется назад. Но обработка файла размером 12 мегабайт занимает около 10 минут. Долго...
Был-бы исходный текст нового match...
Меня и Instring устраивает. Проблема как раз в том, что если я задам

If Instring('xxx',S,1,1000) - происходит копирование значения S.
Собственно, версию Instring без копирования S (с параметром *String или *CString) сделать недолго.

Но потом... S=S[1:I-1] & Loc:Repl & S[I+n : Len(S)] - ещё куча операций с длинными строками. Вот от этих операций бы избавиться...

---------------------------------------
C уважением,
Юрий Философов
Написал: ClaList(2)

Добавлено: 23 Август 2004, 17:06
Гость
А чем тебя не устраивает вызов внешней утилиты (например BK Replace)?
Развитые возможности, текстовый файл конфигурации, параметры командной строки.
Все изменения (разных строк на разные строки) за один проход.
URL и хелп в приложении.
Для обработки внешних текстовых файлов ничего лучше не придумаешь.

------------------------------------------------------------
Igor Gubin

Слабовато... Мне надо в файле (это HTML в юникоде)

- заменить заголовок на свой;
- символы юникода заменить на ASCII ;
- исправить гиперссылки

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

<a href="...."
на другие по указанным правилам ;
- вставить пробелы в пустые ячейки таблиц - заменить

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

<TD></TD
> на

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

<TD>&nbsp;</TD>

- исправить ссылки на картинки, чтобы они указывали на правильный каталог;
- исправить некоторые дефекты форматирования. Например, вложенные списки и оторванные от текста буквы - в моих HTML бывает часто.
- ещё кой-что по мелочи :)

В общем, всё это работает. Но хотелось бы пошустрее. Cобственно, оптимизирую потихоньку...

---------------------------------------
C уважением,
Юрий Философов
Написал: ClaList(2)

Добавлено: 23 Август 2004, 19:13
Гость
Ручкамис, ручкамис...
Собственно у меня похожая ситуация, посему и использую Instring & Co.
Правда у меня файл не читается, а генерится, так что мне проще.

------------------------------------------------------------
Igor Gubin

А переписать "поисково-заменный" движек на сях не пробовал? У меня очень неплохо получалось выиграть в скорости когда-то.

--
Best regards,
Vadym mailto:vadim@softcreator.com
ICQ: 82308757
Написал: ClaList(2)

Добавлено: 24 Август 2004, 10:06
Гость
Да. И не один, много. Обработка в файле исключается - замен около десятка. Поэтому файл вычитывается, производится замена и пишется назад. Но обработка файла размером 12 мегабайт занимает около 10 минут. Долго...

Сколько-сколько?!! Что-то ты намудрил 100%-но!
Мои тесты показывают следующие скоростя:
INSTRING при поиске 10 символьной подстроки в 1МБ строке даёт порядка 1500 циклов в сек (P4-2400).
Строка и подстрока генерятся случайным набором символов, т.е. наихудший вариант.
Далее в цикле обычный INSTRING

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

    ! Searching
    locStart = 0
    locClockBegin = clock()
    loop locI = 1 to locItems
      locStart = instring(locSubStr, locStr, 1, locStart+1)
    .
    locClockEnd = clock()
Так что твои 12МБ должны обрабатываться в секунды или даже доли секунд. Ищи глюк.
Но потом... S=S[1:I-1] & Loc:Repl & S[I+n : Len(S)] - ещё куча операций с длинными строками. Вот от этих операций бы избавиться...

Ааа, эт другое дело! Не используй соединение строк.
Используй прямую запись в строку и будет тебе счастье :)
Т.е. примерно такое:

tmpJ = I-1
tmpS[ 1 : tmpJ] = S[1:I-1]
tmpI = tmpJ + 1
tmpJ = tmpI + len(Loc:Repl)
tmpS[ tmpI : tmpJ] = Loc:Repl
и т.д.

а в самом конце, после всех операций
S = tmpS[tmpI : tmpJ]
если нужно, или return tmpS[]

Использование прямого доступа даст тебе на порядки большую скорость, особенно если строка формируется в цикле или из многочисленных мелких кусочков.
Естетственно, даром ничего не бывает :) Здесь плата - память для врем.строки.

Сергей - chusha@mail333.com ; chusha@hotbox.ru

Привет всем

Сам недавно сталкивался ниже на мой взгляд удачное решение

Functions: Replace function
2004-06-04 -- Jonathan Kay

Newsgroups: comp.lang.clarion
I need a procedure something like this
ReplaceString(BigTextString,FindString,ReplaceString), where it looks for the FindString and replaces it with ReplaceString in the BigTextString
.
I'd be inclined to do away with the 'BigString'; there may be times you don't know how big to make it. Here's what I use - just prototype to return a STRING:

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

Replace PROCEDURE(STRING Text,STRING Find,STRING Replace,BYTE CS=FALSE)
Pos  UNSIGNED,AUTO
RVal ANY ! Do not add ,AUTO
  CODE
    LOOP
      Pos =
CHOOSE(~CS,INSTRING(UPPER(Find),UPPER(Text),1),INSTRING(Find,Text,1))
      RVal = RVal & CHOOSE(~Pos,Text,Text[1 : Pos-1] & Replace)
      Text = CHOOSE(~Pos,'',Text[Pos+LEN(Find) : LEN(Text)])
    UNTIL ~Text
    RETURN RVal
Сергей Половинкин s.polovinkin@vaz.ru (ДИС)

Попробуй использовать Windows API функции работы со строками, там все на указателях:

char *strstr( const char *string, const char *strCharSet );
есть версия и для юникода.
Но потом... S=S[1:I-1] & Loc:Repl & S[I+n : Len(S)] - ещё куча операций с длинными строками. Вот от этих операций бы избавиться...
Для того, чтобы избавиться от таких многократных перетаскиваний хвоста строки по памяти используй второй (приемный буфер), в который данные пиши всегда в конец.

Удачи!
__________________________________
Владимир Якимченко (IСQ 16 993 194)


(Добавление)

Всем спасибо. Буду претворять в жизнь :)

---------------------------------------
C уважением,
Юрий Философов
Написал: ClaList(2)

Добавлено: 25 Август 2004, 9:52
Гость

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

Replace PROCEDURE(STRING Text,STRING Find,STRING Replace,BYTE CS=FALSE)
 Pos  UNSIGNED,AUTO
 RVal ANY ! Do not add ,AUTO
   CODE
     LOOP
       Pos =
 CHOOSE(~CS,INSTRING(UPPER(Find),UPPER(Text),1),INSTRING(Find,Text,1))
       RVal = RVal & CHOOSE(~Pos,Text,Text[1 : Pos-1] & Replace)
       Text = CHOOSE(~Pos,'',Text[Pos+LEN(Find) : LEN(Text)])
     UNTIL ~Text
     RETURN RVal
Имхо, не совсем удачное решение!
1. UPPER-ы можно было-бы и перед циклом сделать.
2. Опять-же, везде куча лишних операций переписывания с одного места в другое длинных строк.
3. Использование ANY-переменных не решит проблему - скорость даже может упасть за счет использования ООП-механизма и универсального кода, не всегда оптимального в конкретных случаях. Опять-же - много лишних телодвижений внутри UFO-блока RTL по использованию менеджера памяти. А он, как уже неоднократно здесь отмечалось, частенько притормаживает при интенсивной работе с блоками памяти небольшого размера.

На мой взгляд, самое удачное решение (с точки зрения скорости работы) следующее:
- входная строка ВСЕГДА остается неизменной и в ней ТОЛЬКО ищем заменяемый фрагмент
- выходная строка формируется последовательно из фрагментов входной строки и заменяющей последовательности
- использовать прототипы нужных Кларион-функций только с параметрами-ссылками. В случае невозможности - использовать внутренние функции ядра RTL или соответствующие WinAPI-функции.

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

В принципе, вышеприведенный код вполне можно оптимизировать, если учесть замечания 1/2 и убрать совершенно ненужный последний оператор цикла. Изменив, естественно, соответствующим образом первый оператор цикла.

Написал: ClaList(2)

Добавлено: 25 Август 2004, 19:23
Гость

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

! StrFuncClass - класс для работы со строками

strfuncClass Class(),Type,MODULE('strfuncClass.clw'),Link('strfuncClass.Clw')
wd procedure(String locInStr,*String locOutStr, *Long locBegPos,<String locSeparator>),Long,Proc 
  !Возвращает слово из переданной строки начиная с заданной позации
  !и перемещает указатель на конец слова
  !начальные разделители пропускаются
  !По умолчанию Разделитель слов пробел
  !0 - удачное завершение
  !1 - неудачное завершение

wdn function(String locInStr,Long NWord,Long locPos=1,<*Long locEndPos>,<String locSeparator>),String 
     !Выделяет слово NWord из переданной строки
str_REPLACE procedure(*CSTRING,STRING,STRING)
strtr PROCEDURE(String Str, String strfrom, String strto),String

 .
С уважением, Андрей Истомин
Написал: ClaList(2)

Добавлено: 30 Август 2004, 10:45
Гость
Здравствуйте.

Решила поэкспериментировать с функцией замены вхождения в строке, и получилось следующее: если не надо вести поиск с учетом регистра, то самый быстрый результат у меня получился с использованием InString с пересылкой в выходной буфер через string slicing. Однако попытка поиска без учета регистра требует применения upper к остатку входного буфера и при большом числе вхождений ведет к резкой деградации скорости. Вторая функция реализована через string slicing и при поиске с учетом регистра в несколько раз медленнее, чем первая, зато скорость слабо зависит от числа вхождений искомой подстроки и терпимо работает без учета регистра (замедление 2-3 раза). Сделать же UPPER входной строке перед циклом меня не устраивает, так как результат тогда также будет UPPER. Можно, конечно, созлать еще один буфер, но речь то о длинных строках ...
На строке длиной 10 Мб (PIV - 1800) время выполнения примерно 160 тысяч замен в строке от 0.25 до 3 сек в зависимости от функции и чувствительности к регистру поиска (для Instring только с учетом регистра). Строка создавалась str = ALL('Какой нибудь текст',Длина)

С уважением, Марина.
P.S. Применение any для накапливания результата также приводит к деградации скорости при большом числе замен.

P.P.S. Функции тестировались пока не очень...

Пример в аттаче (c55ee-h)
Написал: ClaList(2)

Добавлено: 30 Август 2004, 10:45
Гость
Здравствуйте.

Решила поэкспериментировать с функцией замены вхождения в строке, и получилось следующее: если не надо вести поиск с учетом регистра, то самый быстрый результат у меня получился с использованием InString с пересылкой в выходной буфер через string slicing. Однако попытка поиска без учета регистра требует применения upper к остатку входного буфера и при большом числе вхождений ведет к резкой деградации скорости. Вторая функция реализована через string slicing и при поиске с учетом регистра в несколько раз медленнее, чем первая, зато скорость слабо зависит от числа вхождений искомой подстроки и терпимо работает без учета регистра (замедление 2-3 раза). Сделать же UPPER входной строке перед циклом меня не устраивает, так как результат тогда также будет UPPER. Можно, конечно, созлать еще один буфер, но речь то о длинных строках ...
На строке длиной 10 Мб (PIV - 1800) время выполнения примерно 160 тысяч замен в строке от 0.25 до 3 сек в зависимости от функции и чувствительности к регистру поиска (для Instring только с учетом регистра). Строка создавалась str = ALL('Какой нибудь текст',Длина)

С уважением, Марина.

P.S. Применение any для накапливания результата также приводит к деградации скорости при большом числе замен.
P.P.S. Функции тестировались пока не очень...
Пример в аттаче (c55ee-h)


Хмм. А это правильно?

findLen = size(_Poisk) ! Длина строки поиска
replLen = size(_Zamena) ! Длина строки замены
strlen = size(_Stroka) ! Длина входной строки

Вроде как Size вычисляется в момент компиляции. Сам недавно напоролся, что Size('Строка' & 'Строка') возвращал 0 .

А в примере пара источников торможения.

1. Стандартный Instring будет копировать входной параметр, чего бы очень не хотелось. При больших строках и скромной оперативке это чревато свопингом ...
2. Конкатенация тоже не очень уместна. Лучше сначала сложить в выходной буфер одну строку, а потом другую. А для копирования использовать что-нибудь типа memcopy.

Кстати, недавно встретил в примерчике

S &String
S &=String[I# :J#]

Иными словами, можно сделать ссылку на подстроку. Собрать очередь из ссылок на куски исходной строки и на строку замены. А потом склеить... Хотя, конечно, эффективности особой не прибавит...

---------------------------------------
C уважением,
Юрий Философов,
Главный программист
Корпорация "Диполь", Саратов
E-mail yufil@tacis-dipol.ru (служ)
yufil@mail.ru (дом)
ICQ#75924439

Написал: ClaList(2)

Добавлено: 30 Август 2004, 13:53
Гость
Из письма Олега Руденко от 13.05.01:
_______
По той-же причине эффективнее использовать для определения размера строки вместо оператора Len() инструкцию Size().
Причем эта инструкция правильно работает и со строками, переданными в качестве параметров в процедуры.
Оператор Size() не работает только в двух случаях:
- когда надо определить размер строки без хвостовых пробелов
L# = Len(Clip(Str))
- когда используется такая декларация параметров процедуры:
Tst(*? _Param) или Tst(? _Param)
________________
Я попробовала при разных размерах буфера - результат совпал с len().

1. Вторая функция InString не использует. А вот обойтись только одним буфером, т.е. во входной сразу класть результат можно через string slicing, если подстрока замены не превыщает подстроку поиска. Еще бы хотелось нормально возвращать результат без дублирования выходного буфера, но как для этого использовать ф-цию Cla$PushTemp, которую когда-то приводил Олег Руденко, так и не поняла (может он откликнется ?).

2. Сейчас заменила
buff[buffptr : buffptr + replLen] = _Zamena & _Stroka[strptr]
на
buff[buffptr : buffptr + replLen - 1] = _Zamena
buffptr = buffptr + replLen
buff[buffptr] =_Stroka[strptr]
Скорость воспроизводимо выросла (но немного, около 5%). memcopy попробую вечером, если будет не лень, но сомнительно, чтобы это было заметно быстрей, чем str1[a:b] =str2[a:b]. Вроде здесь не раз писали, что клашины функции довольно эффективны. Во всяком случае замена upper для одного символа на игры с chr(), val() и case symb ; of 'a' ; symb = 'A' к увеличению скорости не привели.
Вроде и так довольно шустро работает. Хотя, конечно , нет предела совершенству...
Кстати, недавно встретил в примерчике

S &String

S &=String[I# :J#]

Иными словами, можно сделать ссылку на подстроку. Собрать очередь из ссылок на куски исходной строки и на строку замены. А потом склеить... Хотя, конечно, эффективности особой не прибавит...
Если вхождений много, то ведь и new() будет время кушать.

С уважением, Марина.

Написал: ClaList(2)

Добавлено: 30 Август 2004, 18:07
Гость
Добрый день. Пишу сама себе.

Замена upper('S') на chr(toupper(val('S')) практически убрала замедление в скорости для регистронечуствительного поиска. Так что придется попробовать memcopy, но что-то прототипа не найду - бросьте, кому не лень.

Марина.

Например, так

memcopy(Ulong Dest,Ulong Src,Ulong Size),Name('_memmove')

Адрес куда писать, адрес откуда читать и длина ....

---------------------------------------
C уважением,
Юрий Философов
Написал: ClaList(2)

Добавлено: 31 Август 2004, 9:55
Гость
Для современных компьютеров выделение доп. буфера в 10Mb не является проблемой! А пересылка такого буфера из одной переменной в другую с одновременным UPPER - доли секунды, которые трятятся лишь ОДИН раз - перед входои в основной цикл обработки!
Если речь идет об очень больших обьемах, то целесообразно делать обработку таких данных порциями.
Так что, имхо, не вижу причин НЕ использовать доп.буфер для хранения UPPER-строки.

Кстати, можешь попробовать использовать вместо InString функцию Match - в ней учет регистра задается отдельным параметром, без необходимости преобразования входной строки.
Еще бы хотелось нормально возвращать результат без дублирования выходного буфера, но как для этого использовать ф-цию Cla$PushTemp, которую когда-то приводил Олег Руденко, так и не поняла (может он откликнется ?).
Вообще-то, не советую! Эти функции - очень специфичны и их работа не один раз менялась от версии к версии.
Уж лучше, если так необходимо, используй возврат результата не через Return а через параметр *STRING.
А через Return можно возвращать, к примеру, кол-во замен.

=============================
С уважением, Олег А. Руденко.
Oleg_Rudenko@mail.ru
Oleg_Rudenko@mail333.com
Библиотека DynaLib
http://dynalib.narod.ru

Спасибо. Быстрей всего получилось при пересылке одиночных символов использовать peek/poke через прмежуточную переменную, а замену копировать _memmove. Но выигрыш, в общем, всего раза в полтора.
И если вместо _memmove использовать peek/poke, почти тоже самое.

С уважением, Марина

(Добавление)

Peek/Poke для одиночных символов и для простых типов данных (BYTE/LONG) заменяется компилятором на обычную инструкцию типа MOV EAX,EBX.
И если вместо _memmove использовать peek/poke, почти тоже самое.
Для строк и структур Peek/Poke заменяется компилятором на вызов _memcpy.

=============================
С уважением, Олег А. Руденко
Написал: ClaList(2)