Javascript-форум (https://javascript.ru/forum/)
-   Серверные языки и технологии (https://javascript.ru/forum/server/)
-   -   Почему не срабатывают условия в PHP? (https://javascript.ru/forum/server/65290-pochemu-ne-srabatyvayut-usloviya-v-php.html)

snovapavel 10.10.2016 17:59

Почему не срабатывают условия в PHP?
 
Добрый день!

Подскажите пожалуйста, почему не срабатывают условия?

Необходимо в тексте найти все буквы «a»
1. Если буква «a» стоит между двух букв «b» - вот так: «bab», то нужно заключить её в тег <b>, вот так: b<b>a</b>b;
2. Если буква «a» не стоит между двумя «b», то заключить её в тег <i> - вот так: <i>a</i>.

Это сильно-сильно упрощенная, необходимая мне конструкция, поэтому её необходимо сделать именно с помощью if-else.

Что делаю я:

// ищем в тексте, поочереди, все буквы «а»
if ( preg_match( "/a/miu", $text ) ) {

  // далее поверяем, находится ли буква «a», между двух букв «b»
  if ( preg_match( "/bab/miu", $text ) ) {
    // если условие верно, то заключаем её в тег <b>
    $text = preg_replace( "/bab/miu", "b<b>a</b>b", $text );
  // во всех остальных случаях...
  } else {
    // заключаем её в тег <i>
    $text = preg_replace( "/a/miu", "<i>a</i>", $text );
  }

}


Почему-то, срабатывает только первая часть условия, вот это:


// далее поверяем, находится ли буква «a», между двух букв «b»
  if ( preg_match( "/bab/miu", $text ) ) {
    // если условие верно, то заключаем её в тег <b>
    $content = preg_replace( "/bab/miu", "b<b>a</b>b", $text );
  }



А вторая часть никогда не срабатывает, вот эта:


else {
    // заключаем её в тег <i>
    $content = preg_replace( "/a/miu", "<i>a</i>", $text );
  }


Почему? И как заставить её работать?......

laimas 10.10.2016 18:23

Цитата:

Сообщение от snovapavel
Почему?

Потому, что неверно. Ваш первый пост о тегах также базировался на ошибочном представлении, а ведь надо было использовать preg_match_all, и так:

preg_match_all('/<(div)[^>]*>.*<p[^>]*>.+<\/p>.*<\/div>|<(blockquote)[^>]*>.*<p[^>]*>.+<\/p>.*<\/blockquote>/sU', $string, $matches, PREG_SET_ORDER);

foreach($matches as $r) {
    $r = current(array_diff(array_slice($r, 1), ['']));
    echo 'Параграф вложен в тег ' .$r . '</br>';
}


А в данном случае замена стразу без всяких проверок if...else

$text = preg_replace(['/bab/i', '/(?<!b>)a(?!<\/b)/i'], ['b<b>a</b>b', '<i>a</i>'], $text);

snovapavel 10.10.2016 19:15

Добрый день!

Спасибо большое за ответ!

Ваш совет:
А в данном случае замена стразу без всяких проверок if...else...

К сожалению, я не представляю, как написать ещё понятнее задачу...

Смотрите:

1. Ищем в тексте все буквы а и далее проверяем:
____Если буква а стоит посредине букв б, то заключаем в тег <b>.
____ВСЕ ОСТАЛЬНЫЕ буквы а (которые не стоят между двух букв б) в тег <i>

Понимаете? И так уже целый день сегодня думал как максимально упростить.

Я учу PHP. И мне бы лучше пока попроще...

Если можете, помогите написать мою функцию. Буду очень признателен.

laimas 10.10.2016 19:46

Цитата:

Сообщение от snovapavel
Если можете, помогите написать мою функцию.

Нет у вас никакой функции, а что я написал, как раз делает то, что нужно, и если только буквы, то еще проще:

$text = 'abbb cccc ddbabddd fafdfff gggbabggg';

$text = preg_replace(['/bab/i', '/(?<!>)a(?!<)/i'], ['b<b>a</b>b', '<i>a</i>'], $text);

echo htmlspecialchars($text); //результат <i>a</i>bbb cccc ddb<b>a</b>bddd f<i>a</i>fdfff gggb<b>a</b>bggg


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

Если изучаете, то тогда с чего либо простого, начиная отсюда - сначала сами выражения, а потом функции PCRE.

snovapavel 10.10.2016 20:12

Спасибо вам большое за помощь.

Если я вас правильно понял, то это условие работает так:

preg_replace( // это понятно

['/bab/i', // ищем любое регулярное выражение (в данном случае bab)

'/(?<!>)a(?!<)/i'], // Простите, но вот это как расшифровывается?

['b<b>a</b>b', // откуда вот это материализовалось?... непонимаю

'<i>a</i>'], // это я так понял, означает в противном случае

$text); // откуда что берём

________________________________

А как добавить сюда третье условие? Скажем faf?

Большое спасибо за помощь.

laimas 10.10.2016 20:24

Цитата:

Сообщение от snovapavel
Простите, но вот это как расшифровывается?

Цитата:

Сообщение от snovapavel
откуда вот это материализовалось?... непонимаю

Ну чтобы понимать, надо изучать.

preg_replace может принимать аргументом паттерн и замену не только как строки, но и как массивы. 'b<b>a</b>b' - материализовалось из вашего условия Если буква а стоит посредине букв б, то заключаем в тег <b>, хотя изначально речь шла о латинице, а посему модификатор u я просто опустил - латиница (en) и в utf будет тем же самым байтом. Если в паттерне указать "не запоминать" крайние по условию символы, то и замена была бы описана иначе.

(?<!>)a(?!<) - это означает что перед искомым символом не должно быть >, а после него не должно быть <. А вот почему так, они из чего материализуются, попробуйте ответить сами. А не получится ответить, тогда.... Но если чуток подумать, то вряд ли не будет найдет ответ. )

PS. Сперва с этим разберитесь, а потом уже Скажем faf. ) Тем более, что в паттерне и заменах есть "мина". Я эту "свинью" подложил специально, ее надо обнаружить.

snovapavel 10.10.2016 20:52

Смотрите, конкретный пример, чтобы мне расставить теги <br> во всех пустых строках, кроме тех, которые находятся внутри тегов <pre> и <div>, мне надо писать так?

$text = preg_replace(['/^\s*$/i', '/(?<!>)^\s*$(?!<)/i'], ['<br>', ''], $text);


И всё-таки, если бы к вам подошли и предложили, скажем, даже сумму 500 рублей и попросили дописать условие, именно через if else, это же можно сделать?

P. S. Оставим пока дискуссию что надо для парсинга текста использовать специальные скрипты и т. д., я пытаюсь, учить PHP в боевых условиях.

Ведь первая часть моих условий исполняется (когда мы определяем наличие соседних букв б)? Почему не исполняется часть «в противном случае»? Можете ответить? Или дописать именно её. Понимаете, я там хоть что-то понимаю.

Ещё раз спасибо за помощь.

laimas 10.10.2016 21:25

Цитата:

Сообщение от snovapavel
чтобы мне расставить теги <br> во всех пустых строках, кроме тех, которые находятся внутри тегов <pre> и <div>, мне надо писать так?

Конечно нет, это не будет работать должным образом.

Чтобы работало так как вы пытались (опять таки, если пример о буквах), нужно получить искомый символ в его окружении - по одному символу слева и справа. Затем проходом в цикле найти в совпадении то, что интересует и принять соответствующее действие.

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

Почитайте о preg_replace_callback().

snovapavel 11.10.2016 09:33

laimas,
Добрый день!

Спасибо вам за помощь.

Всё утро корпел над функцией, которую вы мне посоветовали. В результате написал следующий продуманный код, посмотрите, пожалуйста:

// берём контент
$content = preg_replace_callback(

  // поочерёдно перебираем в нём все пустые строки в их окружении
  // и каждую строку проверяем на соответствие и выполняем с ней то, или иное действие
  "/[\s\S]*(^\s*$)[\s\S]*/miu",

  function( $match_before ) {

    // смотрим, если пустая строка внутри pre (записываем результат в переменную $match_after)
    if ( preg_match( "/\<pre\>[\s\S]*(^\s*$)[\s\S]*\<\/pre\>/miu", $content, $match_after ) ) {

      // и выводим вместо неё надпись, что мы внутри
      $content = preg_replace( $match_after[1], "<p>Это строка внутри pre</p>", $content );

    // в противном случае, мы берём строку, которую нашли в самом начале
    // и так как она не подошла под первое условие
    // с чистой совестью выводим вместо неё сообщение, что мы НЕ внутри
    } else {

      $content = preg_replace( $match_before[1], "<p>Это строка НЕ внутри pre</p>", $content );

    }

  },

  $content

);


Ведь алгоритм написан верно?

laimas 11.10.2016 11:34

А запустить и проверить разве сложно?

Во-первых второй аргумент функции у вас это анонимная функция и никакой $content в ней не будет доступно, а чтобы было доступно, нужно передать эту переменную в нее - function( $match_before ) use($content) { ...
Во-вторых не изменение строки внутри функции, а для этого переменная должна передаваться в функцию по ссылке - function( $match_before ) use(&$content) { ..., а возвращать измененные фрагменты, иначе последствия непредсказуемы.

Запускайте свой код и вы как минимум получите ошибку. Вы что пишите, а проверять не проверяете?

В том, что вы нагородили в preg_replace_callback нет никакого смысла, да и вообще во всем коде. :)

Что есть причина пустой строки, это двойное \r\n или \n. При этом непосредственно \r\n можно трактовать также пустой строкой. Что вы хотите получить вне тегов - заменить двойной перевод строки одиночным или сохранить оригинал?

$s = 'aaa
bbb

<div>ccc

ddd</div>

eee

<pre>fff

ggg</pre>';

$s = preg_replace_callback('/<div>.+<\/div>|<pre>.+<\/pre>/s', function($v) {
    return str_replace('<br />', '', $v[0]);
}, nl2br($s));

echo $s;

//затем вместо nl2br($s) пропишите так:

preg_replace('/^\s+$/m', '<br />', $s)

//и посмотрите на разницу


Примечание - preg_replace_callback для посмотреть, все это можно сделать и без нее.

snovapavel 11.10.2016 13:01

laimas,

Да, не работает...

Ваша функция работает отлично, но, я как понял, мы вначале расставляем тег br везде, а потом вырезаем их из тега pre?

Я это реализовал ещё месяц назад, таким образом:

// Расставляем в конце каждой строки тег <br>
$content = preg_replace( '/\r?\n/miu', "<br>\r\n", $content );

// Убираем все теги <br>, которые идут после открывающего тега <div>
$content = preg_replace( '/((\<div[^\>]*?\>)<br>)/miu', "$2", $content );

// Убираем все теги <br>, которые идут после тега <hr>
$content = preg_replace( '/((\<hr[^\>]*?\>)\<br\>)/miu', "$2", $content );

// Убираем все теги <br>, которые находятся внутри контейнера <pre>...</pre>
$content = preg_replace_callback(

  '/\<pre[^\>]*?\>([\s\S]*?)\<\/pre\>/miu',

  function( $matches ) {
    return preg_replace( '/\<br\>/miu', '', $matches[0] );
  },

  $content

);

// Если пустых строк с тегом <br>, больше чем 1 подряд, убираем лишние и оставляем только одну
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );


Кстати, оцените эту красоту.

Просто уже месяц я думаю над вопросом, как сделать аналог как в CSS .content :not(pre):not(div) на PHP.

Эта всё, чтобы сделать автоматически расстановку параграфов в контенте, понимаете? Ведь WordPress как-то расставляет их сам. Вот я и пишу лёгкий и простой аналог парсера для людей. Совершенно бесплатно.

laimas 11.10.2016 13:13

Цитата:

Сообщение от snovapavel
// Расставляем в конце каждой строки тег <br>
$content = preg_replace( '/\r?\n/miu', "<br>\r\n", $content );

И зря. Запомните следующее - разбор рег. выражением, это затратные операции, и там где можно обойтись вполне без них, забывайте о том, что они вообще существуют.

Поэтому "оцените эту красоту", это не красота, а пустые и неоправданные затраты. :)

Ну а если говорить о парсере html-кода, то уже есть отличные классы готовые. Если расставить автоматом параграфы в тексте присылаемом клиентом, что может быть проще - примем во внимание, что параграф это двойной перевод строки, тогда:

echo '<p>'.implode('</p><p>', preg_split('/[\r\n]{2}|\n{2}/', htmlspecialchars($string), null, PREG_SPLIT_NO_EMPTY)).'</p>';

snovapavel 11.10.2016 13:23

laimas,

Спасибо большое, за ваши советы. Не буду больше вас мучить. Надо углубляться в изучение.

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

// Если пустых строк с тегом <br>, больше чем 1 подряд, убираем лишние и оставляем только одну
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );
$content = preg_replace( '/(^\<br\>\r?\n^\<br\>\r?\n)/miu', "<br>\r\n", $content );


То есть два и более подряд идущих тега:

<br>
<br>
<br>


заменять на один:

<br>


Большое спасибо за помощь!

laimas 11.10.2016 13:32

Цитата:

Сообщение от snovapavel
Если пустых строк с тегом <br>, больше чем 1 подряд, убираем лишние и оставляем только одну

Зачем, если изначально вы \r\n преобразовали в br? Не проще ли убрать лишние переводы строк, а затем nl2br?
А если шерстить <br>, то зачем столько идентичных повторений? Ну и меняйте если более двух - '#<br[^>]*>{2,}#'. А такой \<br\>\r\n\<br\>\r\n бардак может получится, если только самому сильно захотеть. :) Да еще и <br\> - это из какой спецификации?

PS. И я уже говорил, что "англицкий" он и в Африке "англицкий", проверьте этот пример в UTF:

$s = 'Рус Lat Pyc';
echo preg_replace('/Lat/', 'Eng', $s);


Как видите и без модификатора u проблем не будет. Вы шерстите теги, переносы, которые в многабайтной кодировке будут представлены также одним байтом. То же самое и с функциями строк - если использовать многие из них для поиска в UTF строке национального символа, то получим неверный результат, и надо использовать функции для работы с многобайтной кодировкой. Но если мы их используем для поиска не национальных символов, то проблем не будет.

snovapavel 11.10.2016 14:19

Цитата:

Сообщение от laimas (Сообщение 431301)
Да еще и <br\> - это из какой спецификации?

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

laimas 11.10.2016 14:26

Цитата:

Сообщение от snovapavel
Дак ведь спецсимвол же?

<br\> - > спецсимовл? Нет.

Цитата:

Сообщение от snovapavel
чётко говорилось - экранируйте всё подряд

Глупости.

Цитата:

Сообщение от snovapavel
И ещё рекомендовалось везде вставлять жадный оператор ?

Тоже не верно, возможно вы не поняли сути.


Часовой пояс GMT +3, время: 16:18.