Thay thế một phần của chuỗi bằng một chuỗi khác


185

C ++ có thể thay thế một phần của chuỗi bằng một chuỗi khác không?

Về cơ bản, tôi muốn làm điều này:

QString string("hello $name");
string.replace("$name", "Somename");

Nhưng tôi muốn sử dụng các thư viện Standard C ++.


1
có thể trùng lặp chức năng để thay thế chuỗi trong C là gì? - Rất tiếc, đó là C, không phải C ++; Tôi ước tôi có thể hủy bỏ.
đa gen

1
@poly Tôi nghĩ rằng điều này cũng đã được yêu cầu cho C ++, nhưng tôi không thể tìm thấy nó
Michael Mrozek

1
Có một thẻ tiêu chuẩn cho câu hỏi, nhưng có lẽ bạn có thể quan tâm đến các thuật toán chuỗi của boost, nó cũng bao gồm nhiều lựa chọn thuật toán thay thế (inplace / copy, phân biệt chữ hoa chữ thường / chữ hoa chữ thường, thay thế trước / cuối / tất cả / n-th ).
ChúBens

@Michael Mrozek Đã có một cái tại stackoverflow.com/questions 43218231/, nhưng nó mới hơn và phương thức thay thế của bạn ở đây mạnh mẽ hơn.
dave-holm

Câu trả lời:


287

Có một hàm để tìm một chuỗi con trong một chuỗi ( find) và một hàm để thay thế một phạm vi cụ thể trong một chuỗi bằng một chuỗi khác ( replace), vì vậy bạn có thể kết hợp các chuỗi đó để có được hiệu ứng bạn muốn:

bool replace(std::string& str, const std::string& from, const std::string& to) {
    size_t start_pos = str.find(from);
    if(start_pos == std::string::npos)
        return false;
    str.replace(start_pos, from.length(), to);
    return true;
}

std::string string("hello $name");
replace(string, "$name", "Somename");

Đáp lại một bình luận, tôi nghĩ replaceAllcó lẽ sẽ trông giống như thế này:

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if(from.empty())
        return;
    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(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}

2
Tôi sẽ sửa nó như thế nào nếu chuỗi gốc có thêm một thể hiện của "$ name" và tôi muốn thay thế tất cả chúng.
Tom Leese

1
Tại sao không fromtothông qua mỗi consttham chiếu? Chức năng của bạn fromlà gì nếu không có? -1từ tôi cho điều đó.
sbi

10
@sbi Đã sửa, mặc dù bạn có thể gọi nó là đề xuất thay vì tấn công - đơn giản là nó không xảy ra với tôi, tôi hiếm khi nghĩ đến việc sử dụng constvà nếu tôi viết một phương thức tiện ích như thế này thì tôi sẽ chỉ gọi nó nếu tôi biết thay thế là hợp lệ
Michael Mrozek

10
@Michael: Tốt, tôi đã biến phiếu bầu của mình thành phiếu bầu tăng. Việc từ bỏ constlà coi thường một trong những công cụ tốt nhất của C ++. Truyền cho mỗi consttham chiếu phải là chế độ mặc định cho các tham số chức năng. (FTR, không có const, bạn thậm chí không thể chuyển chuỗi ký tự chuỗi cho hàm của mình, vì bạn không thể liên kết tạm thời với các consttham chiếu không . Vì vậy, hàm này thậm chí sẽ không thực hiện những gì nó được viết.)
sbi

19
Đây có phải vẫn là giải pháp duy nhất trong năm 2018? Nếu vậy và bất kỳ ủy ban C ++ nào đang đọc nó, hãy sắp xếp nó ra. Thật xấu hổ. xin chia (chuỗi, chuỗi) và thay thế (chuỗi, chuỗi)!
dùng997112

95

Với C ++ 11, bạn có thể sử dụng std::regexnhư vậy:

#include <regex>
...
std::string string("hello $name");
string = std::regex_replace(string, std::regex("\\$name"), "Somename");

Dấu gạch chéo kép là cần thiết để thoát một ký tự thoát.


Tôi khá chắc chắn std::regex_replacekhông chấp nhận chuỗi của Qt.
BartoszKP

1
Bạn đúng rồi. Khi nó xảy ra, QString cung cấp một phương thức thay thế chấp nhận QRexExp, cho phép sử dụng nội dung của chính Qt. Nhưng tôi nghĩ rằng câu trả lời hiện tại có thể được sửa chữa bằng cách thay thế stringbằng string.toStdString().
Tom

1
Hoặc chỉ bằng cách thay đổi Stringthành std::string, vì câu hỏi không liên quan đến Qt. Vui lòng xem xét việc đó - Tôi sẽ vui lòng đưa ra câu trả lời của bạn sau đó.
BartoszKP

5
Chuỗi thô cho phép viết R"(\$name)"thay vì "\\$name".
Jarod42

2
Bao nhiêu điều này sẽ chậm hơn tìm / thay thế mà không xem xét thời gian xây dựng của std :: regex?
jw_

18

std::string có một replace phương pháp, đó là những gì bạn đang tìm kiếm?

Bạn có thể thử:

s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");

Tôi đã không thử bản thân mình, chỉ cần đọc tài liệu trên find()replace().


2
Từ những gì tôi có thể thấy phương thức thay thế chuỗi std :: không lấy hai chuỗi như tôi muốn.
Tom Leese

2
Điều này không làm việc cho tôi. sizeof nên được thay thế bằng chuỗi ("Somename"). size () - 1
TimZaman

@TimZaman: Điều đó đánh đố tôi, tài liệu nêu rõ bạn có thể khởi tạo từ chuỗi kiểu C.
SC Madsen

5
đối số thứ 2 phải là độ dài của "$ name" (thay vì độ dài của "Somename"), phải không?
Daniel Kiss

10

Để có chuỗi mới được trả về, hãy sử dụng:

std::string ReplaceString(std::string subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
    return subject;
}

Nếu bạn cần hiệu suất, đây là một chức năng được tối ưu hóa để sửa đổi chuỗi đầu vào, nó không tạo ra một bản sao của chuỗi:

void ReplaceStringInPlace(std::string& subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

Các xét nghiệm:

std::string input = "abc abc def";
std::cout << "Input string: " << input << std::endl;

std::cout << "ReplaceString() return value: " 
          << ReplaceString(input, "bc", "!!") << std::endl;
std::cout << "ReplaceString() input string not modified: " 
          << input << std::endl;

ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: " 
          << input << std::endl;

Đầu ra:

Input string: abc abc def
ReplaceString() return value: a!! a!! def
ReplaceString() input string not modified: abc abc def
ReplaceStringInPlace() input string modified: a?? a?? def

Cuộc gọi của bạn đến topic.replace trong Thay thếStringInPlace () có thực sự sửa đổi chuỗi tại chỗ không?
Damian

Tôi đã xem xét ngắn gọn về nguồn và có vẻ như nó sử dụng ngữ nghĩa di chuyển để di chuyển mặt trước của chuỗi cũ vào vị trí, do đó không được sao chép, nhưng đoạn mới được chèn vào sao chép vào chuỗi cũ và đuôi của chuỗi cũ được sao chép vào bộ đệm đã thay đổi kích thước của chuỗi cũ. Có thể chuỗi mở rộng rất nhiều toàn bộ bộ đệm bên dưới được phân bổ lại, nhưng nếu bạn thay thế 1 thành 1 như trong ví dụ của mình, tôi nghĩ rằng nó xảy ra "tại chỗ" hoặc không có bất kỳ sao chép nào, nhưng nếu bạn mở rộng chuỗi, chỉ phần đầu tiên của chuỗi cũ không được sao chép và chỉ có thể sau đó.
Bỏ phiếu

6

Có, bạn có thể làm điều đó, nhưng bạn phải tìm vị trí của chuỗi đầu tiên với thành viên find () của chuỗi, sau đó thay thế bằng thành viên thay thế ().

string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
   s.replace( pos, 5, "somename" );   // 5 = length( $name )
}

Nếu bạn đang có kế hoạch sử dụng Thư viện tiêu chuẩn, bạn thực sự nên nắm giữ một bản sao của cuốn sách Thư viện chuẩn C ++ bao gồm tất cả những thứ này rất tốt.


1
đó là size_t chứ không phải size_type
revo

1
Đó là std :: string :: size_type, không phải size_t hoặc size_type không được cung cấp.
jmucchiello

5

Tôi thường sử dụng cái này:

std::string& replace(std::string& s, const std::string& from, const std::string& to)
{
    if(!from.empty())
        for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size())
            s.replace(pos, from.size(), to);
    return s;
}

Nó liên tục gọi std::string::find()để xác định vị trí xuất hiện khác của chuỗi tìm kiếm cho đến khi std::string::find()không tìm thấy gì. Vì std::string::find()trả về vị trí của trận đấu, chúng tôi không gặp vấn đề về việc vô hiệu hóa các vòng lặp.


4

Điều này nghe có vẻ như là một lựa chọn

string.replace(string.find("%s"), string("%s").size(), "Something");

Bạn có thể gói cái này trong một hàm nhưng giải pháp một dòng này nghe có vẻ chấp nhận được. Vấn đề là điều này sẽ chỉ thay đổi lần xuất hiện đầu tiên, bạn có thể muốn lặp lại nó, nhưng nó cũng cho phép bạn chèn một số biến vào chuỗi này với cùng một mã thông báo ( %s)


1
Giống như phong cách nhưng tìm thấy các chuỗi khác nhau khó hiểu ^^str.replace(str.find("%s"), string("%s").size(), "Something");
Paul Wurtz

3

Nếu tất cả các chuỗi là std :: string, bạn sẽ thấy các vấn đề lạ với việc cắt các ký tự nếu sử dụng sizeof()vì nó có nghĩa là chuỗi C, không phải chuỗi C ++. Cách khắc phục là sử dụng .size()phương thức lớp std::string.

sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);

Điều đó thay thế nội tuyến sHaystack - không cần thực hiện một phép gán = trên đó.

Ví dụ sử dụng:

std::string sHaystack = "This is %XXX% test.";
std::string sNeedle = "%XXX%";
std::string sReplace = "my special";
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
std::cout << sHaystack << std::endl;

2
wstring myString = L"Hello $$ this is an example. By $$.";
wstring search = L"$$";
wstring replace = L"Tom";
for (int i = myString.find(search); i >= 0; i = myString.find(search))
    myString.replace(i, search.size(), replace);

2

Nếu bạn muốn làm điều đó một cách nhanh chóng, bạn có thể sử dụng phương pháp quét hai. Mã giả:

  1. phân tích đầu tiên. tìm bao nhiêu ký tự trùng khớp.
  2. mở rộng độ dài của chuỗi.
  3. phân tích thứ hai. Bắt đầu từ cuối chuỗi khi chúng tôi nhận được một kết quả khớp, chúng tôi chỉ sao chép ký tự từ chuỗi đầu tiên.

Tôi không chắc chắn nếu điều này có thể được tối ưu hóa thành một thuật toán tại chỗ.

Và một ví dụ mã C ++ 11 nhưng tôi chỉ tìm kiếm một char.

#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

void ReplaceString(string& subject, char search, const string& replace)
{   
    size_t initSize = subject.size();
    int count = 0;
    for (auto c : subject) { 
        if (c == search) ++count;
    }

    size_t idx = subject.size()-1 + count * replace.size()-1;
    subject.resize(idx + 1, '\0');

    string reverseReplace{ replace };
    reverse(reverseReplace.begin(), reverseReplace.end());  

    char *end_ptr = &subject[initSize - 1];
    while (end_ptr >= &subject[0])
    {
        if (*end_ptr == search) {
            for (auto c : reverseReplace) {
                subject[idx - 1] = c;
                --idx;              
            }           
        }
        else {
            subject[idx - 1] = *end_ptr;
            --idx;
        }
        --end_ptr;
    }
}

int main()
{
    string s{ "Mr John Smith" };
    ReplaceString(s, ' ', "%20");
    cout << s << "\n";

}

1
std::string replace(std::string base, const std::string from, const std::string to) {
    std::string SecureCopy = base;

    for (size_t start_pos = SecureCopy.find(from); start_pos != std::string::npos; start_pos = SecureCopy.find(from,start_pos))
    {
        SecureCopy.replace(start_pos, from.length(), to);
    }

    return SecureCopy;
}

2
Bạn có thể vui lòng giải thích mã này (trong câu trả lời của bạn)? Bạn có thể nhận được nhiều hơn upvote theo cách đó!
Chàng trai với chiếc mũ

1

Việc thực hiện của riêng tôi, có tính đến chuỗi đó chỉ cần thay đổi kích thước một lần, sau đó thay thế có thể xảy ra.

template <typename T>
std::basic_string<T> replaceAll(const std::basic_string<T>& s, const T* from, const T* to)
{
    auto length = std::char_traits<T>::length;
    size_t toLen = length(to), fromLen = length(from), delta = toLen - fromLen;
    bool pass = false;
    std::string ns = s;

    size_t newLen = ns.length();

    for (bool estimate : { true, false })
    {
        size_t pos = 0;

        for (; (pos = ns.find(from, pos)) != std::string::npos; pos++)
        {
            if (estimate)
            {
                newLen += delta;
                pos += fromLen;
            }
            else
            {
                ns.replace(pos, fromLen, to);
                pos += delta;
            }
        }

        if (estimate)
            ns.resize(newLen);
    }

    return ns;
}

Cách sử dụng có thể là ví dụ như thế này:

std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");

0

Tôi mới học C ++, nhưng chỉnh sửa một số mã được đăng trước đó, có lẽ tôi sẽ sử dụng cái gì đó như thế này. Điều này cho phép bạn linh hoạt thay thế 1 hoặc nhiều trường hợp và cũng cho phép bạn chỉ định điểm bắt đầu.

using namespace std;

// returns number of replacements made in string
long strReplace(string& str, const string& from, const string& to, size_t start = 0, long count = -1) {
    if (from.empty()) return 0;

    size_t startpos = str.find(from, start);
    long replaceCount = 0;

    while (startpos != string::npos){
        str.replace(startpos, from.length(), to);
        startpos += to.length();
        replaceCount++;

        if (count > 0 && replaceCount >= count) break;
        startpos = str.find(from, startpos);
    }

    return replaceCount;
}

0

Điều này có thể còn tốt hơn để sử dụng

void replace(string& input, const string& from, const string& to)
{
    while(true)
    {
        size_t startPosition = input.find(from);
        if(startPosition == string::npos)
            break;
        input.replace(startPosition, from.length(), to);
    }
}
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.