Строка повреждена или ошибка preg_match?

NO-BREAK SPACE и многим другим символам UTF-8 требуется 2 байта для его представления; Итак, в предполагаемом контексте строк UTF8 изолированный (не предшествующий xC2) байт не-ASCII (>127) является нераспознанным символом... Хорошо, это только проблема расположения (!), но это портит всю строку?

Как избежать этого "непредвиденного поведения"? (это происходит в некоторых функциях, а не в других).

Пример (генерирование непредвиденного поведения с preg_match только):

  header("Content-Type: text/plain; charset=utf-8"); // same if text/html
  //PHP Version 5.5.4-1+debphp.org~precise+1
  //using a .php file enconded as UTF8.

  $s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
  preg_match_all('/[-\'\p{L}]+/u',$s,$m);
  var_dump($m);            // empty! (corrupted)
  $m=str_word_count($s,1);
  var_dump($m);            // ok

  $s = "THE UTF-8 NO-BREAK\xC2\xA0SPACE";  // utf8-encoded nbsp
  preg_match_all('/[-\'\p{L}]+/u',$s,$m);
  var_dump($m);            // ok!
  $m=str_word_count($s,1);
  var_dump($m);            // ok

2 ответа

Это не полный ответ, потому что я не говорю, почему некоторые функции PHP " полностью не работают с неверно закодированными строками ", а другие нет: см. @Deceze в комментариях к вопросу и ответ @hakre. Если вы ищете PCRE-замену для str_word_count() смотри мой preg_word_count() ниже.

PS: по поводу обсуждения " однородности поведения встроенной библиотеки PHP5", я пришел к выводу, что PHP5 не так уж и плох, но мы создали множество пользовательских функций переноса (фасада) (см. Разнообразие PHP-framworks!)... Или подожди PHP6:-)


Спасибо @pebbl! Если я понимаю вашу ссылку, на PHP нет сообщений об ошибках. Таким образом, возможный обходной путь моей иллюстрируемой проблемы - добавить условие ошибки... Я нахожу условие здесь (оно гарантирует действительное utf8!)... И спасибо @deceze за то, что помните, что существует встроенная функция для проверки этого условия (Я редактировал код после).

Объединение проблем, решение, переведенное в функцию (РЕДАКТИРОВАНИЕ, спасибо комментариям @hakre!),

 function my_word_count($s,$triggError=true) {
   if ( preg_match_all('/[-\'\p{L}]+/u',$s,$m) !== false )
      return count($m[0]);
   else {
      if ($triggError) trigger_error(
         // not need mb_check_encoding($s,'UTF-8'), see hakre's answer, 
         // so, I wrong, there are no 'misteious error' with preg functions
         (preg_last_error()==PREG_BAD_UTF8_ERROR)? 
              'non-UTF8 input!': 'other error',
         E_USER_NOTICE
         );
      return NULL;
   }
 }

Теперь (отредактировано после обдумывания ответа @hakre), о единообразном поведении: мы можем разработать разумную функцию с библиотекой PCRE, которая имитирует str_word_count поведение, принимая плохой UTF8. Для этой задачи я использовал @bobince iconv совет:

 /**
  * Like str_word_count() but showing how preg can do the same.
  * This function is most flexible but not faster than str_word_count.
  * @param $wRgx the "word regular expression" as defined by user.
  * @param $triggError changes behaviour causing error event.
  * @param $OnBadUtfTryAgain mimic the str_word_count behaviour.
  * @return 0 or positive integer as word-count, negative as PCRE error.
  */
 function preg_word_count($s,$wRgx='/[-\'\p{L}]+/u', $triggError=true,
                          $OnBadUtfTryAgain=true) {
   if ( preg_match_all($wRgx,$s,$m) !== false )
      return count($m[0]);
   else {
      $lastError = preg_last_error();
      $chkUtf8 = ($lastError==PREG_BAD_UTF8_ERROR);
      if ($OnBadUtfTryAgain && $chkUtf8) 
         return preg_word_count(
            iconv('CP1252','UTF-8',$s), $wRgx, $triggError, false
         );
      elseif ($triggError) trigger_error(
         $chkUtf8? 'non-UTF8 input!': "error PCRE_code-$lastError",
         E_USER_NOTICE
         );
      return -$lastError;
   }
 }

Демонстрация ( попробуйте другие материалы!):

 $s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
 print "\n-- str_word_count=".str_word_count($s,0);
 print "\n-- preg_word_count=".preg_word_count($s);

 $s = "THE UTF-8 NO-BREAK\xC2\xA0SPACE";  // utf8-encoded nbsp
 print "\n-- str_word_count=".str_word_count($s,0);
 print "\n-- preg_word_count=".preg_word_count($s);

Хорошо, я чувствую некоторое разочарование в связи с тем, что все не так просто str_word_count в preg_match_all, Однако то, как вы задаете вопрос, немного неточно, я все равно пытаюсь на него ответить. Неточно, потому что у вас есть большое количество неправильных предположений, которые вы, очевидно, принимаете как должное (это случается с лучшими из нас). Я надеюсь, что смогу немного исправить это:

$s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
preg_match_all('/[-\'\p{L}]+/u',$s,$m);
var_dump($m);            // empty! (corrupted)

Этот код неверен. Вы обвиняете PHP здесь в том, что он не дал предупреждение или что-то в этом роде, но я должен признать, что здесь виноват только "вы". PHP позволяет вам проверить на наличие ошибок. Прежде чем вы начнете судить так рано, что при обработке ошибок должно быть сделано предупреждение, я должен напомнить вам, что существуют разные способы, как обращаться с ошибками. Некоторые имеют дело с передачей сообщений, другой тип ошибок - это рассказывать о них с возвращаемыми значениями. И если мы посетим страницу руководства preg_match_all и искать документацию возвращаемого значения, мы можем найти это:

Возвращает количество полных совпадений с образцом (которое может быть нулевым) или FALSE, если произошла ошибка.

Часть в конце:

ЛОЖЬ, если произошла ошибка [Выделите мной]

является некоторым распространенным способом обработки ошибок, чтобы сигнализировать вызывающему коду, что произошла какая-то ошибка. Давайте рассмотрим ваш код, который, по вашему мнению, не работает:

$s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
preg_match_all('/[-\'\p{L}]+/u',$s,$m);
var_dump($m);            // empty! (corrupted)

Единственное, что показывает этот код, это то, что человек, который его напечатал (я полагаю, это был ты), явно решил не заниматься обработкой ошибок. Это хорошо, если только этот человек не протестует против того, что код не будет работать.

Грустная вещь в том, что это обычная ошибка пользователя, если вы пишете хрупкий код (например, без обработки ошибок), не ожидайте, что он будет работать надежно. Это никогда не произойдет.

Так что же требуется, когда вы программируете? Прежде всего вы должны знать о функциях, которые вы используете. Обычно это требует знаний о входных параметрах и возвращаемых значениях. Вы находите эту информацию обычно документированной. Используйте руководство. Во-вторых, вам действительно нужно заботиться о возвращаемых значениях и обрабатывать ошибки самостоятельно. Одна только функция не знает, что это значит, если произошла ошибка. Это исключение? Затем вам нужно выполнить обработку исключений, вероятно, как в демонстрационном примере:

<?php
/**
 * @link http://stackru.com/q/19316127/367456
 */

$s = "THE UTF-8 NO-BREAK\xA0SPACE"; // a non-ASCII byte
$result = preg_match_all('/[-\'\p{L}]+/u',$s,$m);

if ($result === FALSE) {
    switch (preg_last_error()) {
        case PREG_BAD_UTF8_ERROR:
            throw new InvalidArgumentException(
                'UTF-8 encoded binary string expected.'
            );
        default:
            throw new RuntimeException('preg error occured.');

    }
}

var_dump($m);            // nothing at all corrupted...

В любом случае это означает, что вам нужно посмотреть, что вы делаете, узнать об этом и написать больше кода. Нет магии. Нет ошибок Просто немного работы.

Другая часть, которую вы видите перед вами, возможно, чтобы понять, что представляют собой символы в программном обеспечении, но это более независимо от конкретных языков программирования, таких как PHP, например, вы можете прочитать вступительное чтение здесь:

Первое - это обязательное чтение или, возможно, обязательное добавление в закладки, потому что это много, но это объясняет все очень хорошо.

Другие вопросы по тегам