Что нового

Замена подстроки

elakt

Новичок
Сообщения
8
Репутация
0
Добрый день. Пытаюсь решить такую задачу.

Есть строка текста: "№1. ДЖОНСОН&ДЖОНСОН 200МЛ"

В этой строке мне нужно заменить символ амперсанда "&" (в примере выделен красным) на последовательность "&". Обязательное условие: замену проводить только там, где после амперсанда нет символа решетки "#". То есть нельзя трогать сочетания "&#".

В результате должно получиться: "№1. ДЖОНСОН&ДЖОНСОН 200МЛ".

Пробовал решить задачу так:

Код:
$sString = "№1. ДЖОНСОН&ДЖОНСОН 200МЛ"
$sResult = StringRegExpReplace ($sString, "&[^#]", "&")

Но в этом случае на выходе получаю: "№1. ДЖОНСОН&ЖОНСОН 200МЛ". То есть нужная буква "Д" заменилась на новое значение, вместе с амперсандом - заменились ДВА символа, а не один.

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

Подскажите, пожалуйста, как можно решить задачу?
 

Suppir

Продвинутый
Сообщения
967
Репутация
62
elakt [?]
Обязательное условие: замену проводить только там, где после амперсанда нет символа решетки "#"

Тебе помогут lookahead и lookbehind assertions:

(?=#)
впереди должна быть решетка

(?!#)
впереди не должно быть решетки

(?<=#)
сзади должна быть решетка

(?<!#)
сзади не должно быть решетки


Код:
$sString = "№1. ДЖОНСОН&ДЖОНСОН 200МЛ"
$sResult = StringRegExpReplace ($sString, "&(?!#)", "&")
ConsoleWrite($sResult & @CRLF)


(?!#) показывает, что после & нет символа #.





Добавлено:
Сообщение автоматически объединено:

Еще можно было твоим способом, то есть поймать не решетку (в твоем случае символ "Д") с помощью [^#], но тогда тебе нужно захватить этот символ в круглые скобки, а потом восстановить с помощью $1 (т.е. значение первых круглых скобок):

Код:
$sResult = StringRegExpReplace ($sString, "&([^#])", "&$1")
 
Автор
E

elakt

Новичок
Сообщения
8
Репутация
0
Вот же! Спасибо. Второй вопрос на ту же тему.

Есть строка текста: "№&#49. СИРОП СОЛОДКИ &#50&#48&#48МЛ"

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

Задача: после каждого такого фрагмента нужно вставить "точку с запятой" - ";". И только в том случае, если ее там уже нет.

В результате должно получиться: "№&#49;. СИРОП СОЛОДКИ &#50;&#48;&#48;МЛ"

Можно ли решить такой пример?
 

Suppir

Продвинутый
Сообщения
967
Репутация
62
Код:
$sString = "№&#49. СИРОП СОЛОДКИ &#50&#48&#48МЛ"
$sResult = StringRegExpReplace ($sString, "(&#\d{2})(?!;)", "$1;")
ConsoleWrite($sResult & @CRLF)

В шаблоне поиска (&#\d{2}):
(...) - скобки обозначают, что мы захватываем содержимое в переменную (группировку)
\d{2} - обозначает две цифры, идущие подряд (\d - digit, цифра; {2} - квантификатор, который обозначает две штуки). Также можно просто написать \d\d.

Другие квантификаторы:
\d+ - 1 или более цифр
\d* - 0 или более цифр
d? - 0 или 1 цифра
\d{5} - 5 цифр
\d{2,5} - от 2 до 5 цифр
\d{5,} - более 5 цифр
\d{,5} - менее 5 цифр

(?!;) обозначает, что дальше не должно быть точки с запятой.

В шаблоне замены $1 обозначает содержимое первых скобок.


Странный глюк форума: если код оформить тэгом AutoIt, то в шаблон поиска самопроизвольно добавляются фрагменты &#38;
 
Автор
E

elakt

Новичок
Сообщения
8
Репутация
0
Спасибо! Мое решение, без использования регулярных выражений, работает в 10 раз медленнее.

Совсем обнаглею, и озвучу еще одну задачу (ранее я не работал с рег. выражениями).

Есть строка: <Data ss:Type="String">LIERAC <ЛИЕРАК> ФИТЕОЛ ФОРС 1 ШАМП ОТ ПЕРХ 100МЛ </Data>

В ней нужно символы "<" и ">", которые в примере выделены красным цветом, заменить на символ "/". Условия:

1. После символа "<" может быть любая цифра, или русская буква - английских быть не может.
2. Перед символом ">" может быть любая цифра, или русская буква - английских быть не может.

В результате должно получиться: <Data ss:Type="String">LIERAC /ЛИЕРАК/ ФИТЕОЛ ФОРС 1 ШАМП ОТ ПЕРХ 100МЛ </Data>

Выправляю XML-файл, выгруженный FastReport'ом, что бы он нормально открывался в OpenOffice Calc. Ты мне уже очень помог. Возможно и эту задачу можно решить нормальным способом?
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
626
Код:
$s = '<Data ss:Type="String">LIERAC <ЛИЕРАК123 123> ФИТЕОЛ ФОРС 1 ШАМП ОТ ПЕРХ 100МЛ  </Data>'
$pat = '<([0-9А-Яа-я]+)>'
MsgBox(0, "", StringRegExpReplace($s, $pat, '/$1/'))
 
Автор
E

elakt

Новичок
Сообщения
8
Репутация
0
Спасибо, Kaster, только одна поправка: ваш ответ подходит для моего примера, но не подходит для примера, который вы привели в коде. Я не понял сразу почему, потом догадался - там есть символ пробела :smile:

Еще одна задача, для меня пока сложноватая. Требуется в строке у всех числовых значений (цена товара) заменить "точку" на "запятую". Условие, как мне видится, простое: замену нужно производить для подстроки "цифра, точка, цифра". Шаблон получается что-то вроде '(\d)\.(\d)'

Но есть одна загвоздка: в тексте есть даты в виде "дд.мм.гггг", которые так же заменяются по такому шаблону.

Вопрос: как изменить цены, и не задеть даты? Пример:

Код:
$s = '<Data ss:Type="String">LIERAC /ЛИЕРАК/ - цена1 - 12.3 - цена2 - 21.2346 - срок годности - 01.04.2012 - количество 20</Data>'
$pat = '(\d)\.(\d)'
MsgBox(0, "", StringRegExpReplace($s, $pat, '$1,$2'))
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
626
elakt [?]
Спасибо, Kaster, только одна поправка: ваш ответ подходит для моего примера, но не подходит для примера, который вы привели в коде. Я не понял сразу почему, потом догадался - там есть символ пробела
тогда так
Код:
$s = '<Data ss:Type="String">LIERAC <ЛИЕРАК123 123> ФИТЕОЛ ФОРС 1 ШАМП ОТ ПЕРХ 100МЛ  </Data>'
$pat = '<([0-9А-Яа-я\s]+)>'
MsgBox(0, "", StringRegExpReplace($s, $pat, '/$1/'))


Еще одна задача, для меня пока сложноватая. Требуется в строке у всех числовых значений (цена товара) заменить "точку" на "запятую".
Код:
$s = '<Data ss:Type="String">LIERAC /ЛИЕРАК/ - цена1 - 12.3 - цена2 - 21.2346 - срок годности - 01.04.2012 - количество 20</Data>'
$pat = '\s(\d+)\.(\d+)\s'
MsgBox(0, "", StringRegExpReplace($s, $pat, '$1,$2'))
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
elakt [?]
Вопрос: как изменить цены, и не задеть даты?
Чуть более универсальный вариант(работает и при отсутствии \s):
Код:
$sText='<Data ss:Type="String">LIERAC /ЛИЕРАК/ - цена1 - 12.3 - цена2 - 21.2346 - срок годности - 01.04.2012 - количество 20</Data>   Цена:20.56руб    Цена:300.50$'

$sPattern = '(?<!\d\.)([^\.]\d+?)\.(\d+?[^\.])(?!\.\d)'
$sRezult = StringRegExpReplace ( $sText ,$sPattern,'$1,$2' )

MsgBox(0,'$sRezult',$sRezult)
 
Автор
E

elakt

Новичок
Сообщения
8
Репутация
0
2gregaz, а как в ваш вариант добавить условие: "если число не обрамлено кавычками"? Честно попытался разобраться сам, пока не получилось. Цель этого: что бы OpenOffice Calc понимал, что ему в XML-файле передаются именно числа, а не строки, и что бы при этом не "ломались" тэги самого XML, в частности размеры ячеек.

Из следующего примера НЕ нужно обрабатывать подстроку "21.2346":

Код:
$sText='<Data ss:Type="String">LIERAC /ЛИЕРАК/ - цена1 - 12.3 - цена2 - "21.2346" - срок годности - 01.04.2012 - количество 20 -  Цена:20.56руб   Цена:300.50$</Data> '
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
elakt [?]
Из следующего примера НЕ нужно обрабатывать подстроку "21.2346":
Сохраняя все предыдущие условия ? Или ?

Наверное так :
Код:
$sText='<Data ss:Type="String">LIERAC /ЛИЕРАК/ - цена1 - 12.3 - цена2 - "21.2346" - срок годности - 01.04.2012 - количество 20 -  Цена:20.56руб   Цена:300.50$</Data> '

$sPattern = '(?<!\d\.)([^"\d\.]\d+)\.(\d+[^"\d\.])(?!\.\d)'
$sRezult = StringRegExpReplace ( $sText ,$sPattern,'$1,$2' )

MsgBox(0,'$sRezult',$sRezult)

Наверное можно и упростить ???
 
Автор
E

elakt

Новичок
Сообщения
8
Репутация
0
Спасибо, gregaz, я решил эту задачу немного другим способом. Но как оказалось - рано обрадовался: OpenOffice Calc вместо того, что бы "числа, с запятой в качестве разделителя разрядов" понимать как числа, стал подставлять перед ними "апостроф", таким образом безусловно обозначая их формат как "строка". Соответственно опять не работают формулы и т.д.

Стал разбираться, нашел решение: в самом XML-файле обозначить нужные поля, как Number, а не как String. Написал такой шаблон:

Код:
$sText='<Cell ss:Index="9" ss:StyleID="s4">' & @CRLF & _
'<Data ss:Type="String">Филиал ЗАО фирмы 30"</Data>' & @CRLF & _
'<Data ss:Type="String">20.04.2011</Data>' & @CRLF & _
'<Data ss:Type="String">08.04</Data>' & @CRLF & _
'<Data ss:Type="String">012345</Data>' & @CRLF & _
'<Data ss:Type="String">1%</Data>' & @CRLF & _
'<Data ss:Type="String">%</Data>' & @CRLF & _
'<Data ss:Type="String"></Data>' & @CRLF & _
'<Data ss:Type="String">999</Data>' & @CRLF & _
'<Data ss:Type="String">1.00</Data>' & @CRLF & _
'<Data ss:Type="String">0.00</Data>' & @CRLF & _
'<Data ss:Type="String">0.03</Data>' & @CRLF & _
'<Data ss:Type="String">0</Data>' & @CRLF & _
'<Data ss:Type="String">000</Data>' & @CRLF & _
'<Data ss:Type="String">9040.0</Data>' & @CRLF & _
'<Data ss:Type="String">863.75</Data>' & @CRLF & _
'<Data ss:Type="String">11861.2</Data>'

$sPattern = 'Type="String">(?!<)((?!0)\d*\.?\d*?|0\.\d*?|0*)<'
$sRezult = StringRegExpReplace ( $sText ,$sPattern,'Type="Number">$1<' )

MsgBox(0,'$sRezult',$sRezult)


В этом примере тип данных String меняется на Numeric для значений, начиная с 999. Все что выше - должно оставаться String.

Вроде бы работает. Может быть, его можно как-то оптимизировать?
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
elakt [?]
В этом примере тип данных String меняется на Numeric для значений, начиная с 999. Все что выше - должно оставаться String.

Эта задача похоже будет попроще.
Правда мне непонятен критерий, по которому строки :
Код:
'<Data ss:Type="String">08.04</Data>' & @CRLF & _
'<Data ss:Type="String">012345</Data>' & @CRLF & _
не должны входить в набор. Поясни на словах.
А так вот :
Код:
$sPattern = '(Type=")String(">\d+\.?\d*</.+)'
$sRezult = StringRegExpReplace ( $sText ,$sPattern,'$1Number$2' )

MsgBox(0,'$sRezult',$sRezult)
 
Автор
E

elakt

Новичок
Сообщения
8
Репутация
0
gregaz [?]
Эта задача похоже будет попроще.
Правда мне непонятен критерий, по которому строки (...) не должны входить в набор. Поясни на словах.

1. "08.04" - сокращенный формат вывода информации о сроке годности товара. В данном случае это "01 августа 2004 года". Понятно, что начиная с 10-го месяца этот случай нормально не обработать, а так получается что ведущий "ноль" при формате \d\d\.\d\d означает, что это не "число", а "срок годности".

2. "012345" - это серия (номер или код) партии товара, установленный производителем, и его желательно видеть таким, как он написан на упаковке, то есть с ведущим нулем в данном случае. Все "настоящие" числа в файле начинаются "не на ноль", если не являются нулем, либо числом типа "0,03".
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Так :
Код:
;$sPattern = '(Type=")String(">)([1-9]+<|0+<|0\.\d+<|[1-9]\d*\.\d+<)'
;$sRezult = StringRegExpReplace ( $sText ,$sPattern,'$1Number$2$3' )

Так вроде корректней будет:
Код:
$sPattern = '(Type=")String(">)([1-9]\d+<|0+<|0\.\d+<|[1-9]\d*\.\d+<)'
$sRezult = StringRegExpReplace ( $sText ,$sPattern,'$1Number$2$3' )



Добавлено:
Сообщение автоматически объединено:

Еще упростил :
Код:
$sPattern = '(Type=")String(">)(0+<|0\.\d+<|[1-9]\d*\.?\d*<)'
$sRezult = StringRegExpReplace ( $sText ,$sPattern,'$1Number$2$3' )
 
Автор
E

elakt

Новичок
Сообщения
8
Репутация
0
Задачу решил.

Спасибо, ребята, реально помогли!
 
Верх