PHP взрывает строку, но обрабатывает слова в кавычках как одно слово

Как я могу взорвать следующую строку:

Lorem ipsum "dolor sit amet" consectetur "adipiscing elit" dolor

в

array("Lorem", "ipsum", "dolor sit amet", "consectetur", "adipiscing elit", "dolor")

Так что текст в цитате рассматривается как одно слово.

Вот что у меня есть на данный момент:

$mytext = "Lorem ipsum %22dolor sit amet%22 consectetur %22adipiscing elit%22 dolor"
$noquotes = str_replace("%22", "", $mytext");
$newarray = explode(" ", $noquotes);

но мой код делит каждое слово на массив. Как сделать так, чтобы слова в кавычках рассматривались как одно слово?


person timofey    schedule 04.02.2010    source источник
comment
Это похоже на работу для Regex   -  person Earlz    schedule 04.02.2010
comment


Ответы (5)


Вы можете использовать preg_match_all(...):

$text = 'Lorem ipsum "dolor sit amet" consectetur "adipiscing \\"elit" dolor';
preg_match_all('/"(?:\\\\.|[^\\\\"])*"|\S+/', $text, $matches);
print_r($matches);

который будет производить:

Array
(
    [0] => Array
        (
            [0] => Lorem
            [1] => ipsum
            [2] => "dolor sit amet"
            [3] => consectetur
            [4] => "adipiscing \"elit"
            [5] => dolor
        )

)

И, как вы можете видеть, он также учитывает экранированные кавычки внутри строк в кавычках.

ИЗМЕНИТЬ

Краткое объяснение:

"           # match the character '"'
(?:         # start non-capture group 1 
  \\        #   match the character '\'
  .         #   match any character except line breaks
  |         #   OR
  [^\\"]    #   match any character except '\' and '"'
)*          # end non-capture group 1 and repeat it zero or more times
"           # match the character '"'
|           # OR
\S+         # match a non-whitespace character: [^\s] and repeat it one or more times

И в случае совпадения %22 вместо двойных кавычек вы должны сделать:

preg_match_all('/%22(?:\\\\.|(?!%22).)*%22|\S+/', $text, $matches);
person Bart Kiers    schedule 04.02.2010
comment
Есть ли причина не использовать preg_split вместо preg_match_all? это кажется более естественным, ИМО. - person prodigitalson; 04.02.2010
comment
Это потрясающе! Мне придется немного изучить код, чтобы понять, что только что произошло! Благодарность - person timofey; 04.02.2010
comment
@prodigitalson: нет, используя preg_split(...), вы не можете учитывать экранированные символы. preg_match_all(...) ведет себя скорее как синтаксический анализатор, что здесь более естественно. Кроме того, при использовании preg_split(...) вам нужно будет смотреть вперед в каждом пробеле, чтобы увидеть, сколько кавычек впереди него, что делает его операцией O(n^2): нет проблем для небольших строк, но может уменьшить время выполнения, когда задействованы большие строки. - person Bart Kiers; 04.02.2010
comment
@timofey, посмотри мое редактирование. Не стесняйтесь просить дополнительных разъяснений, если вам что-то непонятно: вы тот, кто поддерживает код, поэтому вы должны его понять (и я более чем счастлив предоставить дополнительную информацию, если она потребуется). - person Bart Kiers; 04.02.2010
comment
Спасибо, Барт К.! Я уже искал в гугле ответы на этот вопрос :) - person timofey; 04.02.2010
comment
Но тогда, если я хочу заменить Lorem ipsum %22dolor sit amet%22 consectetur %22adipiscing elit%22 dolor (в основном кавычки указаны как %22), следующее не работает: preg_match_all('/%22(? :\\\\.|[^\\\\])*%22|\S+/', $text, $matches); - person timofey; 04.02.2010
comment
Это начинает иметь смысл! Спасибо - person timofey; 04.02.2010
comment
#Барт К.: Понятно... Спасибо за информацию! - person prodigitalson; 05.02.2010
comment
В строках php с одинарными кавычками '\' не исчезнет, ​​поэтому вам не нужно \\\\ для одного \. - person Calmarius; 22.12.2010
comment
О, это неправда. \ и ' по-прежнему следует экранировать. прости - person Calmarius; 22.12.2010
comment
почему ваше решение делает это pastebin.com/bhrnMGST для этой строки - внутри нее есть \предложение в кавычках\ - person madphp; 10.06.2011
comment
@Bart Kiers ваше решение применимо к моему примеру? - person madphp; 10.06.2011
comment
@Барт Кирс Спасибо! Если у меня одинарные кавычки? - person madphp; 10.06.2011
comment
@Bart Kiers Вещи немного изменились. Извини за это. После использования mysql_real_escape_string() я получаю следующее: внутри есть \\\'предложение в кавычках\\\'. Поэтому мне нужно учитывать эти дополнительные косые черты (я не знаю, имеет ли это значение) и одинарные или двойные кавычки. - person madphp; 10.06.2011
comment
@ Барт Кирс, это не продлится и 2 минут. хаха. Дайте мне еще одно регулярное выражение, и я уйду. - person madphp; 10.06.2011
comment
Альтернатива предварительному разделению: stackoverflow.com/a/32034603/2897386 - person DustWolf; 16.12.2020

Это было бы намного проще с str_getcsv().

$test = 'Lorem ipsum "dolor sit amet" consectetur "adipiscing elit" dolor';
var_dump(str_getcsv($test, ' '));

Дает тебе

array(6) {
  [0]=>
  string(5) "Lorem"
  [1]=>
  string(5) "ipsum"
  [2]=>
  string(14) "dolor sit amet"
  [3]=>
  string(11) "consectetur"
  [4]=>
  string(15) "adipiscing elit"
  [5]=>
  string(5) "dolor"
}
person Petah    schedule 07.07.2011
comment
Это работает на моей машине разработки, но не на моем рабочем сервере. :-/ - person Martin Ueding; 17.03.2012
comment
Для str_getcsv требуется PHP 5.3. - person armakuni; 02.08.2013
comment
Ааа почему я только сейчас узнаю об этой функции?! - person Drew Hammond; 03.01.2015
comment
Хорошее решение — использовать что-то уже встроенное в язык и не полагаться на регулярные выражения. - person Sheldon Juncker; 25.08.2017
comment
На сегодняшний день это наиболее подходящий ответ на вопрос (PHP ≥ 7, 2018 г.). - person pixelbrackets; 28.03.2018
comment
Имейте в виду, что он игнорирует кавычки. Если вам нужно, чтобы они были в расколе, то это не сработает. - person Gayan Dasanayake; 12.04.2018
comment
Я сделал тест скорости, и preg_match_all примерно в 3-5 раз быстрее. Вероятно, это не проблема для большинства людей, особенно если не нужны кавычки (в этом случае их намного проще использовать), но я думаю, стоит упомянуть. - person err; 01.01.2019
comment
@err хочешь поделиться тестами? - person Petah; 05.01.2019
comment
Ничего особенного, просто обернул цикл от 1 до 10000 и проверил микротаймы до и после. Оба достаточно быстры для одноразового использования, даже с тестовым количеством, поэтому я упомянул, что это, вероятно, не будет проблемой для большинства из нас. - person err; 06.01.2019
comment
Ты мой герой. Меня не интересовало использование сложного регулярного выражения, которое я не мог понять. Но это решение, теперь это решение! :D - person l008com; 09.01.2019

Вы также можете попробовать эту функцию множественного взрыва

function multiexplode ($delimiters,$string)
{

$ready = str_replace($delimiters, $delimiters[0], $string);
$launch = explode($delimiters[0], $ready);
return  $launch;
}

$text = "here is a sample: this text, and this will be exploded. this also | this one too :)";
$exploded = multiexplode(array(",",".","|",":"),$text);

print_r($exploded);
person Nikz    schedule 25.05.2013
comment
Этот ответ хорош, но если вы попросите его разделить на пробелы и кавычки, он разделится на пробелы внутри кавычек. - person starbeamrainbowlabs; 20.05.2015

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

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

Это, вероятно, очень медленный и неэффективный способ сделать это, но он работает для меня.

function explode_adv($openers, $closers, $togglers, $delimiters, $str)
{
    $chars = str_split($str);
    $parts = [];
    $nextpart = "";
    $toggle_states = array_fill_keys($togglers, false); // true = now inside, false = now outside
    $depth = 0;
    foreach($chars as $char)
    {
        if(in_array($char, $openers))
            $depth++;
        elseif(in_array($char, $closers))
            $depth--;
        elseif(in_array($char, $togglers))
        {
            if($toggle_states[$char])
                $depth--; // we are inside a toggle block, leave it and decrease the depth
            else
                // we are outside a toggle block, enter it and increase the depth
                $depth++;

            // invert the toggle block state
            $toggle_states[$char] = !$toggle_states[$char];
        }
        else
            $nextpart .= $char;

        if($depth < 0) $depth = 0;

        if(in_array($char, $delimiters) &&
           $depth == 0 &&
           !in_array($char, $closers))
        {
            $parts[] = substr($nextpart, 0, -1);
            $nextpart = "";
        }
    }
    if(strlen($nextpart) > 0)
        $parts[] = $nextpart;

    return $parts;
}

Использование заключается в следующем. explode_adv принимает 5 аргументов:

  1. Массив символов, открывающих блок, например. [, ( и т. д.
  2. Массив символов, закрывающих блок, например. ], ) и т. д.
  3. Массив символов, которые переключают блок, например. ", ' и т. д.
  4. Массив символов, который должен вызвать разделение на следующую часть.
  5. Строка для работы.

Этот метод, вероятно, имеет недостатки - правки приветствуются.

person starbeamrainbowlabs    schedule 20.05.2015

В некоторых ситуациях может оказаться полезным малоизвестный token_get_all():

$tokens = token_get_all("<?php $text ?>");
$separator = ' ';
$items = array();
$item = "";
$last = count($tokens) - 1;
foreach($tokens as $index => $token) {
    if($index != 0 && $index != $last) {
        if(count($token) == 3) {
            if($token[0] == T_CONSTANT_ENCAPSED_STRING) {
                $token = substr($token[1], 1, -1);
            } else {
                $token = $token[1];
            }
        }
        if($token == $separator) {
            $items[] = $item;
            $item = "";
        } else {
            $item .= $token;
        }
    }
}

Результаты:

Array
(
    [0] => Lorem
    [1] => ipsum
    [2] => dolor sit amet
    [3] => consectetur
    [4] => adipiscing elit
    [5] => dolor
)
person cleong    schedule 01.11.2014