Sử dụng str numplace để nó chỉ hoạt động trong trận đấu đầu tiên?


Câu trả lời:


345

Có thể được thực hiện với preg numplace :

function str_replace_first($from, $to, $content)
{
    $from = '/'.preg_quote($from, '/').'/';

    return preg_replace($from, $to, $content, 1);
}

echo str_replace_first('abc', '123', 'abcdef abcdef abcdef'); 
// outputs '123def abcdef abcdef'

Phép thuật nằm trong tham số thứ tư tùy chọn [Giới hạn]. Từ tài liệu:

[Giới hạn] - Thay thế tối đa có thể có cho mỗi mẫu trong mỗi chuỗi chủ đề. Mặc định là -1 (không giới hạn).


Mặc dù vậy, hãy xem câu trả lời của zombat để biết phương pháp hiệu quả hơn (nhanh hơn khoảng 3-4 lần).


39
Nhược điểm của phương pháp này là hình phạt hiệu suất của các biểu thức thông thường.
zombat

27
Một nhược điểm khác là bạn phải sử dụng preg_quote () trên "kim" và thoát các ký tự meta $ và \ trong phần thay thế.
Josh Davis

32
Điều này thất bại như là một giải pháp chung chung do các vấn đề thoát khó chịu.
Jeremy Kauffman

2
Quá thường xuyên các biểu thức chính quy bị loại bỏ do 'hiệu suất', nếu hiệu suất là mối quan tâm chính, chúng tôi sẽ không viết PHP! Một cái gì đó không phải là '/' có thể được sử dụng để bọc mẫu, có lẽ là '~', giúp tránh sự cố thoát ra ở một mức độ nào đó. Nó phụ thuộc vào dữ liệu là gì và nó đến từ đâu.
ThomasRedstone

1
Giảm hiệu suất sang một bên - những người phàn nàn về việc thoát khỏi các vấn đề có bất cứ điều gì cụ thể trong tâm trí, bên cạnh các lỗi tiềm ẩn trong preg_quote? Ví dụ: @ThomasRedstone lo lắng rằng dấu phân cách /có thể nguy hiểm nếu nó xuất hiện $from, nhưng may mắn thay, đó không phải là: nó được thoát đúng vì preg_quotetham số thứ hai (người ta có thể dễ dàng kiểm tra điều đó). Tôi rất muốn nghe về các vấn đề cụ thể (đó sẽ là lỗi bảo mật PCRE nghiêm trọng trong cuốn sách của tôi).
MvanGeest

610

Không có phiên bản nào của nó, nhưng giải pháp không hề khó hiểu.

$pos = strpos($haystack, $needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
}

Khá dễ dàng, và tiết kiệm hình phạt hiệu suất của các biểu thức thông thường.


Tiền thưởng: Nếu bạn muốn thay thế lần xuất hiện cuối cùng , chỉ cần sử dụng strrposthay thế strpos.


17
Có thể nhanh hơn nhiều và sẽ sử dụng ít bộ nhớ hơn các biểu thức thông thường. Không biết tại sao ai đó sẽ bỏ phiếu xuống ...
Josh Davis

12
Tôi thích cách tiếp cận này, nhưng mã có lỗi, tham số cuối cùng của lệnh gọi lớp nền nên là strlen ($ kim) thay vì strlen ($ thay thế) .. xin hãy cẩn thận về điều đó !!
Nelson

Đó là "hacky" theo nghĩa là phải mất nhiều thời gian hơn để tìm hiểu những gì đang xảy ra. Ngoài ra nếu đó là mã rõ ràng, nó sẽ không được đề cập rằng mã có lỗi. Nếu có thể phạm sai lầm trong một đoạn nhỏ như vậy, thì đã quá hack rồi.
Camilo Martin

9
Tôi không đồng ý với @CamiloMartin liên quan đến số lượng dòng so với khả năng sai lầm. Mặc dù substr_replacelà một hàm hơi khó sử dụng do tất cả các tham số, nhưng vấn đề thực sự là việc thực hiện thao tác chuỗi bằng số đôi khi chỉ là khó khăn - bạn phải cẩn thận để chuyển đúng / bù cho các hàm. Tôi thực sự đã đi xa để nói rằng đoạn mã trên là cách tiếp cận đơn giản nhất và theo tôi, hợp lý.
Alex

1
Cách tiếp cận rực rỡ. Hoạt động hoàn hảo khi thay thế các giá trị biến có ký tự regex dành riêng trong chúng (vì vậy preg numplace là chịu). Điều này là đơn giản và thanh lịch.
Praesagus

96

Chỉnh sửa: cả hai câu trả lời đã được cập nhật và bây giờ là chính xác. Tôi sẽ để lại câu trả lời vì thời gian chức năng vẫn còn hữu ích.

Rất tiếc, câu trả lời của 'zombat' và 'quá nhiều php' không đúng. Đây là bản sửa đổi cho câu trả lời zombat đã đăng (vì tôi không đủ uy tín để đăng bình luận):

$pos = strpos($haystack,$needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack,$replace,$pos,strlen($needle));
}

Lưu ý strlen ($ kim), thay vì strlen ($ thay thế). Ví dụ của Zombat sẽ chỉ hoạt động chính xác nếu kim và thay thế có cùng độ dài.

Đây là chức năng tương tự trong một chức năng có cùng chữ ký với str numplace của PHP:

function str_replace_first($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos !== false) {
        return substr_replace($subject, $replace, $pos, strlen($search));
    }
    return $subject;
}

Đây là câu trả lời sửa đổi của 'quá nhiều php':

implode($replace, explode($search, $subject, 2));

Lưu ý 2 ở cuối thay vì 1. Hoặc ở định dạng hàm:

function str_replace_first($search, $replace, $subject) {
    return implode($replace, explode($search, $subject, 2));
}

Tôi đã tính thời gian cho hai chức năng và chức năng đầu tiên nhanh gấp đôi khi không tìm thấy kết quả khớp. Chúng có cùng tốc độ khi một trận đấu được tìm thấy.


Tại sao không khái quát hóa như thế này: str numplace_flexible (hỗn hợp $ s, hỗn hợp $ r, int $ offset, int $ giới hạn) trong đó hàm thay thế các lần xuất hiện giới hạn $ bắt đầu từ khớp $ offset (nth).
Adam Friedman

Quá tệ điều này chỉ áp dụng cho các thay thế phân biệt chữ hoa chữ thường.
andreszs 19/03/2015

4
@Andrew stripos()để giải cứu :-)
Gras Double

76

Tôi tự hỏi cái nào là nhanh nhất, vì vậy tôi đã kiểm tra tất cả.

Dưới đây bạn sẽ tìm thấy:

  • Một danh sách toàn diện về tất cả các chức năng đã được đóng góp trên trang này
  • Kiểm tra điểm chuẩn cho mỗi lần đối chiếu (thời gian thực hiện trung bình trên 10.000 lần chạy)
  • Liên kết đến từng câu trả lời (cho mã đầy đủ)

Tất cả các chức năng đã được kiểm tra với cùng một cài đặt:

$string = 'OOO.OOO.OOO.S';
$search = 'OOO'; 
$replace = 'B';

Các hàm chỉ thay thế lần xuất hiện đầu tiên của chuỗi trong chuỗi:


Các hàm chỉ thay thế lần xuất hiện cuối cùng của chuỗi trong chuỗi:


Cảm ơn vì điều này, tôi thường sử dụng
preg numplace

@oLinkWebDevelopment Tôi muốn xem kịch bản điểm chuẩn của bạn. Tôi nghĩ rằng nó có thể hữu ích.
Dave Morton

Lý do tại sao substr_replace()chiến thắng kết quả là đơn giản; bởi vì nó là một chức năng nội bộ. Hai chức năng bên trong và cùng một thứ do người dùng định nghĩa khác nhau về hiệu năng, bởi vì chức năng bên trong chạy ở các lớp thấp hơn. Vậy tại sao khôngpreg_match() ? Các biểu thức thông thường hầu như chậm hơn mọi hàm thao tác chuỗi bên trong, do quốc gia của chúng tìm kiếm trong một chuỗi nhiều lần.
MAChitgarha

1
Tôi hy vọng rằng điểm chuẩn trên "người chiến thắng" ( substr_replace($string, $replace, 0, strlen($search));) của bạn không chỉ đơn thuần là viết tĩnh đó 0. Một phần của sự kết hợp của các giải pháp phi regex là chúng cần "tìm" điểm bắt đầu trước khi biết nơi để thay thế.
mickmackusa

55

Thật không may, tôi không biết bất kỳ hàm PHP nào có thể làm điều này.
Bạn có thể tự cuộn khá dễ dàng như thế này:

function replace_first($find, $replace, $subject) {
    // stolen from the comments at PHP.net/str_replace
    // Splits $subject into an array of 2 items by $find,
    // and then joins the array with $replace
    return implode($replace, explode($find, $subject, 2));
}

Tôi nghĩ rằng đây là phiên bản golf nhất trong số tất cả - sử dụng jointhay vì implode.
Tít

return implode($replace, explode($find, $subject, $limit+1));cho các số thay thế tùy chỉnh
beppe9000

7

Tôi đã tạo ra hàm nhỏ này thay thế chuỗi trên chuỗi (phân biệt chữ hoa chữ thường) có giới hạn mà không cần Regrec. Nó hoạt động tốt.

function str_replace_limit($search, $replace, $string, $limit = 1) {
    $pos = strpos($string, $search);

    if ($pos === false) {
        return $string;
    }

    $searchLen = strlen($search);

    for ($i = 0; $i < $limit; $i++) {
        $string = substr_replace($string, $replace, $pos, $searchLen);

        $pos = strpos($string, $search);

        if ($pos === false) {
            break;
        }
    }

    return $string;
}

Ví dụ sử dụng:

$search  = 'foo';
$replace = 'bar';
$string  = 'foo wizard makes foo brew for evil foo and jack';
$limit   = 2;

$replaced = str_replace_limit($search, $replace, $string, $limit);

echo $replaced;
// bar wizard makes bar brew for evil foo and jack

Mặc dù tôi thà làm ===falsethay vì is_bool(nói rõ ràng hơn - tôi đang giơ ngón tay cái này lên chỉ vì nó đã tránh được sự điên rồ của RegExp ! ... và đồng thời nó đang hoạt động và giải pháp sạch sẽ ...
jave.web

Thích một preg_giải pháp dễ dàng tùy biến không phải là sự điên rồ mà là một sở thích cá nhân. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);khá đơn giản để đọc cho những người không sợ regex. Cần tìm kiếm không phân biệt chữ hoa chữ thường? Thêm isau dấu phân cách mẫu kết thúc. Cần hỗ trợ unicode / multibyte? Thêm usau dấu phân cách mẫu kết thúc. Cần hỗ trợ ranh giới từ? Thêm \bvào cả hai mặt của chuỗi tìm kiếm của bạn. Nếu bạn không muốn regex, đừng sử dụng regex. Ngựa cho các khóa học, nhưng chắc chắn không điên.
mickmackusa

3

Cách dễ nhất sẽ là sử dụng biểu thức chính quy.

Một cách khác là tìm vị trí của chuỗi với strpose () và sau đó là một lớp nền ()

Nhưng tôi thực sự sẽ đi cho RegExp.


"Gợi ý" này khá mơ hồ / giá trị thấp so với các bài đăng khác trên trang này.
mickmackusa

3
function str_replace_once($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos === false) {
        return $subject;
    }

    return substr($subject, 0, $pos) . $replace . substr($subject, $pos + strlen($search));
}

Các câu trả lời chỉ có mã là giá trị thấp trên StackOverflow vì chúng làm rất kém trong việc giáo dục / trao quyền cho hàng ngàn nhà nghiên cứu trong tương lai.
mickmackusa

3

=> MÃ ĐƯỢC SỬA ĐỔI, vì vậy hãy xem xét một số ý kiến ​​quá cũ

cảm ơn mọi người giúp tôi cải thiện điều đó

Bất kỳ BUG, ​​xin vui lòng liên lạc với tôi; Tôi sẽ sửa nó ngay sau đó

Vì vậy, hãy cho:

Thay thế 'o' đầu tiên thành 'ea' chẳng hạn:

$s='I love you';
$s=str_replace_first('o','ea',$s);
echo $s;

//output: I leave you

Chức năng:

function str_replace_first($a,$b,$s)
         {
         $w=strpos($s,$a);
         if($w===false)return $s;
         return substr($s,0,$w).$b.substr($s,$w+strlen($a));
         }

Thất bại nếu $ này đã lặp lại các ký tự như aaa vs aaaaaaaaa
Cristo

Tôi nghĩ rằng nên được substr($where,$b+strlen($this)), không substr($where,$b+1). Và tôi đoán đó substr_replacelà nhanh hơn.
Tít

Mã đã được sửa đổi, bây giờ nó hoạt động ngay cả đối với các chuỗi dài
PYK

Giải pháp này không hoạt động như mã hóa. Bằng chứng: 3v4l.org/cMeZj Và khi bạn khắc phục vấn đề đặt tên biến, nó không hoạt động khi không tìm thấy giá trị tìm kiếm - nó làm hỏng chuỗi đầu vào. Bằng chứng: 3v4l.org/XHtfc
mickmackusa

Có công bằng cho ai đó yêu cầu CỐ ĐỊNH mã không? @mickmackusa Bạn có thể kiểm tra lại được không?
PYK

2
$string = 'this is my world, not my world';
$find = 'world';
$replace = 'farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;

Đây chỉ là câu trả lời đầu tiên. Bên cạnh đó, bạn nên làm một preg_quotesố $findtrước khi sử dụng nó như là một biểu thức.
Emil Vikström

đây là những gì tôi đã sử dụng, vì vậy tôi đã bình chọn nó. Câu trả lời đầu tiên đã gây ra một cuộc xung đột với Drupal, nó hẳn đã ghi đè lên chức năng của người trợ giúp drupal. Vì vậy, tôi chỉ lấy mã bên trong hàm và sử dụng nó phù hợp với phần còn lại của mã ...
Dan Mantyla

Câu trả lời chỉ có mã này cung cấp lời khuyên dư thừa trên trang (không đề cập đến nó còn thiếu preg_quote(). Câu trả lời trùng lặp muộn này có thể được xóa khỏi trang vì lời khuyên của nó được cung cấp bởi câu trả lời được chấp nhận trước đó và cao hơn được chấp nhận.
mickmackusa

2

Để mở rộng câu trả lời của @ renocor , tôi đã viết một hàm tương thích ngược 100% với str_replace(). Nghĩa là, bạn có thể thay thế tất cả các lần xuất hiện của str_replace()str_replace_limit()mà không rối tung bất cứ điều gì, ngay cả những người sử dụng mảng cho $search, $replacevà / hoặc $subject.

Hàm này có thể hoàn toàn khép kín, nếu bạn muốn thay thế lệnh gọi hàm ($string===strval(intval(strval($string)))), nhưng tôi khuyên bạn nên chống lại nó vì đây valid_integer()là một hàm khá hữu ích khi xử lý các số nguyên được cung cấp dưới dạng chuỗi.

Lưu ý: Bất cứ khi nào có thể, str_replace_limit()sẽ sử dụng str_replace()thay thế, vì vậy tất cả các cuộc gọi str_replace()có thể được thay thế str_replace_limit()mà không phải lo lắng về hiệu suất.

Sử dụng

<?php
$search = 'a';
$replace = 'b';
$subject = 'abcabc';
$limit = -1; // No limit
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

2 sự thay thế - bbcbbc

$limit = 1; // Limit of 1
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

1 thay thế - bbcabc

$limit = 10; // Limit of 10
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

2 sự thay thế - bbcbbc

Chức năng

<?php

/**
 * Checks if $string is a valid integer. Integers provided as strings (e.g. '2' vs 2)
 * are also supported.
 * @param mixed $string
 * @return bool Returns boolean TRUE if string is a valid integer, or FALSE if it is not 
 */
function valid_integer($string){
    // 1. Cast as string (in case integer is provided)
    // 1. Convert the string to an integer and back to a string
    // 2. Check if identical (note: 'identical', NOT just 'equal')
    // Note: TRUE, FALSE, and NULL $string values all return FALSE
    $string = strval($string);
    return ($string===strval(intval($string)));
}

/**
 * Replace $limit occurences of the search string with the replacement string
 * @param mixed $search The value being searched for, otherwise known as the needle. An
 * array may be used to designate multiple needles.
 * @param mixed $replace The replacement value that replaces found search values. An
 * array may be used to designate multiple replacements.
 * @param mixed $subject The string or array being searched and replaced on, otherwise
 * known as the haystack. If subject is an array, then the search and replace is
 * performed with every entry of subject, and the return value is an array as well. 
 * @param string $count If passed, this will be set to the number of replacements
 * performed.
 * @param int $limit The maximum possible replacements for each pattern in each subject
 * string. Defaults to -1 (no limit).
 * @return string This function returns a string with the replaced values.
 */
function str_replace_limit(
        $search,
        $replace,
        $subject,
        &$count,
        $limit = -1
    ){

    // Set some defaults
    $count = 0;

    // Invalid $limit provided. Throw a warning.
    if(!valid_integer($limit)){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting an '.
                'integer', E_USER_WARNING);
        return $subject;
    }

    // Invalid $limit provided. Throw a warning.
    if($limit<-1){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_WARNING);
        return $subject;
    }

    // No replacements necessary. Throw a notice as this was most likely not the intended
    // use. And, if it was (e.g. part of a loop, setting $limit dynamically), it can be
    // worked around by simply checking to see if $limit===0, and if it does, skip the
    // function call (and set $count to 0, if applicable).
    if($limit===0){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_NOTICE);
        return $subject;
    }

    // Use str_replace() whenever possible (for performance reasons)
    if($limit===-1){
        return str_replace($search, $replace, $subject, $count);
    }

    if(is_array($subject)){

        // Loop through $subject values and call this function for each one.
        foreach($subject as $key => $this_subject){

            // Skip values that are arrays (to match str_replace()).
            if(!is_array($this_subject)){

                // Call this function again for
                $this_function = __FUNCTION__;
                $subject[$key] = $this_function(
                        $search,
                        $replace,
                        $this_subject,
                        $this_count,
                        $limit
                );

                // Adjust $count
                $count += $this_count;

                // Adjust $limit, if not -1
                if($limit!=-1){
                    $limit -= $this_count;
                }

                // Reached $limit, return $subject
                if($limit===0){
                    return $subject;
                }

            }

        }

        return $subject;

    } elseif(is_array($search)){
        // Only treat $replace as an array if $search is also an array (to match str_replace())

        // Clear keys of $search (to match str_replace()).
        $search = array_values($search);

        // Clear keys of $replace, if applicable (to match str_replace()).
        if(is_array($replace)){
            $replace = array_values($replace);
        }

        // Loop through $search array.
        foreach($search as $key => $this_search){

            // Don't support multi-dimensional arrays (to match str_replace()).
            $this_search = strval($this_search);

            // If $replace is an array, use the value of $replace[$key] as the replacement. If
            // $replace[$key] doesn't exist, just an empty string (to match str_replace()).
            if(is_array($replace)){
                if(array_key_exists($key, $replace)){
                    $this_replace = strval($replace[$key]);
                } else {
                    $this_replace = '';
                }
            } else {
                $this_replace = strval($replace);
            }

            // Call this function again for
            $this_function = __FUNCTION__;
            $subject = $this_function(
                    $this_search,
                    $this_replace,
                    $subject,
                    $this_count,
                    $limit
            );

            // Adjust $count
            $count += $this_count;

            // Adjust $limit, if not -1
            if($limit!=-1){
                $limit -= $this_count;
            }

            // Reached $limit, return $subject
            if($limit===0){
                return $subject;
            }

        }

        return $subject;

    } else {
        $search = strval($search);
        $replace = strval($replace);

        // Get position of first $search
        $pos = strpos($subject, $search);

        // Return $subject if $search cannot be found
        if($pos===false){
            return $subject;
        }

        // Get length of $search, to make proper replacement later on
        $search_len = strlen($search);

        // Loop until $search can no longer be found, or $limit is reached
        for($i=0;(($i<$limit)||($limit===-1));$i++){

            // Replace 
            $subject = substr_replace($subject, $replace, $pos, $search_len);

            // Increase $count
            $count++;

            // Get location of next $search
            $pos = strpos($subject, $search);

            // Break out of loop if $needle
            if($pos===false){
                break;
            }

        }

        // Return new $subject
        return $subject;

    }

}

4
hơi khó chịu nếu bạn hỏi tôi Ngoài ra, điều tôi 'ghét' nhất ở giải pháp này là việc xử lý lỗi. Nó phá vỡ tập lệnh nếu bạn vượt qua các giá trị không chính xác. Bạn nghĩ rằng nó trông chuyên nghiệp nhưng thực tế không phải vậy, thay vì một lỗi tạo ra một thông báo hoặc cảnh báo thay thế. Tốt hơn là bỏ qua những chuyện nhảm nhí, thay vào đó là false hoặc null và không bao giờ sử dụng backtrace trong chức năng như thế này. Giải pháp tốt nhất là lập trình viên có thể quyết định phải làm gì khi đầu ra sai / không mong muốn.
Codebeat

@Erwinus Điều này sử dụng E_USER_WARNINGxuyên suốt, đó là một cảnh báo , không phải là một lỗi . Backtrace cực kỳ hữu ích để tìm ra mã nào đang truyền dữ liệu không hợp lệ cho hàm ở vị trí đầu tiên (điều này hoàn toàn cần thiết để theo dõi các lỗi trong sản xuất). Đối với việc trả lại $subjectthay vì false/ nullhoặc ném lỗi, đó đơn giản là một lựa chọn cá nhân cho trường hợp sử dụng của tôi. Để phù hợp với str_replace()chức năng của nó, sử dụng các lỗi nghiêm trọng có thể bắt được sẽ là cách tốt nhất (cũng như str_replace()khi cung cấp một bao đóng cho hai đối số đầu tiên).
0b10011

À, không để ý về E_USER_WARNING mà bạn đang sử dụng, xin lỗi vì điều đó. Vấn đề với việc trả lại chủ đề là bạn không bao giờ có thể thấy có gì đó không đúng, bên ngoài chức năng. Điều đó nói rằng, chức năng có thể là một nửa kích thước nếu bạn làm điều đó thông minh hơn (có thể). Thứ hai, ý kiến ​​là tốt khi nó giải thích một cái gì đó phức tạp nhưng không hữu ích cho những điều đơn giản như tăng giá trị. Nói chung tôi nghĩ rằng nó là không cần thiết rất lớn. Ngoài ra, sử dụng cảnh báo trong môi trường sản xuất có thể là vấn đề bảo mật khi bạn sử dụng mã này trên máy chủ không chặn các thông báo thời gian chạy theo mặc định (nhật ký).
Codebeat

@Erwinus Tôi đã tiết lộ khi nhận xét vì một số người không hiểu ngôn ngữ cũng như những người khác và những bình luận luôn có thể bị xóa bởi những người hiểu nó. Nếu bạn biết một cách tốt hơn để có được kết quả cuối cùng giống nhau cho tất cả các trường hợp cạnh, bằng mọi cách, hãy chỉnh sửa câu trả lời. Và nếu môi trường sản xuất của bạn không ngăn chặn các thông báo lỗi, bạn đã gặp sự cố lớn hơn chức năng này;)
0b10011 17/07/13

TL; DR Đoạn trích này quá lớn đến nỗi tôi không thể tưởng tượng được việc chọn nó qua chức năng regex (tôi ghét cuộn). Nếu bạn muốn đếm số lần thay thế được thực hiện, có một tham số cho điều đó preg_replace(). Hơn nữa, preg_replace()/ regex cung cấp xử lý ranh giới từ (nếu muốn) - thứ mà các hàm không phải regex sẽ không cung cấp một cách thanh lịch.
mickmackusa

2

Theo kết quả thử nghiệm của tôi, tôi muốn bỏ phiếu cho thường xuyên được cung cấp bởi karim79. (Tôi không có đủ danh tiếng để bỏ phiếu ngay bây giờ!)

Giải pháp từ zombat sử dụng quá nhiều lệnh gọi hàm, tôi thậm chí còn đơn giản hóa các mã. Tôi đang sử dụng PHP 5.4 để chạy cả hai giải pháp trong 100.000 lần và đây là kết quả:

$str = 'Hello abc, have a nice day abc! abc!';
$pos = strpos($str, 'abc');
$str = substr_replace($str, '123', $pos, 3);

==> 1,85 giây

$str = 'Hello abc, have a nice day abc! abc!';
$str = preg_replace('/abc/', '123', $str, 1);

==> 1,35 giây

Bạn có thể thấy. Hiệu suất của preg numplace không quá tệ như nhiều người nghĩ. Vì vậy, tôi muốn đề xuất giải pháp đẳng cấp nếu biểu thức thông thường của bạn không phức tạp.


Đoạn mã đầu tiên của bạn là một so sánh không công bằng vì không sử dụng đúng cách thực hiện. You are not kiểm tra $poschofalse , vì vậy khi kim không tồn tại trong đống cỏ khô, nó sẽ làm hỏng đầu ra.
mickmackusa

Cảm ơn @mickmackusa, bạn đã đúng. Nhưng đó không phải là vấn đề. Tôi đã nói mã này được đơn giản hóa chỉ để so sánh hiệu quả của việc triển khai.
Thợ săn Wu

Đây đúng là ý của tôi! Bạn không bao giờ được thực hiện so sánh điểm chuẩn không thực hiện cùng một quy trình. So sánh táo với nửa quả cam là không hữu ích. Thực hiện đầy đủ cách tiếp cận phi regex hoàn chỉnh sẽ làm cho sự khác biệt tốc độ sâu sắc hơn.
mickmackusa

Vâng, cảm ơn một lần nữa. Nhưng điều tôi muốn là tìm ra cách thực hiện tốt hơn, không tạo ra sự khác biệt sâu sắc hơn.
Thợ săn Wu

2

Để mở rộng câu trả lời của zombat (mà tôi tin là câu trả lời hay nhất), tôi đã tạo một phiên bản đệ quy của hàm anh ta có một $limittham số để chỉ định số lần xuất hiện bạn muốn thay thế.

function str_replace_limit($haystack, $needle, $replace, $limit, $start_pos = 0) {
    if ($limit <= 0) {
        return $haystack;
    } else {
        $pos = strpos($haystack,$needle,$start_pos);
        if ($pos !== false) {
            $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
            return str_replace_limit($newstring, $needle, $replace, $limit-1, $pos+strlen($replace));
        } else {
            return $haystack;
        }
    }
}

Lưu ý, không có kiểm tra chất lượng $start_pos, vì vậy nếu vượt quá độ dài chuỗi, chức năng này sẽ tạo ra:Warning: strpos(): Offset not contained in string... . Chức năng này không thực hiện thay thế khi $start_posvượt quá độ dài. Bằng chứng về sự thất bại: 3v4l.org/qGuVIR ... Chức năng của bạn có thể kết hợp các return $haystackđiều kiện và tránh khai báo các biến sử dụng một lần như thế này: 3v4l.org/Kdmqp Tuy nhiên, như tôi đã nói trong các nhận xét ở đâu đó trên trang này, tôi muốn nói sử dụng một preg_replace()cuộc gọi rất sạch sẽ, trực tiếp, không đệ quy .
mickmackusa

vâng để bạn có thể thêm dòng này elseStatment$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

2

Cho một chuỗi

$string = 'OOO.OOO.OOO.S';
$search = 'OOO';
$replace = 'B';

//replace ONLY FIRST occurance of "OOO" with "B"
    $string = substr_replace($string,$replace,0,strlen($search));
    //$string => B.OOO.OOO.S

//replace ONLY LAST occurance of "OOOO" with "B"
    $string = substr_replace($string,$replace,strrpos($string,$search),strlen($search)) 
    //$string => OOO.OOO.B.S

    //replace ONLY LAST occurance of "OOOO" with "B"
    $string = strrev(implode(strrev($replace),explode(strrev($search),strrev($string),2)))
    //$string => OOO.OOO.B.S

Cho một nhân vật

$string[strpos($string,$search)] = $replace;


//EXAMPLE

$string = 'O.O.O.O.S';
$search = 'O';
$replace = 'B';

//replace ONLY FIRST occurance of "O" with "B" 
    $string[strpos($string,$search)] = $replace;  
    //$string => B.O.O.O.S

//replace ONLY LAST occurance of "O" with "B" 
    $string[strrpos($string,$search)] = $replace; 
    // $string => B.O.O.B.S

Đoạn mã nền đầu tiên () đầu tiên không thành công khi chuỗi tìm kiếm không ở độ lệch 0 của chuỗi đầu vào. Bằng chứng về sự thất bại: 3v4l.org/oIbRv Và cả hai substr_replace()kỹ thuật này đều làm hỏng chuỗi đầu vào khi không có giá trị tìm kiếm. Bằng chứng về sự thất bại: 3v4l.org/HmEml (Và kỹ thuật cuối cùng với tất cả các revcuộc gọi bị ảnh hưởng nghiêm trọng / khó khăn trên mắt.)
mickmackusa

2

Bổ sung cho những gì mọi người nói, hãy nhớ rằng toàn bộ chuỗi là một mảng:

$string = "Lorem ipsum lá lá lá";

$string[0] = "B";

echo $string;

"Borem ipsum lá lá"


3
Trừ khi nó chứa các ký tự đa nhân ... và sau đó kỹ thuật của bạn thất bại. Thật không may khi bạn cung cấp một chuỗi đầu vào mẫu có chứa á. Trình diễn thất bại
mickmackusa

Bạn có thể xác minh xem stringchuỗi của bạn có phải là chuỗi đa bào hay khôngmb_strlen($subject) != strlen($subject)
RousseauAlexandre

Bài đăng này không cố gắng trả lời câu hỏi đang được hỏi.
mickmackusa

2
$str = "/property/details&id=202&test=123#tab-6p";
$position = strpos($str,"&");
echo substr_replace($str,"?",$position,1);

Sử dụng lớp nền, chúng ta có thể thay thế sự xuất hiện của ký tự đầu tiên chỉ trong chuỗi. như & được lặp lại nhiều lần nhưng chỉ ở vị trí đầu tiên chúng ta phải thay thế & bằng?


1

Chức năng này được lấy cảm hứng rất nhiều từ câu trả lời của @renocor. Nó làm cho chức năng đa byte an toàn.

function str_replace_limit($search, $replace, $string, $limit)
{
    $i = 0;
    $searchLength = mb_strlen($search);

    while(($pos = mb_strpos($string, $search)) !== false && $i < $limit)
    {
        $string = mb_substr_replace($string, $replace, $pos, $searchLength);
        $i += 1;
    }

    return $string;
}

function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
{
    $string = (array)$string;
    $encoding = is_null($encoding) ? mb_internal_encoding() : $encoding;
    $length = is_null($length) ? mb_strlen($string) - $start : $length;

    $string = array_map(function($str) use ($replacement, $start, $length, $encoding){

        $begin = mb_substr($str, 0, $start, $encoding);
        $end = mb_substr($str, ($start + $length), mb_strlen($str), $encoding);

        return $begin . $replacement . $end;

    }, $string);

    return ( count($string) === 1 ) ? $string[0] : $string;
}

0

Bạn có thể sử dụng điều này:

function str_replace_once($str_pattern, $str_replacement, $string){ 

        if (strpos($string, $str_pattern) !== false){ 
            $occurrence = strpos($string, $str_pattern); 
            return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern)); 
        } 

        return $string; 
    } 

Tìm thấy ví dụ này từ php.net

Sử dụng:

$string = "Thiz iz an examplz";
var_dump(str_replace_once('z','Z', $string)); 

Đầu ra:

ThiZ iz an examplz

Điều này có thể làm giảm hiệu suất một chút, nhưng giải pháp dễ nhất.


Nếu đó là đầu ra hơn điểm là gì? Không phải nó chỉ nên thay thế chữ "z" đầu tiên bằng chữ "Z" sao? Thay vì thay thế tất cả chúng? Tôi nghĩ đó là những gì chúng ta đã nói ở đây ...
Xoay

Xấu của tôi, nó sẽ chỉ thay thế lần đầu tiên Đã chỉnh sửa.
happyhardik

Lời khuyên tương tự này đã được Bas đưa ra gần 3 năm trước (và không cần gọi quá nhiều strpos()). Bị bỏ qua vì nó không thêm bất kỳ giá trị mới nào vào trang.
mickmackusa

0

Nếu chuỗi của bạn không chứa bất kỳ ký tự đa dòng nào và nếu bạn muốn thay thế chỉ một ký tự, bạn chỉ cần sử dụng strpos

Đây là một chức năng xử lý lỗi

/**
 * Replace the first occurence of given string
 *
 * @param  string $search  a char to search in `$subject`
 * @param  string $replace a char to replace in `$subject`
 * @param  string $subject
 * @return string
 *
 * @throws InvalidArgumentException if `$search` or `$replace` are invalid or if `$subject` is a multibytes string
 */
function str_replace_first(string $search , string $replace , string $subject) : string {
    // check params
    if(strlen($replace) != 1 || strlen($search) != 1) {
        throw new InvalidArgumentException('$search & $replace must be char');
    }elseif(mb_strlen($subject) != strlen($subject)){
        throw new InvalidArgumentException('$subject is an multibytes string');
    }
    // search 
    $pos = strpos($subject, $search);
    if($pos === false) {
        // not found
        return $subject;
    }

    // replace
    $subject[$replace] = $subject;

    return $subject;
}

0

Đối với giải pháp vòng lặp

<?php
echo replaceFirstMatchedChar("&", "?", "/property/details&id=202&test=123#tab-6");

function replaceFirstMatchedChar($searchChar, $replaceChar, $str)
{
    for ($i = 0; $i < strlen($str); $i++) {

        if ($str[$i] == $searchChar) {
            $str[$i] = $replaceChar;
            break;
        }
    }
    return $str;
}

-1

Đây là một lớp đơn giản mà tôi đã tạo để bao bọc các hàm str numplace () được sửa đổi một chút của chúng tôi .

Hàm php :: str_rreplace () của chúng tôi cũng cho phép bạn thực hiện một str numplace () bị giới hạn, có thể rất tiện lợi khi cố gắng chỉ thay thế (các) thể hiện X cuối cùng của chuỗi.

Cả hai ví dụ này đều sử dụng preg numplace () .

<?php
class php {

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_replace($find, $replace, $subject, $replacement_limit = -1) {
        $find_pattern = str_replace('/', '\/', $find);
        return preg_replace('/' . $find_pattern . '/', $replace, $subject, $replacement_limit);
    }

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_rreplace($find, $replace, $subject, $replacement_limit = -1) {
        return strrev( self::str_replace(strrev($find), strrev($replace), strrev($subject), $replacement_limit) );
    }
}

Bài viết của bạn không thêm giá trị cho trang này đã qua trang bão hòa. Giải pháp regex của bạn thất bại trong nhiều trường hợp rìa vì bạn đã sử dụng công cụ không chính xác để thoát các ký tự trong chuỗi kim. Bằng chứng về sự thất bại: 3v4l.org/dTdYK Câu trả lời được đánh giá cao và được chấp nhận từ năm 2009 đã cho thấy việc thực hiện đúng kỹ thuật này. Phương pháp thứ hai của bạn không trả lời câu hỏi được hỏi và đã được cung cấp bởi oLinkWebDevelopment.
mickmackusa

-1
$str = "Hello there folks!"
$str_ex = explode("there, $str, 2);   //explodes $string just twice
                                      //outputs: array ("Hello ", " folks")
$str_final = implode("", $str_ex);    // glues above array together
                                      // outputs: str("Hello  folks")

Có thêm một không gian nữa nhưng nó không thành vấn đề vì nó là kịch bản backgound trong trường hợp của tôi.


Kỹ thuật này được cung cấp bởi toomuchphp vào năm 2009 ! Tôi đã đánh giá thấp vì bài viết của bạn không thêm giá trị mới cho các nhà nghiên cứu. Vui lòng đảm bảo, trước khi đăng câu trả lời, giải pháp của bạn là duy nhất cho trang và thêm giá trị cho trang.
mickmackusa

-3

Đây là câu trả lời đầu tiên của tôi ở đây, tôi hy vọng sẽ làm điều đó một cách chính xác. Tại sao không sử dụng đối số thứ tư của hàm str numplace cho vấn đề này?

mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

đếm: Nếu được thông qua, điều này sẽ được đặt thành số lần thay thế được thực hiện.


chỉnh sửa: Câu trả lời này là sai, bởi vì tham số thứ 4 của str numplace là một biến được chỉ định số lần thay thế được thực hiện. Điều này không phù hợp với preg numplace , có tham số thứ 4 $limitvà tham số thứ 5 &$count.


Các đối số thứ tư sẽ được đặt bởi str numplace () thành số lần thay thế đã được thực hiện. Đó là lý do tại sao bạn gặp lỗi khi bạn truyền một số nguyên và không phải là một biến cho nó.
arminrosu

-6

Thật dễ dàng để tìm một giải pháp để chỉ thay thế một vài trường hợp đầu tiên hoặc đầu tiên (bằng cách đưa ra giá trị đếm). Không có nhiều giải pháp để thay thế ví dụ cuối cùng hoặc cuối cùng.

Có lẽ một cái gì đó như str numplace ($ find, $ thay thế, $ topic, -3) sẽ thay thế ba trường hợp cuối cùng.

Dù sao, chỉ là một gợi ý.


4
Tại sao trả lời một câu hỏi với một gợi ý khi một câu trả lời đã được chấp nhận hai năm trước?!
mbinette

Ngoài ra -3 sẽ không hoạt động như tham số, bởi vì tham số thứ 4 là đầu ra và không phải là tham số đầu vào. Sẽ tốt hơn nếu bạn kiểm tra những gì bạn đề xuất, thay vì đăng mã bị lỗi.
Ghostwriter78

Vâng, điều này là sai, tuy nhiên, tại sao tôi gặp sự cố màn hình trống, khi tôi thử nó? Tôi đã thực hiện các lỗi thông thường (E_ALL); ini_set ("display_errors", 1); Vẫn còn lỗi màn hình trống.
Doug Cassidy
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.