LINUX.ORG.RU

Замена regexp'ом

 , , ,


1

3

Почему g делает две замены в перле и яве?

user@localhost:$ echo "test////////" | perl -pe "s/\/*$/d/"
testd
user@localhost:$ echo "test////////" | perl -pe "s/\/*$/d/g"
testdd
duser@localhost:$echo "test////////" | sed -e "s/\/*$/d/"
testd
user@localhost:$ echo "test////////" | sed -e "s/\/*$/d/g"
testd
user@localhost:$ echo "test////////" | sed -re "s/\/*$/d/g"
testd
user@localhost:$ echo "test////////" | sed -re "s/\/*$/d/"
testd
В яве replaceAll выдаёт testdd, replaceFirst testd. Я ожидал одну замену.

★★★★★
$ echo "test////////" | perl -Mre=debug -pe "s/\/*$/d/"
Compiling REx "/*$"
Final program:
   1: STAR (4)
   2:   EXACT </> (0)
   4: EOL (5)
   5: END (0)
floating ""$ at 0..9223372036854775807 (checking floating) minlen 0
Matching REx "/*$" against "test////////%n"
Intuit: trying to determine minimum start position...
  Found floating substr ""$ at offset 12...
  (multiline anchor test skipped)
Intuit: Successfully guessed: match at offset 0
   0 <> <test//////>         |  1:STAR(4)
                                  EXACT </> can match 0 times out of 2147483647...
   0 <> <test//////>         |  4:  EOL(5)
                                    failed...
                                  failed...
   1 <t> <est///////>        |  1:STAR(4)
                                  EXACT </> can match 0 times out of 2147483647...
   1 <t> <est///////>        |  4:  EOL(5)
                                    failed...
                                  failed...
   2 <te> <st////////>       |  1:STAR(4)
                                  EXACT </> can match 0 times out of 2147483647...
   2 <te> <st////////>       |  4:  EOL(5)
                                    failed...
                                  failed...
   3 <tes> <t////////>       |  1:STAR(4)
                                  EXACT </> can match 0 times out of 2147483647...
   3 <tes> <t////////>       |  4:  EOL(5)
                                    failed...
                                  failed...
   4 <test> <////////%n>     |  1:STAR(4)
                                  EXACT </> can match 8 times out of 2147483647...
  12 <est////////> <%n>      |  4:  EOL(5)
  12 <est////////> <%n>      |  5:  END(0)
Match successful!
testd
Freeing REx: "/*$"
outtaspace ★★★
()

В яве replaceAll выдаёт testdd, replaceFirst testd. Я ожидал одну замену.

Какой конкретно регэкс ты подсовываешь яве? Ты там почти наверняка не заэскейпил какой-нибудь бэкслэш.

asaw ★★★★★
()

Ну тебя же такое

System.out.println("test!".replaceAll("[p]*$", "d")); 

test!d

не удивляет. Почему должно такое

System.out.println("test!".replaceAll("!*$", "d"));

testdd
удивлять ? :D

Хочешь заменить все символы в концы пиши через +

System.out.println("test!!!!".replaceAll("!+$", "d")); 

testd
vtVitus ★★★★★
()
Последнее исправление: vtVitus (всего исправлений: 2)
Ответ на: комментарий от asaw

Какой конкретно регэкс ты подсовываешь яве? Ты там почти наверняка не заэскейпил какой-нибудь бэкслэш.

Так и передаю, не надо там ничего эскейпить:

"test////////".replaceFirst("/*$", "d");
Результат testd.
"test////////".replaceAll("/*$", "d");
Результат testdd.

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от vtVitus

Почему должно такое

System.out.println("test!".replaceAll("!*$", "d"));

testdd
удивлять ?

Да вот в том-то и дело, удивляет. Разве что только если * включает в себя и $, тогда, видимо, да, будет два прохода и две замены.

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от blexey

Но почему? Я ожидаю, что /* — это символ / 0 или больше раз. А оказывается, что это [/$] 0 или больше раз.

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от orm-i-auga

Ну так ты же сам почти догадался:

Разве что только если * включает в себя и $

Только не * включает в себя и $, а

.*$
матчит конец строки.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 1)
Ответ на: комментарий от orm-i-auga

Да не важно какой там символ. Любой символ помноженный на 0 раз подойдет) Может, вот так понятнее будет:

System.out.println("b".replaceAll("a*b", "d"));
d

asaw ★★★★★
()
Ответ на: комментарий от asaw

Ну если в этом примере проводить аналогию с исходным, то результат должен был бы быть bd. Однако $ обрабатывается как-то по-другому.

user@localhost:$ echo "b" | perl -pe "s/a*b/d/"
d
user@localhost:$ echo "b" | perl -pe "s/a*$/d/"
bd

orm-i-auga ★★★★★
() автор топика
Последнее исправление: orm-i-auga (всего исправлений: 1)
Ответ на: комментарий от orm-i-auga

Однако $ обрабатывается как-то по-другому.

Конечно, $ присутствует в любой строке, как и ^. Вот примерно так:

System.out.println("".replaceAll("(anything can be here)*$", "d"));
d

asaw ★★★★★
()
Ответ на: комментарий от asaw

Ладно, упростим пример, вот аналогичный исходному:

$ echo "abcd" | perl -pe "s/d*$/e/g"
abcee
Получается, при первом проходе он нашёл символ d в конце строки, повторяющийся один раз, и сделал замену. Пошёл второй проход, он нашёл символ d в конце строки, повторяющийся ноль раз и сделал замену. Но почему тогда он остановился на этом последнем разе, что мешает сделать третий проход, затем четвертый, и т.д., и каждый раз добавлять в конец e?

UPD Кажется понял. Если при каждом проходе запоминать, что именно было заменено в какой позиции, то в замененном тексте нельзя делать повторные замены. Вот и условие останова.

orm-i-auga ★★★★★
() автор топика
Последнее исправление: orm-i-auga (всего исправлений: 1)
Ответ на: комментарий от orm-i-auga

Проход там скорее всего вообще один, а работает примерно так:

import java.util.regex.*;

class HelloWorld {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("Z*");
        Matcher matcher = pattern.matcher("bcZZdef");
        boolean b;
        while(b = matcher.find()) {
            System.out.println(matcher.start() + ": \"" + matcher.group() + "\"");
        }
        System.out.println("bcZZdef".replaceAll("Z*", "!"));
    }
}
0: ""                                                                                                                                                                                                 
1: ""                                                                                                                                                                                                 
2: "ZZ"                                                                                                                                                                                               
4: ""                                                                                                                                                                                                 
5: ""                                                                                                                                                                                                 
6: ""                                                                                                                                                                                                 
7: ""                                                                                                                                                                                                 
!b!c!!d!e!f! 

То есть строка последовательно перебирается, а все найденные группы заменяются и формируется новая строка.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 2)
Ответ на: комментарий от asaw

Да, действительно оказывается.

import java.util.regex.*;

class HelloWorld {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("d*$");
        Matcher matcher = pattern.matcher("abcd");
        boolean b;
        while(b = matcher.find()) {
            System.out.println(matcher.start() + ": \"" + matcher.group() + "\"");
        }
        System.out.println("abcd".replaceAll("d*$", "e"));
    }
}
3: "d"
4: ""
abcee

orm-i-auga ★★★★★
() автор топика
Ответ на: комментарий от orm-i-auga

Нет. /* соответствует именно пустой строке, в соответствии с правилами. Попробуй убрать $ из выражения - пустая строка точно так же станет обнаруживаться в самом начале строки:

echo "test" | perl -pe "s/(\/*)/d/"
dtest

blexey ★★★★★
()
Последнее исправление: blexey (всего исправлений: 1)
Ответ на: комментарий от orm-i-auga

Ещё раз взгляни на свой регексп /*$ и переведи его на человеческий: «слэш, встречающийся 0 или более раз, за которым следует конец строки». Что-то работает не так?

blexey ★★★★★
()
Ответ на: комментарий от blexey

Ну там фишка в том, что $ обрабатывается несколько по-особому - как якорь, а не как символ. Поэтому сам он не относится к найденной последовательности.

asaw ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.