PHP: Cách tốt nhất để trích xuất văn bản trong ngoặc đơn?


83

Cách tốt nhất / hiệu quả nhất để trích xuất văn bản đặt giữa dấu ngoặc đơn là gì? Giả sử tôi muốn lấy chuỗi "văn bản" từ chuỗi "bỏ qua mọi thứ ngoại trừ (văn bản)" theo cách hiệu quả nhất có thể.

Cho đến nay, điều tốt nhất tôi nghĩ ra là:

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

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

Có cách nào tốt hơn để làm điều này? Tôi biết nói chung sử dụng regex có xu hướng kém hiệu quả hơn, nhưng trừ khi tôi có thể giảm số lượng lệnh gọi hàm, có lẽ đây sẽ là cách tiếp cận tốt nhất? Suy nghĩ?


Bạn có thể thấy s($fullString)->between("(", ")")hữu ích, như được tìm thấy trong thư viện độc lập này .
caw

Câu trả lời:


144

tôi chỉ làm một regex và kết thúc nó với. trừ khi bạn đang thực hiện đủ số lần lặp lại khiến nó trở thành một vấn đề lớn về hiệu suất, thì việc viết mã sẽ dễ dàng hơn (và hiểu khi bạn nhìn lại nó)

$text = 'ignore everything except this (text)';
preg_match('#\((.*?)\)#', $text, $match);
print $match[1];

1
Không, nó không phải:. chỉ khớp với một ký tự duy nhất.
Edward Z. Yang

1
không cần thiết, ? là một trận đấu lười biếng. nếu không có nó, một chuỗi như 'bỏ qua (mọi thứ) ngoại trừ điều này (văn bản)', kết quả phù hợp sẽ là 'mọi thứ) ngoại trừ điều này (văn bản'
Owen

1
Tốt để biết. Nên tránh tất cả những ký hiệu bình phương. Ví dụ: / src = "([^"] *) "/ hiện được thay thế bằng /src="(.*?)"/: D
Dimitry

Thật tốt khi bạn có thể "hiểu được khi bạn nhìn lại nó". Không đạt được điều đó, bạn có một số nhận xét về Stack Overflow để làm rõ điều đó.
Mnebuerquo 13/10/08

2
the / src = "([^"] *) "/ hiệu quả hơn /src="(.*?)"/
Tanj

14

Vì vậy, trên thực tế, mã bạn được đăng không hoạt động: substr()'stham số $ string, $ bắt đầu và $ chiều dài , và strpos()'sthông số $haystack, $needle. Đã sửa đổi một chút:

$ str = "bỏ qua mọi thứ ngoại trừ this (text)";
$ start = strpos ($ str, '(');
$ end = strpos ($ str, ')', $ start + 1);
$ length = $ end - $ start;
$ result = substr ($ str, $ start + 1, $ length - 1);

Một số điều tinh tế: Tôi đã sử dụng $start + 1tham số offset để giúp PHP trong khi thực hiện strpos()tìm kiếm trên dấu ngoặc đơn thứ hai; chúng tôi tăng $startmột và giảm $lengthđể loại trừ dấu ngoặc đơn khỏi kết quả khớp.

Ngoài ra, không có lỗi khi kiểm tra mã này: bạn sẽ muốn chắc chắn $start$endkhông === false trước khi thực hiện substr.

Đối với việc sử dụng strpos/substrso với regex; về hiệu suất, mã này sẽ đánh bại một biểu thức chính quy. Tuy nhiên, nó đáng lo hơn một chút. Tôi ăn và hít thở strpos/substr, vì vậy tôi không quá bận tâm về điều này, nhưng ai đó có thể thích sự nhỏ gọn của regex.


9

Sử dụng một biểu thức chính quy:

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

3

Đây là mã mẫu để trích xuất tất cả văn bản giữa '[' và ']' và lưu trữ nó 2 mảng riêng biệt (nghĩa là văn bản bên trong dấu ngoặc đơn trong một mảng và văn bản bên ngoài dấu ngoặc đơn trong một mảng khác)

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

Output: extract_text ("xin chào bạn khỏe không?"); sẽ sản xuất:

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

array(0) {
}

extract_text ("xin chào [http://www.google.com/test.mp3] bạn có khỏe không?"); sẽ sản xuất

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


array(1) {
  [0]=>
  string(30) "http://www.google.com/test.mp3"
}

+1 nhưng làm thế nào để làm tương tự cho [* và *]? Vì [] chỉ có thể được sử dụng trên html chẳng hạn.
Mike Castro Demaria

1

Chức năng này có thể hữu ích.

    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 () => được sử dụng để tìm vị trí của lần xuất hiện đầu tiên trong một chuỗi.

strrpos () => được sử dụng để tìm vị trí của lần xuất hiện đầu tiên trong một chuỗi.


1

Các giải pháp regex đã được đăng - \((.*?)\)\(([^\)]+)\)- không trả về các chuỗi trong cùng giữa dấu ngoặc mở và đóng. Nếu là một chuỗi Text (abc(xyz 123)thì cả hai đều trả về một (abc(xyz 123)kết quả so khớp toàn bộ và không (xyz 123).

Mẫu đối sánh với các chuỗi con (sử dụng với preg_matchđể tìm nạp lần đầu tiên và preg_match_allđể tìm nạp tất cả các lần xuất hiện) trong dấu ngoặc đơn không có các dấu ngoặc mở và đóng khác ở giữa là, nếu kết quả khớp phải bao gồm dấu ngoặc đơn:

\([^()]*\)

Hoặc, bạn muốn nhận các giá trị không có dấu ngoặc đơn:

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

Thay thế *bằng +nếu phải có ít nhất 1 ký tự giữa ().

Chi tiết :

  • \( - một dấu ngoặc tròn mở (phải được thoát ra để biểu thị một dấu ngoặc đơn vì nó được sử dụng bên ngoài một lớp ký tự)
  • [^()]*- không hoặc nhiều ký tự khác ngoài ()(lưu ý những ký tự này ()không phải được thoát ra bên trong một lớp ký tự như bên trong nó ()không thể được sử dụng để chỉ định một nhóm và được coi là dấu ngoặc đơn theo nghĩa đen)
  • \) - một dấu ngoặc tròn đóng (phải được thoát ra để biểu thị một dấu ngoặc theo nghĩa đen vì nó được sử dụng bên ngoài một lớp ký tự).

Phần \(\Ktrong regex thay thế khớp (và bị bỏ qua khỏi giá trị khớp (với \Ktoán tử đặt lại khớp). (?<=\()là một giao diện tích cực yêu cầu dấu hiệu (xuất hiện ngay lập tức ở bên trái của vị trí hiện tại, nhưng (giá trị này không được thêm vào giá trị đối sánh vì các mẫu hậu cảnh (lookaround) không tiêu tốn. (?=\()là một cái nhìn tích cực yêu cầu một )biểu tượng xuất hiện ngay lập tức ở bên phải của vị trí hiện tại.

Mã 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
}

Đầu ra:

Array ( [0] => (text)  [1] => (text here) )
Array ( [0] => text    [1] => text here   )

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

đây là một cải tiến nhỏ đối với câu trả lời trước đó sẽ trả về tất cả các mẫu ở dạng mảng:

getStringsBetween ('[T] his [] is [test] string [pattern]') sẽ trả về:


0

tôi nghĩ đây là cách nhanh nhất để lấy các từ giữa dấu ngoặc đơn đầu tiên trong một chuỗi.

$string = 'ignore everything except this (text)';
$string = explode(')', (explode('(', $string)[1]))[0];
echo $string;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.