Замена в зависимости от позиции в строке - PullRequest
7 голосов
/ 01 февраля 2010

Есть ли способ использовать регулярные выражения для замены символов в строке на основе позиции?

Например, одно из моих правил переписывания для проекта, над которым я работаю, это «заменить o на ö, если o - это следующий за последним гласный и даже нумерованный (считая слева направо) .»

Так, например:

  • heabatoik станет heabatöik (o - следующий за последним гласный, а также четвертый гласный)
  • habatoik не изменится (o - это следующий за последним гласный, но третий гласный)

Возможно ли это с помощью preg_replace в PHP?

Ответы [ 4 ]

8 голосов
/ 01 февраля 2010

Начиная с начала строки темы, вы хотите сопоставить 2 n + 1 гласные, за которыми следует o, но только если за o следует ровно еще одна гласная:

$str = preg_replace(
  '/^((?:(?:[^aeiou]*[aeiou]){2})*)' .  # 2n vowels, n >= 0
    '([^aeiou]*[aeiou][^aeiou]*)' .     # odd-numbered vowel
    'o' .                               # even-numbered vowel is o
    '(?=[^aeiou]*[aeiou][^aeiou]*$)/',  # exactly one more vowel
  '$1$2ö',
  'heaeafesebatoik');

Чтобы сделать то же самое, но для нечетного o, сопоставьте 2 n ведущих гласных, а не 2 n + 1:

$str = preg_replace(
  '/^((?:(?:[^aeiou]*[aeiou]){2})*)' .  # 2n vowels, n >= 0
    '([^aeiou]*)' .                     # followed by non-vowels
    'o' .                               # odd-numbered vowel is o
    '(?=[^aeiou]*[aeiou][^aeiou]*$)/',  # exactly one more vowel
  '$1$2ö',
  'habatoik');

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

1 голос
/ 01 февраля 2010

Мне нравится рассказывать о Шмитте.(У меня недостаточно очков, чтобы добавить комментарий, я не пытаюсь украсть его гром).Я бы использовал флаг PREG_OFFSET_CAPTURE, так как он возвращает не только гласные, но и местоположения.Это мое решение:

const LETTER = 1;
const LOCATION = 2
$string = 'heabatoik'

preg_match_all('/[aeiou]/', $string, $in, $out, PREG_OFFSET_CAPTURE);

$lastElement = count($out) - 1; // -1 for last element index based 0

//if second last letter location is even
//and second last letter is beside last letter
if ($out[$lastElement - 1][LOCATION] % 2 == 0 &&
    $out[$lastElement - 1][LOCATION] + 1 == $out[$lastElement][LOCATION])
       substr_replace($string, 'ö', $out[$lastElement - 1][LOCATION]);

Примечание:

print_r(preg_match_all('/[aeiou]/', 'heabatoik', $in, $out, PREG_OFFSET_CAPTURE));
Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [0] => e
                    [1] => 1
                )

            [1] => Array
                (
                    [0] => a
                    [1] => 2
                )

            [2] => Array
                (
                    [0] => a
                    [1] => 4
                )

            [3] => Array
                (
                    [0] => o
                    [1] => 6
                )

            [4] => Array
                (
                    [0] => i
                    [1] => 7
                )
        )
)
1 голос
/ 01 февраля 2010

Вы можете использовать preg_match_all , чтобы разбить строку на гласные / не гласные партии и обработать это.

например. что-то вроде

preg_match_all("/(([aeiou])|([^aeiou]+)*/",
    $in,
    $out, PREG_PATTERN_ORDER);

В зависимости от ваших конкретных потребностей, вам может потребоваться изменить положение ()*+? в регулярном выражении.

0 голосов
/ 01 февраля 2010

Вот как бы я это сделал:

$str = 'heabatoik';

$vowels = preg_replace('#[^aeiou]+#i', '', $str);
$length = strlen($vowels);
if ( $length % 2 && $vowels[$length - 2] == 'o' ) {
    $str = preg_replace('#o([^o]+)$#', 'ö$1', $str);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...