2008-10-23 53 views
5

Tôi đang cố gắng thay đổi đầu vào của người dùng ở dạng ký tự đại diện ("*word*") thành định dạng biểu thức chính quy. Cuối cùng, tôi đang sử dụng mã dưới đây để lột các '*' vào đầu và kết thúc của các đầu vào để tôi có thể thêm các ký tự biểu hiện thường xuyên trên hai đầu:std :: chuỗi xóa ký tự cuối cùng không thành công?

string::iterator iter_begin = expressionBuilder.begin(); 
string::iterator iter_end = expressionBuilder.end(); 
iter_end--; 
if ((char)*iter_begin == '*' && (char)*iter_end == '*') 
{ 
    expressionBuilder.erase(iter_begin); 
    expressionBuilder.erase(iter_end); 
    expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b"; 
} 

Tuy nhiên, các cuộc gọi đến "expressionBuilder.erase(iter_end)" không không xóa dấu sau '*' khỏi chuỗi đầu vào để tôi bắt đầu với cụm từ thông dụng không chính xác. Tôi làm gì sai ở đây? "(char)*iter_end == '*'" phải đúng với mã bên trong nếu statment để chạy (mà nó làm), vậy tại sao không cùng một iterator làm việc khi được chuyển tới xóa()?

Trả lời

3

Cố gắng xóa chúng theo thứ tự ngược lại:

expressionBuilder.erase(iter_end); 
expressionBuilder.erase(iter_begin); 

Sau khi xóa các * đầu tiên, iter_end đề cập đến một nhân vật quá khứ cuối chuỗi trong ví dụ của bạn. Các STL documentation chỉ ra rằng iterators là vô hiệu hóa bởi erase(), vì vậy về mặt kỹ thuật ví dụ của tôi là sai quá nhưng tôi tin rằng nó sẽ làm việc trong thực tế.

+0

May mắn với chuỗi bạn không cần phải sử dụng lặp, hầu hết các chức năng có một hình thức mà phải mất một chỉ số để thay thế. Tuy nhiên, như bạn nói, ngay cả với xóa chỉ mục nó vẫn nên được thực hiện "trở lại phía trước". –

+0

P4tXrx5jrMlbhyludk9pxHBT30kGHo9n: bạn đúng về kết thúc(), nhưng có một iter_end-- trong đó nhìn vào ký tự cuối cùng thực tế của chuỗi. –

+0

Điều này có ý nghĩa hoàn hảo và việc đảo ngược thứ tự đã giải quyết được vấn đề. Cảm ơn! – jeffm

1

(Đã sửa đổi, vì tôi đã bỏ lỡ dòng iter_end--).

Bạn có thể muốn một câu lệnh if mà chỉ kiểm tra nếu *iter_begin == '*', và sau đó gọi find() để có được những '*' khác. Hoặc bạn có thể sử dụng rbegin() để nhận "trình bắt đầu lặp lại của chuỗi ngược lại", trước nó một và sau đó gọi base() để biến nó thành trình lặp thường xuyên. Điều đó sẽ giúp bạn có được nhân vật cuối cùng trong chuỗi.


Thậm chí tốt hơn, std::stringrfind() and find_last_of() methods. Họ sẽ giúp bạn có được '*' mới nhất. Bạn cũng có thể chỉ cần gọi số replace() thay vì tước các số '*' giây rồi thêm lại nội dung mới.

+0

Lưu ý rằng có một iter_end-- trong đó sao lưu một ký tự. –

+0

Bạn đã bỏ lỡ "iter_end--;" dòng, mà di chuyển iterator trở lại mục cuối cùng? Tôi chắc chắn câu trả lời của Greg là đúng, bởi vì trình lặp chuỗi là cơ bản chỉ là chỉ mục, vì vậy chỉ mục kết thúc bị vô hiệu hóa bởi lần xóa đầu tiên. – Roddy

+0

Tôi đã cố gắng tránh "find_last_of" bởi vì tôi đã biết nhân vật ở đâu, nhưng có lẽ tôi đã đánh giá cao nó. – jeffm

7

Mã ban đầu của bạn và các giải pháp được đề xuất cho đến nay có một số vấn đề ngoài vấn đề hiển nhiên mà bạn đã đăng:

  • sử dụng các vòng lặp vô hiệu sau chuỗi được sửa đổi
  • dereferencing lặp có thể không hợp lệ ngay cả trước khi chuỗi được sửa đổi (nếu chuỗi rỗng, ví dụ)
  • một lỗi nếu chuỗi expressionBuilder chỉ chứa hát Ký tự le '*'

Bây giờ, hai mục cuối cùng có thể không thực sự là vấn đề nếu mã sử dụng đoạn mã/thường trình đã xác thực rằng chuỗi có ít nhất 2 ký tự, nhưng trong trường hợp đó không phải là tình hình, tôi tin rằng sau để được mạnh mẽ hơn khi đối mặt với giá trị tùy ý cho expressionBuilder:

// using the reverse iterator rbegin() is a nice easy way 
//  to get the last character of a string 

if ((expressionBuilder.size() >= 2) && 
    (*expressionBuilder.begin() == '*') && 
    (*expressionBuilder.rbegin() == '*')) { 

    expressionBuilder.erase(expressionBuilder.begin()); 

    // can't nicely use rbegin() here because erase() wont take a reverse 
    // iterator, and converting reverse iterators to regular iterators 
    // results in rather ugly, non-intuitive code 
    expressionBuilder.erase(expressionBuilder.end() - 1); // note - not invalid since we're getting it anew 

    expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b"; 
} 

Lưu ý rằng đoạn mã này sẽ làm việc khi expressionBuilder"", "*", hoặc "**" ở chỗ nó không thực hiện bất cứ hành động không xác định . Tuy nhiên, nó có thể không tạo ra kết quả bạn muốn trong những trường hợp đó (đó là bởi vì tôi không biết chính xác những gì bạn muốn trong những trường hợp đó). Sửa đổi cho phù hợp với nhu cầu của bạn.

+0

Cảm ơn. Tôi khá biết tại thời điểm này rằng chuỗi không phải là trống hoặc "*", nhưng tôi đồng ý rằng nó sẽ là tốt hơn để mã nó theo cách đó chỉ trong trường hợp một cái gì đó thay đổi sau này. – jeffm

+0

rất hay - chỉ cần sử dụng điều này trong một số mã của tôi quá – danio

0

Trừ việc xử lý lỗi, bạn có thể có lẽ chỉ làm điều đó như thế này:

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

string stripStar(const string& s) { 
    return string(s.begin() + 1, s.end() - 1); 
} 

int main() { 
    cout << stripStar("*word*") << "\n"; 
} 
+0

Nếu bạn gọi 'stripStar (" word ")' hoặc thậm chí 'stripStar (" word * ")'? Tôi nghĩ OP muốn sự linh hoạt này. – Cosine

Các vấn đề liên quan