PHP: najlepszy sposób na wyodrębnienie tekstu w nawiasach?

83

Jaki jest najlepszy / najskuteczniejszy sposób wyodrębnienia tekstu umieszczonego w nawiasach? Powiedzmy, że chciałem uzyskać ciąg „tekst” z ciągu „ignoruj ​​wszystko oprócz tego (tekstu)” w najbardziej efektywny sposób.

Jak dotąd najlepsze, co wymyśliłem, to:

$fullString = "ignore everything except this (text)";
$start = strpos('(', $fullString);
$end = strlen($fullString) - strpos(')', $fullString);

$shortString = substr($fullString, $start, $end);

Czy jest lepszy sposób, aby to zrobić? Wiem, że generalnie używanie wyrażeń regularnych jest mniej wydajne, ale jeśli nie uda mi się zmniejszyć liczby wywołań funkcji, być może byłoby to najlepsze podejście? Myśli?

Wilco
źródło
Może okazać się s($fullString)->between("(", ")")pomocny, co znajdziesz w tej samodzielnej bibliotece .
krakaj

Odpowiedzi:

144

po prostu zrobiłbym regex i skończył z tym. chyba że wykonujesz wystarczającą liczbę iteracji, które stają się ogromnym problemem z wydajnością, po prostu łatwiej jest kodować (i zrozumieć, gdy spojrzysz wstecz)

$text = 'ignore everything except this (text)';
preg_match('#\((.*?)\)#', $text, $match);
print $match[1];
piekarnik
źródło
1
Nie, to nie jest:. dopasowuje tylko jeden znak.
Edward Z. Yang
1
niekoniecznie, ? to leniwy mecz. bez tego ciąg typu „ignoruj ​​(wszystko) oprócz tego (tekst)”, dopasowanie zakończy się jako „wszystko) z wyjątkiem tego (tekst”
Owen
1
Dobrze wiedzieć. Powinien unikać tych wszystkich kwadratów. Np. / Src = "([^"] *) "/ teraz zastąpiono /src="(.*?)"/: D
Dimitry
Dobrze, że możesz „zrozumieć, kiedy się na to spojrzy”. W przeciwnym razie masz kilka komentarzy dotyczących przepełnienia stosu, aby to wyjaśnić.
Mnebuerquo
2
/ src = "([^"] *) "/ jest bardziej wydajne niż /src="(.*?)"/
Tanj
14

Tak, rzeczywiście, kod zostanie zaksięgowana nie działa: substr()'sparametry $ string, $ start i $ długość i strpos()'sparametry $haystack, $needle. Lekko zmieniony:

$ str = "ignoruj ​​wszystko oprócz tego (tekstu)";
$ start = strpos ($ str, '(');
$ end = strpos ($ str, ')', $ start + 1);
$ długość = $ koniec - $ początek;
$ wynik = substr ($ str, $ start + 1, $ długość - 1);

Kilka subtelności: użyłem $start + 1parametru offset, aby pomóc PHP podczas strpos()wyszukiwania w drugim nawiasie; zwiększamy $startjeden i zmniejszamy, $lengthaby wykluczyć nawiasy z dopasowania.

Ponadto nie ma sprawdzania błędów w tym kodzie: będziesz chciał się upewnić $starti $endnie === false przed wykonaniem substr.

Jeśli chodzi o używanie w strpos/substrporównaniu do wyrażenia regularnego; pod względem wydajności kod ten bez trudu pokonuje wyrażenie regularne. Jest jednak trochę bardziej wyrazisty. Jem i oddycham strpos/substr, więc nie przeszkadza mi to zbytnio, ale ktoś inny może preferować zwartość wyrażenia regularnego.

Edward Z. Yang
źródło
1
Zauważ, że jeśli zmodyfikujesz ten kod tak, aby używał strrpos (zaczyna się od końca łańcucha) na końcu $, to będzie on poprawnie obsługiwał przypadki, w których znajdują się pareny w ... jak (cóż, to jest (bardzo) ładne).
ftrotter
9

Użyj wyrażenia regularnego:

if( preg_match( '!\(([^\)]+)\)!', $text, $match ) )
    $text = $match[1];
Obrabować
źródło
3

To jest przykładowy kod do wyodrębnienia całego tekstu między „[” a „]” i zapisania go w 2 oddzielnych tablicach (tj. Tekst w nawiasach w jednej tablicy i tekst poza nawiasami w innej tablicy)

   function extract_text($string)
   {
    $text_outside=array();
    $text_inside=array();
    $t="";
    for($i=0;$i<strlen($string);$i++)
    {
        if($string[$i]=='[')
        {
            $text_outside[]=$t;
            $t="";
            $t1="";
            $i++;
            while($string[$i]!=']')
            {
                $t1.=$string[$i];
                $i++;
            }
            $text_inside[] = $t1;

        }
        else {
            if($string[$i]!=']')
            $t.=$string[$i];
            else {
                continue;
            }

        }
    }
    if($t!="")
    $text_outside[]=$t;

    var_dump($text_outside);
    echo "\n\n";
    var_dump($text_inside);
  }

Wyjście: extract_text ("cześć, jak się masz?"); będzie produkować:

array(1) {
  [0]=>
  string(18) "hello how are you?"
}

array(0) {
}

extract_text ("cześć [http://www.google.com/test.mp3] jak się masz?"); będzie produkować

array(2) {
  [0]=>
  string(6) "hello "
  [1]=>
  string(13) " how are you?"
}


array(1) {
  [0]=>
  string(30) "http://www.google.com/test.mp3"
}
Sachin Murali G.
źródło
+1, ale jak to zrobić dla [* i *]? Ponieważ [] może być używane tylko na przykład w html.
Mike Castro Demaria
1

Ta funkcja może być przydatna.

    public static function getStringBetween($str,$from,$to, $withFromAndTo = false)
    {
       $sub = substr($str, strpos($str,$from)+strlen($from),strlen($str));
       if ($withFromAndTo)
         return $from . substr($sub,0, strrpos($sub,$to)) . $to;
       else
         return substr($sub,0, strrpos($sub,$to));
    }
    $inputString = "ignore everything except this (text)";
    $outputString = getStringBetween($inputString, '(', ')'));
    echo $outputString; 
    //output will be test

    $outputString = getStringBetween($inputString, '(', ')', true));
    echo $outputString; 
    //output will be (test)

strpos () =>, który służy do znalezienia pozycji pierwszego wystąpienia w ciągu.

strrpos () =>, który służy do znalezienia pozycji pierwszego wystąpienia w ciągu.

vijay
źródło
1

Już opublikowane rozwiązania regex - \((.*?)\)i \(([^\)]+)\)- nie zwracają najbardziej wewnętrznych ciągów między nawiasami otwierającymi i zamykającymi. Jeśli ciąg jest Text (abc(xyz 123)one zarówno powrócić(abc(xyz 123) jako cały mecz, a nie (xyz 123).

Wzorzec pasujący do podciągów (użyj z, preg_matchaby pobrać pierwszy i preg_match_allwszystkie wystąpienia) w nawiasach bez innych otwierających i zamykających nawiasów między nimi to, jeśli dopasowanie powinno zawierać nawiasy:

\([^()]*\)

Lub chcesz uzyskać wartości bez nawiasów:

\(([^()]*)\)        // get Group 1 values after a successful call to preg_match_all, see code below
\(\K[^()]*(?=\))    // this and the one below get the values without parentheses as whole matches 
(?<=\()[^()]*(?=\)) // less efficient, not recommended

Wymień *się +jeśli nie musi wynosić co najmniej 1 char pomiędzy (i ).

Szczegóły :

  • \( - otwierający nawias okrągły (musi zostać zmieniony, aby wskazać literalny nawias, ponieważ jest używany poza klasą znaków)
  • [^()]*- zero lub więcej znaków innych niż (i) (zwróć uwagę na te (i )nie muszą być znakami ucieczki wewnątrz klasy znaków, tak jak w niej, (i )nie można ich użyć do określenia grupowania i są traktowane jako dosłowne nawiasy)
  • \) - zamykający nawias okrągły (musi zostać zmieniony, aby wskazać literalny nawias, ponieważ jest używany poza klasą znaków).

\(\KCzęści w alternatywnych dopasowania regex (i pomija z wartości dopasowania (z \Koperatorem zerowania meczu). (?<=\()to pozytywne spojrzenie w tył, które wymaga (natychmiastowego pojawienia się po lewej stronie bieżącej lokalizacji, ale( nie jest dodawany do wartości dopasowania, ponieważ wzorce lookbehind (patrzenie w tył) nie są używane. (?=\()to pozytywne spojrzenie w przód, które wymaga )natychmiastowego pojawienia się znaku po prawej stronie bieżącej lokalizacji.

Kod PHP :

$fullString = 'ignore everything except this (text) and (that (text here))';
if (preg_match_all('~\(([^()]*)\)~', $fullString, $matches)) {
    print_r($matches[0]); // Get whole match values
    print_r($matches[1]); // Get Group 1 values
}

Wynik:

Array ( [0] => (text)  [1] => (text here) )
Array ( [0] => text    [1] => text here   )
Wiktor Stribiżew
źródło
0
function getStringsBetween($str, $start='[', $end=']', $with_from_to=true){
$arr = [];
$last_pos = 0;
$last_pos = strpos($str, $start, $last_pos);
while ($last_pos !== false) {
    $t = strpos($str, $end, $last_pos);
    $arr[] = ($with_from_to ? $start : '').substr($str, $last_pos + 1, $t - $last_pos - 1).($with_from_to ? $end : '');
    $last_pos = strpos($str, $start, $last_pos+1);
}
return $arr; }

jest to małe ulepszenie poprzedniej odpowiedzi, która zwróci wszystkie wzorce w postaci tablicy:

getStringsBetween ('[T] his [] is [test] string [pattern]') zwróci:

user628176
źródło
0

Myślę, że to najszybszy sposób na umieszczenie słów między pierwszym nawiasem w ciągu.

$string = 'ignore everything except this (text)';
$string = explode(')', (explode('(', $string)[1]))[0];
echo $string;
rüff0
źródło