PHP preg_replace: [éèêë] vs (é | è | ê | ë) [дублировать]

Установить cURL 7.49.0 на Ubuntu 16.04 и Производные

31
задан JW. 12 November 2009 в 21:55
поделиться

6 ответов

Похоже, что это «функция», см. http://bugs.php.net/bug.php?id=37391

Переключатель 'u' только делает смысл для pcre, сам PHP не знает об этом.

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

17
ответ дан Tomalak 27 August 2018 в 03:34
поделиться

Извините меня за некропостинг, но может быть кто-то сочтет это полезным: нижеприведенный код может работать как замена для функций preg_match и preg_match_all и возвращает правильные совпадения с правильным смещением для строк с кодировкой UTF8.

     mb_internal_encoding('UTF-8');

     /**
     * Returns array of matches in same format as preg_match or preg_match_all
     * @param bool   $matchAll If true, execute preg_match_all, otherwise preg_match
     * @param string $pattern  The pattern to search for, as a string.
     * @param string $subject  The input string.
     * @param int    $offset   The place from which to start the search (in bytes).
     * @return array
     */
    function pregMatchCapture($matchAll, $pattern, $subject, $offset = 0)
    {
        $matchInfo = array();
        $method    = 'preg_match';
        $flag      = PREG_OFFSET_CAPTURE;
        if ($matchAll) {
            $method .= '_all';
        }
        $n = $method($pattern, $subject, $matchInfo, $flag, $offset);
        $result = array();
        if ($n !== 0 && !empty($matchInfo)) {
            if (!$matchAll) {
                $matchInfo = array($matchInfo);
            }
            foreach ($matchInfo as $matches) {
                $positions = array();
                foreach ($matches as $match) {
                    $matchedText   = $match[0];
                    $matchedLength = $match[1];
                    $positions[]   = array(
                        $matchedText,
                        mb_strlen(mb_strcut($subject, 0, $matchedLength))
                    );
                }
                $result[] = $positions;
            }
            if (!$matchAll) {
                $result = $result[0];
            }
        }
        return $result;
    }

    $s1 = 'Попробуем русскую строку для теста';
    $s2 = 'Try english string for test';

    var_dump(pregMatchCapture(true, '/обу/', $s1));
    var_dump(pregMatchCapture(false, '/обу/', $s1));

    var_dump(pregMatchCapture(true, '/lish/', $s2));
    var_dump(pregMatchCapture(false, '/lish/', $s2));

Выход моего примера:

    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(2) {
          [0]=>
          string(6) "обу"
          [1]=>
          int(4)
        }
      }
    }
    array(1) {
      [0]=>
      array(2) {
        [0]=>
        string(6) "обу"
        [1]=>
        int(4)
      }
    }
    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(2) {
          [0]=>
          string(4) "lish"
          [1]=>
          int(7)
        }
      }
    }
    array(1) {
      [0]=>
      array(2) {
        [0]=>
        string(4) "lish"
        [1]=>
        int(7)
      }
    }
4
ответ дан Artie Groove 27 August 2018 в 03:34
поделиться
  • 1
    Можете ли вы объяснить, что делает ваш код вместо простого вставки дампа кода? И как это отвечает на вопрос? – nhahtdh 19 December 2014 в 06:41
  • 2
    Он делает именно то, что описано в комментариях, и возвращает CORRECT смещения строки. Это вопрос вопроса. Не знаю, почему у меня было -2 для моего ответа. Он работает для меня. – Guy Fawkes 19 December 2014 в 06:50
  • 3
    Ну, вот почему вы должны включить объяснение того, что делает ваш код. Люди не понимают, что вы пытаетесь сделать здесь. – nhahtdh 19 December 2014 в 08:17
  • 4
    Измените мой ответ, добавьте тесты. – Guy Fawkes 19 December 2014 в 08:55

Я написал небольшой класс для преобразования смещений, возвращаемых preg_match, в соответствующие смещения utf:

final class NonUtfToUtfOffset
{
    /** @var int[] */
    private $utfMap = [];

    public function __construct(string $content)
    {
        $contentLength = mb_strlen($content);

        for ($offset = 0; $offset < $contentLength; $offset ++) {
            $char = mb_substr($content, $offset, 1);
            $nonUtfLength = strlen($char);

            for ($charOffset = 0; $charOffset < $nonUtfLength; $charOffset ++) {
                $this->utfMap[] = $offset;
            }
        }
    }

    public function convertOffset(int $nonUtfOffset): int
    {
        return $this->utfMap[$nonUtfOffset];
    }
}

Вы можете использовать его следующим образом:

$content = 'aą bać d';
$offsetConverter = new NonUtfToUtfOffset($content);

preg_match_all('#(bać)#ui', $content, $m, PREG_OFFSET_CAPTURE);

foreach ($m[1] as [$word, $offset]) {
    echo "bad: " . mb_substr($content, $offset, mb_strlen($word))."\n";
    echo "good: " . mb_substr($content, $offsetConverter->convertOffset($offset), mb_strlen($word))."\n";
}

https: //3v4l.org/8Y32J

2
ответ дан bronek89 27 August 2018 в 03:34
поделиться

Хотя модификатор u позволяет интерпретировать как образец, так и субъект как UTF-8, захваченные смещения все еще подсчитываются в байтах.

Вы можете использовать mb_strlen для получить длину в символах UTF-8, а не в байтах:

$str = "\xC2\xA1Hola!";
preg_match('/H/u', $str, $a_matches, PREG_OFFSET_CAPTURE);
echo mb_strlen(substr($str, 0, $a_matches[0][1]));
34
ответ дан Mark Amery 27 August 2018 в 03:34
поделиться
  • 1
    Человек, это 2010 и PHP все еще всасывает abysmally в Unicode. – Tomalak 21 July 2010 в 09:30
  • 2
    @Tomalak man это 2012 и PHP все еще сосет abysmally в Unicode. – Michael Robinson 9 October 2012 в 00:52
  • 3
    @MichaelRobinson man это 2014, и PHP все еще сосет abysmally в Unicode – Luka Ramishvili 13 May 2014 в 14:15
  • 4
    @pathros. Сейчас 2016, а PHP еще отвратительно в Unicode ... и PHP7 ничего не изменил в этом случае. – Tomasz Kowalczyk 21 January 2016 в 23:44
  • 5
    @jerdiggity Это 2018, и, как вы догадались, PHP все еще сосет abysmally в Unicode. – Harry Sadler 3 January 2018 в 00:55

Попробуйте добавить это (* UTF8) перед регулярным выражением:

preg_match('(*UTF8)/H/u', "\xC2\xA1Hola!", $a_matches, PREG_OFFSET_CAPTURE);

Магия, благодаря комментарию в http://www.php.net/manual/es/function. PREG-match.php # 95828

24
ответ дан Natxet 27 August 2018 в 03:34
поделиться
  • 1
    Интересно, хотя мне кажется, что вам нужно исходное / перед (*UTF8). Это не работает в моей системе, но может быть и на других. Что это делает, когда вы делаете echo $a_matches[0][1];? – JW. 28 February 2012 в 02:05
  • 2
    Самое простое решение! Спасибо! – Gustav 28 December 2013 в 05:05
  • 3
    Я использовал его так на PHP 5.4.29, работает как прелесть: preg_match_all('/(*UTF8)[^A-Za-z0-9\s]/', $txt, $matches); – Novalis 5 July 2014 в 09:57
  • 4
    Не работает для меня ни на PHP 5.6, ни на PHP 7 на Ubuntu 16.04. (*UTF8) до того, как разделитель является ошибкой, после этого не действует. Я подозреваю, что это зависит от того, как / где вы получили свой php, в частности настройки, скомпилированные libpcre*. – user 24 November 2016 в 17:37
  • 5
    Не меняет смещения для меня, но это интересно узнать. Исходная документация для этой "функции" is: pcre.org/pcre.txt – BurninLeo 15 October 2017 в 15:29

Если все, что вы хотите сделать, это найти многобайтовое безопасное положение H try mb_strpos ()

mb_internal_encoding('UTF-8');
$str = "\xC2\xA1Hola!";
$pos = mb_strpos($str, 'H');
echo $str."\n";
echo $pos."\n";
echo mb_substr($str,$pos,1)."\n";

Выход:

¡Hola!
1
H
1
ответ дан velcrow 27 August 2018 в 03:34
поделиться
  • 1
    Это был просто упрощенный пример, но это может быть полезно для других. – JW. 16 August 2011 в 23:45
Другие вопросы по тегам:

Похожие вопросы: