Làm cách nào để Tìm kiếm / Tìm và Thay thế trong một chuỗi tiêu chuẩn?


Câu trả lời:


74

Tại sao không thực hiện thay thế của riêng bạn?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}

3
Bạn đang gặp rắc rối một chút với bộ nhớ ở đây với tất cả các lệnh gọi "thay thế": độ phức tạp sẽ là n² nếu bạn xóa "o" khỏi "ooooooo ... o". Tôi đoán một người có thể làm tốt hơn, nhưng giải pháp này có điểm đáng chú ý là dễ hiểu.
Zonko

1
Tại sao đây không phải là vòng lặp for thực tế, chứ không phải là vòng lặp for bị xáo trộn?
Shirik

Tôi đã quen áp dụng nguyên tắc 'ít bất ngờ nhất'. Đối với vòng lặp là để sử dụng tăng chỉ mục đơn giản, hầu hết thời gian. Ở đây, theo tôi, vòng lặp while rõ ràng hơn.
yves Baumes

1
@aldo Theo nguyên tắc chung, tốt hơn là nên tránh sự phức tạp và ví dụ: sử dụng regex như đã đề cập trong các câu trả lời khác. Nhưng tùy thuộc vào nhu cầu của bạn, bạn có thể muốn kiểm soát các phụ thuộc dự án của mình. Một đoạn mã nhỏ thực hiện chính xác những gì bạn cần, không cần nhiều hơn, đôi khi tốt hơn.
yves Baumes 27/10/12

158
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

Đây là tài liệu chính thức về Replace_all.


1
Lưu ý rằng bạn không phải tạo rõ ràng std :: string cho mẫu và thay thế: boost :: Replace_all (target, "foo", "bar");
Alexis Wilke

4
+1, với một caveat: replace_allsẽ segfault cho các phiên bản của tăng> 1,43 trên Sun Studio cho bất kỳ phiên bản <12,3
Brian Vandenberg

3
boosttăng đáng kể thời gian biên dịch trên các thiết bị nhúng. Ngay cả lõi tứ ARMv7. 100 dòng mã biên dịch trong 2 phút, không cần tăng tốc, 2 giây.
Piotr Kula

4
@ppumkin: điều đó có nghĩa là trình biên dịch của bạn (hoặc thiết lập bản dựng, hoặc bất cứ thứ gì) tệ hại, không phải kiến ​​trúc đích, không liên quan gì đến nó.
Daniel Kamil Kozar,

Nếu trình biên dịch của bạn hỗ trợ tiêu đề được biên dịch trước, bạn nên sử dụng nó khi sử dụng boost. Nó thực sự tiết kiệm thời gian.
Alexey Omelchenko

33

Trong C ++ 11, bạn có thể thực hiện việc này dưới dạng một lớp lót với lệnh gọi tới regex_replace:

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

đầu ra:

Removeallspaces

Cảm ơn, rất dễ sử dụng và nhớ!
Julian Declercq

Cũng lưu ý rằng đó fromcó thể là một biểu thức chính quy - vì vậy bạn có thể sử dụng các tiêu chí đối sánh phức tạp hơn nếu cần. Điều tôi không thấy là cách thực hiện điều này mà không áp dụng một số dạng phân tích cú pháp biểu thức chính quy - thay vào đó chỉ sử dụng diễn giải trực tiếp các fromký tự.
Brent Bradburn

Điều này có thể yêu cầu một trình biên dịch cập nhật. Nó hoạt động với gcc 5.0, nhưng tôi gặp một số rắc rối với gcc 4.8.4.
Brent Bradburn

@nobar, vâng, nếu tôi nhớ chính xác thì hỗ trợ regex trong 4.8.x chưa hoàn thành. Ngoài ra, bạn có thể có những tìm kiếm phức tạp hơn, nhưng bạn sẽ bị phạt thời gian khôn ngoan ... Nó sẽ chậm hơn so với các chức năng tìm kiếm và thay thế chuyển tiếp khác.
Alexis Wilke

2
Xin lưu ý rằng điều này sẽ chỉ hoạt động đối với các ký tự chữ và số rất cơ bản và không có gì khác nếu không thực hiện nhiều tiền xử lý tùy thuộc vào loại chuỗi. Tôi chưa tìm thấy thay thế chuỗi dựa trên regex có mục đích chung.
Piyush Soni

17

Tại sao không trả về một chuỗi đã sửa đổi?

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 hàm được tối ưu hóa để sửa đổi chuỗi đầu vào, nó không tạo 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();
    }
}

Kiểm tra:

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 changed: " 
          << 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

6

Tính năng tìm và thay thế tại chỗ nội tuyến được tạo mẫu của tôi:

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

Nó trả về số lượng các mục được thay thế (để sử dụng nếu bạn muốn chạy liên tục, v.v.). Để dùng nó:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

4
Tôi đã thử mẫu này trong GCC nhưng nó sẽ không biên dịch - nó không thích việc sử dụng T :: size_t. Thay thế T :: size_t bằng typename T :: size_type sẽ khắc phục được sự cố.
Andrew Wyatt,

3

Cách dễ nhất (cung cấp một cái gì đó gần với những gì bạn đã viết) là sử dụng Boost.Regex , cụ thể là regex_replace .

std :: string đã tích hợp sẵn các phương thức find () và Replace (), nhưng chúng khó làm việc hơn vì chúng yêu cầu xử lý các chỉ số và độ dài chuỗi.


3
Ngoài ra còn có các thuật toán chuỗi tăng, bao gồm cả Replace_all (regex có thể hơi nặng đối với sự thay thế đơn giản như vậy).
UncleBens 29/09/09

3

Tôi tin rằng điều này sẽ hiệu quả. Nó nhận const char * 's làm tham số.

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}

Cho rằng size_typeđối với một chuỗi là unsigned, >=điều kiện kiểm tra của bạn trong vòng lặp sẽ luôn là true. Bạn phải sử dụng std::string::nposở đó.
Pavel Minaev 29/09/09

size_type không có dấu. Nó không được ký trên nhiều nền tảng, nhưng không phải tất cả.
Alan

12
Tại sao trên thế giới đây không phải là một phần của std :: string? Có lớp String nghiêm túc nào khác trong thế giới lập trình không cung cấp hoạt động 'tìm và thay thế' không? Chắc chắn nó phổ biến hơn việc có hai trình lặp và muốn thay thế văn bản giữa chúng ?? Đôi khi std :: string cảm thấy giống như một chiếc ô tô với kính chắn gió phổ có thể điều chỉnh được nhưng không có cách nào để cuộn cửa sổ của người lái xuống.
Spike0xff

@ roll_down_window
Spike0xff

1
@gustafr: Sai lầm của tôi. Tôi đã làm việc trên các hệ thống mà các trình biên dịch cũ hơn đã xác định size_t không đúng cách.
Alan

1
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}

1
Chúng tôi chỉ cần tìm kiếm các trận đấu mới từ trận đấu cuối cùng, đó là lý do tại sao thuật toán theo dõi cẩn thận trận đấu cuối cùng trong pos. pos2 luôn lưu trữ kết quả phù hợp tiếp theo, vì vậy chúng tôi nối chuỗi giữa pos và pos2 với kết quả, sau đó tăng thêm pos và pos2. Nếu không tìm thấy kết quả phù hợp nào khác, chúng tôi nối phần còn lại của chuỗi với kết quả.
Björn Ganster

1
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

Việc kiểm tra oldStr trống là điều quan trọng. Nếu vì bất cứ lý do gì mà tham số đó trống, bạn sẽ bị mắc kẹt trong một vòng lặp vô hạn.

Nhưng đúng vậy, hãy sử dụng giải pháp C ++ 11 hoặc Boost đã được thử và kiểm tra nếu bạn có thể.

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.