PHP: Der beste Weg, um Text in Klammern zu extrahieren?

83

Was ist der beste / effizienteste Weg, um Textsätze in Klammern zu extrahieren? Angenommen, ich wollte die Zeichenfolge "Text" aus der Zeichenfolge "Alles außer diesem (Text) ignorieren" so effizient wie möglich erhalten.

Das Beste, was ich mir bisher ausgedacht habe, ist Folgendes:

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

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

Gibt es einen besseren Weg, dies zu tun? Ich weiß, dass die Verwendung von Regex im Allgemeinen weniger effizient ist. Wenn ich jedoch die Anzahl der Funktionsaufrufe nicht reduzieren kann, ist dies möglicherweise der beste Ansatz. Gedanken?

Wilco
quelle
Möglicherweise finden Sie s($fullString)->between("(", ")")hilfreiche Informationen in dieser eigenständigen Bibliothek .
Caw

Antworten:

144

Ich würde einfach eine Regex machen und es hinter mich bringen. Wenn Sie nicht genügend Iterationen ausführen, um ein großes Leistungsproblem zu lösen, ist es einfacher, Code zu erstellen (und zu verstehen, wenn Sie darauf zurückblicken).

$text = 'ignore everything except this (text)';
preg_match('#\((.*?)\)#', $text, $match);
print $match[1];
Owen
quelle
1
Nein, das ist es nicht :. Entspricht nur einem einzelnen Zeichen.
Edward Z. Yang
1
nicht unbedingt, ? ist ein fauler Match. Ohne sie, eine Zeichenfolge wie 'ignorieren (alles) außer diesem (Text)', würde das Match am Ende 'alles) sein, außer diesem (Text'
Owen
1
Gut zu wissen. Sollte all diese quadratischen Nots vermeiden. ZB / src = "([^"] *) "/ jetzt ersetzt durch /src="(.*?)"/: D
Dimitry
Es ist gut, dass Sie "verstehen können, wenn Sie zurückblicken". Andernfalls haben Sie einige Stapelüberlaufkommentare, um dies zu verdeutlichen.
Mnebuerquo
2
das / src = "([^"] *) "/ ist effizienter als /src="(.*?)"/
Tanj
14

Der von Ihnen veröffentlichte Code funktioniert also nicht: Die substr()'sParameter sind $ string, $ start und $ length und die strpos()'sParameter sind $haystack, $needle. Leicht verändert:

$ str = "ignoriere alles außer diesem (Text)";
$ start = strpos ($ str, '(');
$ end = strpos ($ str, ')', $ start + 1);
$ length = $ end - $ start;
$ result = substr ($ str, $ start + 1, $ length - 1);

Einige Feinheiten: Ich habe $start + 1den Offset-Parameter verwendet, um PHP bei der strpos()Suche in der zweiten Klammer zu unterstützen. Wir erhöhen $starteins und reduzieren $length, um die Klammern von der Übereinstimmung auszuschließen.

Außerdem gibt es in diesem Code keine Fehlerprüfung: Sie sollten sicherstellen $startund $endnicht === false, bevor Sie das ausführen substr.

Wie für die Verwendung strpos/substrgegen Regex; In Bezug auf die Leistung schlägt dieser Code einen regulären Ausdruck zweifellos. Es ist allerdings etwas wortreicher. Ich esse und atme strpos/substr, deshalb stört mich das nicht allzu sehr, aber jemand anderes mag die Kompaktheit eines regulären Ausdrucks bevorzugen.

Edward Z. Yang
quelle
9

Verwenden Sie einen regulären Ausdruck:

if( preg_match( '!\(([^\)]+)\)!', $text, $match ) )
    $text = $match[1];
rauben
quelle
3

Dies ist ein Beispielcode, um den gesamten Text zwischen '[' und ']' zu extrahieren und in 2 separaten Arrays zu speichern (dh Text in Klammern in einem Array und Text außerhalb von Klammern in einem anderen Array).

   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);
  }

Ausgabe: extract_text ("Hallo, wie geht es dir?"); wird herstellen:

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

array(0) {
}

extract_text ("Hallo [http://www.google.com/test.mp3] wie geht es dir?"); wird herstellen

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.
quelle
+1 aber wie geht das auch für [* und *]? Weil [] zum Beispiel nur auf HTML verwendet werden kann.
Mike Castro Demaria
1

Diese Funktion kann nützlich sein.

    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 () =>, mit dem die Position des ersten Auftretens in einem String ermittelt wird.

strrpos () =>, mit dem die Position des ersten Auftretens in einem String ermittelt wird.

Vijay
quelle
1

Die bereits veröffentlichten Regex-Lösungen - \((.*?)\)und \(([^\)]+)\)- geben nicht die innersten Zeichenfolgen zwischen einer offenen und einer geschlossenen Klammer zurück. Wenn ein String ist Text (abc(xyz 123)sie beide zurückgeben ein (abc(xyz 123)als Ganzes Spiel, und nicht (xyz 123).

Das Muster, das Teilzeichenfolgen ( preg_matchzum Abrufen der ersten und preg_match_allzum Abrufen aller Vorkommen mit) in Klammern ohne andere offene und geschlossene Klammern dazwischen entspricht, lautet, wenn die Übereinstimmung Klammern enthalten soll:

\([^()]*\)

Oder Sie möchten Werte ohne Klammern erhalten:

\(([^()]*)\)        // 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

Ersetzen Sie *durch, +wenn zwischen (und mindestens 1 Zeichen liegen muss ).

Details :

  • \( - eine öffnende runde Klammer (muss maskiert werden, um eine wörtliche Klammer zu kennzeichnen, da sie außerhalb einer Zeichenklasse verwendet wird)
  • [^()]*- null oder mehr Zeichen , die nicht (und )(beachten Sie diese (und )müssen nicht als im Inneren innerhalb einer Zeichenklasse entronnen sein, (und )nicht verwendet werden kann , eine Gruppierung und werden behandelt , als wörtliche Klammern angeben)
  • \) - eine schließende runde Klammer (muss maskiert werden, um eine wörtliche Klammer zu kennzeichnen, da sie außerhalb einer Zeichenklasse verwendet wird).

Der \(\KTeil in einem alternativen regulären Ausdruck stimmt (mit dem \KÜbereinstimmungswert überein und lässt ihn aus (mit dem Operator zum Zurücksetzen der Übereinstimmung). (?<=\()ist ein positives Lookbehind, bei dem a (unmittelbar links vom aktuellen Speicherort angezeigt werden muss , das (jedoch nicht zum Übereinstimmungswert hinzugefügt wird, da Lookbehind-Muster (Lookaround-Muster) nicht verbraucht werden. (?=\()ist ein positiver Lookahead, für den ein )Zeichen unmittelbar rechts vom aktuellen Standort angezeigt werden muss .

PHP-Code :

$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
}

Ausgabe:

Array ( [0] => (text)  [1] => (text here) )
Array ( [0] => text    [1] => text here   )
Wiktor Stribiżew
quelle
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; }

Dies ist eine kleine Verbesserung gegenüber der vorherigen Antwort, bei der alle Muster in Array-Form zurückgegeben werden:

getStringsBetween ('[T] his [] is [test] string [pattern]') gibt Folgendes zurück:

user628176
quelle
0

Ich denke, dies ist der schnellste Weg, um die Wörter zwischen den ersten Klammern in einer Zeichenfolge zu erhalten.

$string = 'ignore everything except this (text)';
$string = explode(')', (explode('(', $string)[1]))[0];
echo $string;
rüff0
quelle