Làm thế nào để thay thế tất cả các lần xuất hiện của một ký tự trong chuỗi?


480

Cách hiệu quả để thay thế tất cả các lần xuất hiện của một nhân vật bằng một nhân vật khác là std::stringgì?

Câu trả lời:


742

std::stringkhông chứa chức năng như vậy nhưng bạn có thể sử dụng replacechức năng độc lập từ algorithmtiêu đề.

#include <algorithm>
#include <string>

void some_func() {
  std::string s = "example string";
  std::replace( s.begin(), s.end(), 'x', 'y'); // replace all 'x' to 'y'
}

6
std::stringmột container được thiết kế đặc biệt để hoạt động với các chuỗi ký tự. liên kết
Kirill V. Lyadvinsky

164
Thật không may, điều này cho phép chỉ thay thế một char bằng một char khác. Nó không thể thay thế một char bằng nhiều ký tự hơn (nghĩa là bằng một chuỗi). Có cách nào để thực hiện tìm kiếm - thay thế bằng nhiều ký tự hơn không?
SasQ

6
@Kirill V. Lyadvinsky Điều gì xảy ra nếu tôi chỉ muốn loại bỏ một sự cố.
CUỘC SỐNG

4
@ KirillV.Lyadvinsky: Khi tôi sử dụng phương pháp này để thay thế tất cả các x bằng y, kết quả là một chuỗi y dài bất kể chuỗi gốc là gì. Tôi tò mò bạn nghĩ vấn đề sẽ là gì. (mã hoàn toàn giống như bạn đã viết)
Siêu việt

6
@Transcendent: Đây chính xác là những gì xảy ra với std::string::replace()thay vì std::replace()! 'x' ( char) được truyền ngầm thành size_t[giá trị 120], do đó toàn bộ chuỗi hoặc một phần của chuỗi sẽ được lấp đầy với 120 bản sao của 'y'.
IBue

127

Tôi nghĩ tôi cũng sẽ sử dụng giải pháp tăng cường :

#include <boost/algorithm/string/replace.hpp>

// in place
std::string in_place = "blah#blah";
boost::replace_all(in_place, "#", "@");

// copy
const std::string input = "blah#blah";
std::string output = boost::replace_all_copy(input, "#", "@");

Sau đó, bạn đang thiếu một vài -Icờ cho trình biên dịch của mình để nó tìm thấy các thư viện Boost trên hệ thống của bạn. Có lẽ bạn cần phải cài đặt nó trước.
Martin Uting

Cách trên hiệu quả hơn vì nó xuất hiện với std lib. Không phải tất cả đều sử dụng thư viện boost ;-)
hfrmobile

122

Câu hỏi tập trung vào characterthay thế, nhưng, vì tôi thấy trang này rất hữu ích (đặc biệt là nhận xét của Konrad ), tôi muốn chia sẻ cách triển khai tổng quát hơn này, cho phép giải quyết substringstốt:

std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
    }
    return str;
}

Sử dụng:

std::cout << ReplaceAll(string("Number Of Beans"), std::string(" "), std::string("_")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("X")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("h")) << std::endl;

Đầu ra:

Số_Of_Beans

XXjXugtXty

hhjhugthty


BIÊN TẬP:

Những điều trên có thể được thực hiện theo cách phù hợp hơn, trong trường hợp các màn trình diễn là mối quan tâm của bạn, bằng cách trả về không có gì ( void) và thực hiện các thay đổi trực tiếp trên chuỗi strđược cung cấp dưới dạng đối số, được truyền theo địa chỉ thay vì theo giá trị . Điều này sẽ tránh việc sao chép chuỗi gốc vô ích và tốn kém, trong khi trả về kết quả. Cuộc gọi của bạn, sau đó ...

Mã số:

static inline void ReplaceAll2(std::string &str, const std::string& from, const std::string& to)
{
    // Same inner code...
    // No return statement
}

Hy vọng điều này sẽ hữu ích cho một số người khác ...


4
Điều này có một vấn đề hiệu năng trong trường hợp chuỗi nguồn lớn và có nhiều lần xuất hiện của chuỗi được thay thế. chuỗi :: thay thế () sẽ được gọi nhiều lần gây ra nhiều bản sao chuỗi. Xem giải pháp của tôi mà giải quyết vấn đề đó.
minastaros

1
Nit chọn trước: theo địa chỉ => bằng cách tham khảo . Cho dù đó là một địa chỉ hay không là một chi tiết thực hiện.
Max Truxa

1
Bạn thực sự nên kiểm tra xem fromchuỗi có trống không, nếu không một vòng lặp vô tận sẽ xảy ra.
newbie

34

Hãy tưởng tượng một blob nhị phân lớn trong đó tất cả 0x00 byte sẽ được thay thế bằng "\ 1 \ x30" và tất cả 0x01 byte bằng "\ 1 \ x31" vì giao thức truyền tải không cho phép \ 0 byte.

Trong trường hợp:

  • chuỗi thay thế và chuỗi thay thế có độ dài khác nhau,
  • có nhiều lần xuất hiện của chuỗi thay thế trong chuỗi nguồn và
  • chuỗi nguồn lớn

các giải pháp được cung cấp không thể được áp dụng (vì chúng chỉ thay thế các ký tự đơn) hoặc có vấn đề về hiệu năng, vì chúng sẽ gọi chuỗi :: thay thế nhiều lần để tạo ra các bản sao kích thước của blob nhiều lần. (Tôi không biết giải pháp tăng cường, có thể nó ổn từ quan điểm đó)

Điều này đi dọc theo tất cả các lần xuất hiện trong chuỗi nguồn và xây dựng từng chuỗi mới một lần :

void replaceAll(std::string& source, const std::string& from, const std::string& to)
{
    std::string newString;
    newString.reserve(source.length());  // avoids a few memory allocations

    std::string::size_type lastPos = 0;
    std::string::size_type findPos;

    while(std::string::npos != (findPos = source.find(from, lastPos)))
    {
        newString.append(source, lastPos, findPos - lastPos);
        newString += to;
        lastPos = findPos + from.length();
    }

    // Care for the rest after last occurrence
    newString += source.substr(lastPos);

    source.swap(newString);
}

Đây là giải pháp tốt nhất ở đây được xây dựng trên STL. Nếu bạn sẽ sử dụng chức năng tùy chỉnh để dễ dàng sử dụng ở bất cứ đâu, hãy đặt chức năng này.
Roger Sanders

21

Một tìm kiếm đơn giản và thay thế cho một nhân vật sẽ đi một cái gì đó như:

s.replace(s.find("x"), 1, "y")

Để làm điều này cho toàn bộ chuỗi, điều dễ dàng sẽ là lặp cho đến khi bạn s.findbắt đầu quay lại npos. Tôi cho rằng bạn cũng có thể bắt range_errorđể thoát khỏi vòng lặp, nhưng điều đó hơi xấu.


7
Mặc dù đây có lẽ là một giải pháp phù hợp khi số lượng ký tự cần thay thế nhỏ so với độ dài của chuỗi, nhưng nó không có tỷ lệ tốt. Khi tỷ lệ các ký tự trong chuỗi gốc cần được thay thế tăng lên, phương thức này sẽ tiếp cận O (N ^ 2) kịp thời.
andand

7
Thật. Triết lý chung của tôi là làm điều dễ dàng (viết và đọc) cho đến khi sự thiếu hiệu quả đang gây ra vấn đề thực sự. Có một số trường hợp bạn có thể có các chuỗi hài hước trong đó O (N ** 2) quan trọng, nhưng 99% thời gian các chuỗi của tôi là 1K hoặc ít hơn.
TED

3
... Điều đó đã được nói, tôi thích phương pháp của Kirill hơn (và đã bình chọn nó).
TED

Điều gì xảy ra nếu không tìm thấy "x"? Ngoài ra, tại sao bạn đang sử dụng niềng răng đôi?
Prasath Govind

@PrasathGovind - Tôi chỉ hiển thị các cuộc gọi cần thiết (do đó "đại loại như"). Các chi tiết quan trọng nhưng che khuất như xử lý lỗi thích hợp được để lại như một bài tập cho người đọc. Đối với "niềng răng đôi", tôi không chắc đó là gì, hoặc bạn đang nói về cái gì. Đối với tôi một "cú đúp" là {nhân vật. Tôi không biết "cú đúp" là gì. Có lẽ bạn có một số loại vấn đề phông chữ?
TED

6

Nếu bạn đang tìm cách thay thế nhiều hơn một ký tự và chỉ xử lý std::string, thì đoạn mã này sẽ hoạt động, thay thế sNeedle trong sHaystack bằng sReplace và sNeedle và sReplace không cần phải có cùng kích thước. Thường trình này sử dụng vòng lặp while để thay thế tất cả các lần xuất hiện, thay vì chỉ lần đầu tiên được tìm thấy từ trái sang phải.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}

Đây là O (n ^). Bạn có thể làm điều đó trong thời gian O (n).
Changming CN

3
@ChangmingSun có nghĩa là giải pháp O (n) nào?
habakuk

2
Đây sẽ là vòng lặp vô hạn nếu kNeedle là một chuỗi con của sReplace.
niềm tự hào

Thêm vào đó là một findcuộc gọi hai lần. Xem xét làm cho kết quả đó là một biến tạm thời.
Luc Bloom

4

Như Kirill đã đề xuất, hãy sử dụng phương thức thay thế hoặc lặp lại dọc theo chuỗi thay thế từng char một cách độc lập.

Ngoài ra, bạn có thể sử dụng findphương pháp hoặc find_first_oftùy thuộc vào những gì bạn cần làm. Không có giải pháp nào trong số này sẽ thực hiện công việc trong một lần, nhưng với một vài dòng mã bổ sung bạn phải làm cho chúng hoạt động cho bạn. :-)


3
#include <iostream>
#include <string>
using namespace std;
// Replace function..
string replace(string word, string target, string replacement){
    int len, loop=0;
    string nword="", let;
    len=word.length();
    len--;
    while(loop<=len){
        let=word.substr(loop, 1);
        if(let==target){
            nword=nword+replacement;
        }else{
            nword=nword+let;
        }
        loop++;
    }
    return nword;

}
//Main..
int main() {
  string word;
  cout<<"Enter Word: ";
  cin>>word;
  cout<<replace(word, "x", "y")<<endl;
  return 0;
}

Nếu worddài, có thể có rất nhiều chi phí trong khi gọi hàm. Bạn có thể tối ưu hóa điều này bằng cách đi qua word, targetreplacementnhư const-tài liệu tham khảo.
TrebledJ

2

Thế còn Abseil StrReplace ALL ? Từ tệp tiêu đề:

// This file defines `absl::StrReplaceAll()`, a general-purpose string
// replacement function designed for large, arbitrary text substitutions,
// especially on strings which you are receiving from some other system for
// further processing (e.g. processing regular expressions, escaping HTML
// entities, etc.). `StrReplaceAll` is designed to be efficient even when only
// one substitution is being performed, or when substitution is rare.
//
// If the string being modified is known at compile-time, and the substitutions
// vary, `absl::Substitute()` may be a better choice.
//
// Example:
//
// std::string html_escaped = absl::StrReplaceAll(user_input, {
//                                                {"&", "&amp;"},
//                                                {"<", "&lt;"},
//                                                {">", "&gt;"},
//                                                {"\"", "&quot;"},
//                                                {"'", "&#39;"}});

1

Trường cũ :-)

std::string str = "H:/recursos/audio/youtube/libre/falta/"; 

for (int i = 0; i < str.size(); i++) {
    if (str[i] == '/') {
        str[i] = '\\';
    }
}

std::cout << str;

Kết quả:

H: \ recursos \ audio \ youtube \ libre \ falta \


0

Những công việc này! Tôi đã sử dụng một cái gì đó tương tự như thế này cho một ứng dụng hiệu sách, nơi kho lưu trữ được lưu trữ trong CSV (như tệp .dat). Nhưng trong trường hợp của một char duy nhất, có nghĩa là trình thay thế chỉ là một char duy nhất, ví dụ '|', nó phải nằm trong dấu ngoặc kép "|" để không ném một chuyển đổi không hợp lệ const char.

#include <iostream>
#include <string>

using namespace std;

int main()
{
    int count = 0;  // for the number of occurences.
    // final hold variable of corrected word up to the npos=j
    string holdWord = "";
    // a temp var in order to replace 0 to new npos
    string holdTemp = "";
    // a csv for a an entry in a book store
    string holdLetter = "Big Java 7th Ed,Horstman,978-1118431115,99.85";

    // j = npos
    for (int j = 0; j < holdLetter.length(); j++) {

        if (holdLetter[j] == ',') {

            if ( count == 0 ) 
            {           
                holdWord = holdLetter.replace(j, 1, " | ");      
            }
            else {

                string holdTemp1 = holdLetter.replace(j, 1, " | ");

                // since replacement is three positions in length,
                // must replace new replacement's 0 to npos-3, with
                // the 0 to npos - 3 of the old replacement 
                holdTemp = holdTemp1.replace(0, j-3, holdWord, 0, j-3); 

                holdWord = "";

                holdWord = holdTemp;

            }
            holdTemp = "";
            count++;
        }
    } 
    cout << holdWord << endl;
    return 0;
}

// result:
Big Java 7th Ed | Horstman | 978-1118431115 | 99.85

Hiện tại tôi đang sử dụng CentOS, vì vậy phiên bản trình biên dịch của tôi ở bên dưới. Phiên bản C ++ (g ++), C ++ 98 mặc định:

g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

0

Nếu bạn sẵn sàng sử dụng std::strings, bạn có thể sử dụng strsubchức năng của ứng dụng mẫu này hoặc cập nhật nó nếu bạn muốn nó lấy một loại hoặc bộ tham số khác để đạt được cùng một mục tiêu. Về cơ bản, nó sử dụng các thuộc tính và chức năng của std::stringđể nhanh chóng xóa bộ ký tự trùng khớp và chèn các ký tự mong muốn trực tiếp vào trong std::string. Mỗi lần thực hiện thao tác thay thế này, phần bù sẽ cập nhật nếu nó vẫn có thể tìm thấy các ký tự trùng khớp để thay thế và nếu không thể thay thế thêm, nó sẽ trả về chuỗi ở trạng thái từ bản cập nhật cuối cùng.

#include <iostream>
#include <string>

std::string strsub(std::string stringToModify,
                   std::string charsToReplace,
                   std::string replacementChars);

int main()
{
    std::string silly_typos = "annoiiyyyng syyyllii tiipos.";

    std::cout << "Look at these " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos, "yyy", "i");
    std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos, "ii", "y");

    std::cout << "There, no more " << silly_typos << std::endl;
    return 0;
}

std::string strsub(std::string stringToModify,
                   std::string charsToReplace,
                   std::string replacementChars)
{
    std::string this_string = stringToModify;

    std::size_t this_occurrence = this_string.find(charsToReplace);
    while (this_occurrence != std::string::npos)
    {
        this_string.erase(this_occurrence, charsToReplace.size());
        this_string.insert(this_occurrence, replacementChars);
        this_occurrence = this_string.find(charsToReplace,
                                           this_occurrence + replacementChars.size());
    }

    return this_string;
}

Nếu bạn không muốn dựa vào việc sử dụng std::strings làm tham số của mình để thay vào đó bạn có thể chuyển qua chuỗi kiểu C, bạn có thể xem mẫu được cập nhật bên dưới:

#include <iostream>
#include <string>

std::string strsub(const char * stringToModify,
                   const char * charsToReplace,
                   const char * replacementChars,
                   uint64_t sizeOfCharsToReplace,
                   uint64_t sizeOfReplacementChars);

int main()
{
    std::string silly_typos = "annoiiyyyng syyyllii tiipos.";

    std::cout << "Look at these " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos.c_str(), "yyy", "i", 3, 1);
    std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos.c_str(), "ii", "y", 2, 1);

    std::cout << "There, no more " << silly_typos << std::endl;
    return 0;
}

std::string strsub(const char * stringToModify,
                   const char * charsToReplace,
                   const char * replacementChars,
                   uint64_t sizeOfCharsToReplace,
                   uint64_t sizeOfReplacementChars)
{
    std::string this_string = stringToModify;

    std::size_t this_occurrence = this_string.find(charsToReplace);
    while (this_occurrence != std::string::npos)
    {
        this_string.erase(this_occurrence, sizeOfCharsToReplace);
        this_string.insert(this_occurrence, replacementChars);
        this_occurrence = this_string.find(charsToReplace,
            this_occurrence + sizeOfReplacementChars);
    }

    return this_string;
}

0

Đối với các tình huống đơn giản, nó hoạt động khá tốt mà không cần sử dụng bất kỳ thư viện nào khác sau đó std :: string (đã được sử dụng).

Thay thế tất cả các lần xuất hiện của ký tự a bằng ký tự b trong some_ chuỗi :

for (size_t i = 0; i < some_string.size(); ++i) {
    if (some_string[i] == 'a') {
        some_string.replace(i, 1, "b");
    }
}

Nếu chuỗi lớn hoặc nhiều cuộc gọi để thay thế là một vấn đề, bạn có thể áp dụng kỹ thuật được đề cập trong câu trả lời này: https://stackoverflow.com/a/29752943/3622300


0

Đây là một giải pháp tôi đã triển khai, với tinh thần DRI tối đa. nó sẽ tìm kiếm sNeedle trong sHaystack và thay thế nó bằng sReplace, nTimes nếu không 0, khác với tất cả các lần xuất hiện của sNeedle. nó sẽ không tìm kiếm lại trong văn bản thay thế.

std::string str_replace(
    std::string sHaystack, std::string sNeedle, std::string sReplace, 
    size_t nTimes=0)
{
    size_t found = 0, pos = 0, c = 0;
    size_t len = sNeedle.size();
    size_t replen = sReplace.size();
    std::string input(sHaystack);

    do {
        found = input.find(sNeedle, pos);
        if (found == std::string::npos) {
            break;
        }
        input.replace(found, len, sReplace);
        pos = found + replen;
        ++c;
    } while(!nTimes || c < nTimes);

    return input;
}
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.