2015-11-02 104 views
0

我正在嘗試編寫一個程序,它使用基於範圍的for循環在C++中消除空格。例如,如果輸入是「你叫什麼名字?」 ,輸出應該是「Whatisyourname?」然而當我運行下面的代碼時,它給出的輸出是「Whatisyourname?me?」,爲什麼?在C++中使用基於範圍的循環

int main() 
{ 
    string s = "What is your name?"; 
    int write_index = 0; 
    for (const char &c : s) 
    { 
     if (c != ' ') 
     { 
      s[write_index++] = c; 
     } 
    } 
    cout << s << endl; 
    system("pause"); 
} 
+3

您用較短的輸出覆蓋輸入,但長度保持不變。 –

+0

啊,有道理。 對不起,我是C++的新手,你能告訴我這個循環如何用正常的循環寫入嗎? –

+0

您可以跟蹤空格的數量,然後在最後調整字符串的大小。 – u8sand

回答

1

這樣做的原因是因爲字符串s仍然是隻要原來的字符串,"What is your name?"。除了最後三個字符之外,您在字符串中的每個字符的頂部寫了一個字符。你可以做的是刪除空格後刪除字符串中的最後三個字符。這是未經測試,但類似這樣的應該工作:

s.erase(write_index, s.length() - write_index) 

您的基於循環使用的範圍是正確的。請記住,你正在循環所有輸入字符(就好像你正在循環與for (int i = 0; i < s.length(); i++),但你不輸出儘可能多的字符,因爲你正在閱讀。

所以等效的循環會像這樣的:

for (int i = 0; i < s.length(); i++) { 
    const char& c = s[i]; 

    if (c != ' ') { 
     s[write_index++] = c; 
    } 
} 
+0

for(const char&c:s) @JordanMelo這個循環如何映射到正常的循環?像我們使用循環標準時的等價語句一樣? –

+0

我編輯了我的答案,以顯示相當於循環:) –

+0

你是一個傳奇,謝謝你這麼多:)! –

0

你可以跟蹤你有空間的數量,並在年底調整串

int main() 
    { 
     string s = "What is your name?"; 
     int length = s.length(); 
     int write_index = 0; 
     for (const char &c : s) 
     { 
       if (c != ' ') 
       { 
        s[write_index++] = c; 
       } 
       else 
       { 
        length -= 1; 
       } 
     } 
     s.resize(length); 
     cout << s << endl; 
    } 
2

添加循環之後下面的語句

s.erase(write_index); 

s.resize(write_index); 

從字符串刪除冗餘字符。

的一般方法,以這樣的任務是以下

#include <algorithm> 
#include <string> 

//... 

s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); 
+0

+1用於擦除刪除成語。謹慎地說,在raw中使用erase-remove idiom會遇到一個問題,如果你在'erase'調用結束時忘記了's.end()',代碼就會編譯並通過「這是否刪除了一個元素」的基本測試。然後,它通過任何東西,但只有一個元素的容器擦除時失敗。我被這個錯誤燒燬了。 – Yakk

+0

@Yakk在porgrams中有很多這樣的錯誤。:) –

0

試試這個:

#include <string.h> 
#include <iostream> 

using namespace std; 

int main() 
    { 
     string s = "What is your name?"; 
     std::string aux(s.size(),' '); 
     int write_index = 0; 
     for (const char &c : s) 
     { 
       if (c != ' ') 
       { 
        aux[write_index++] = c; 
       } 
     } 
     cout << s << endl; 
     cout << aux << endl; 
     system("pause"); 
    } 
+0

當你已經寫了'namespace std;'在上面,你爲什麼寫'std :: string?'我的意思是,如果我們寫入'string'而不是'std ::',那麼代碼的工作原理是一樣的嗎? –

+2

'std :: string'在'',而不是'' – zmb

0

現在,我不親自代碼C++,但是這看起來極其相似的for-each loop在C# ,Java和JavaScript;所以我會給它一個。

讓我們先來打破你的代碼,看看發生了什麼事情

int main() { 
    // creates a string object which is essentially a glorified array of chars 
    string s = "What is your name?"; 
    int write_index = 0; 
    // for evry char "c" in the char-array "s" 
    for (const char &c : s) { 
    // if c isn't a space 
    if (c != ' ') { 
     // write c to s at index "write_index" then increment "write_index" 
     s[write_index++] = c; 
    } 
    } 

    std::cout << s << std::endl; 
    system("pause"); 
} 

的邏輯似乎不錯,所以爲什麼「你叫什麼名字?」變成「whatisyourname?me?」?簡單。因爲你覆蓋了現有的數組。

「你叫什麼名字?」長度爲18個字符,並且由於您只向數組寫入非空格字符,如果它不是空格,則實質上是複製字符爲文本中的每個空格留下一個空格。

例如下面的代碼在前7個字符上運行這個代碼後會發生什麼:「whatiss your name?」,在第一個12之後:「whatisyourur name?」,最後是18之後:「whatisyourname?me? 」。字符串的長度永遠不會改變。

所以,你有多種選擇來解決這個問題:

  1. 構建從舊的一個是字符串建設者一個新的字符串(如C++中存在這樣的事),並返回新創建的字符串。
  2. 計算您遇到的空格數並返回一個子字符串,它的字符數較短(原始爲18個字符 - 3個空格=新的字符爲15個字符)。
  3. 由所需的字符的量減少字符串的長度(感謝Yakk爲這一個)
+0

好的嘗試,並且你發現了這個問題。在C++中,你可以改變'std :: string'的長度,而不是你的兩個選項中的任何一個。 – Yakk

+0

@Yakk感謝您的回覆 –

1

這裏有兩個有用的小功能:

template<class C, class F> 
bool remove_erase_if(C& c, F&& f) { 
    using std::begin; using std::end; 
    auto it = std::remove_if(begin(c), end(c), std::forward<F>(f)); 
    if (it == c.end()) 
    return false; 
    c.erase(it, c.end()); 
    return true; 
} 
template<class C, class T> 
bool remove_erase(C& c, T&& t) { 
    using std::begin; using std::end; 
    auto it = std::remove(begin(c), end(c), std::forward<T>(t)); 
    if (it == c.end()) 
    return false; 
    c.erase(it, c.end()); 
    return true; 
} 

這些既採取的容器中,並且或者一個測試或一個元素。

然後,他們刪除並擦除所有通過測試的元素,或等於元素。

您的代碼模擬了上述代碼的remove部分,並沒有執行erase部分。所以剩下的角色依然存在。

remove(或您的代碼)只是將所有「保留」的數據移動到容器的前面。最後剩下的東西......留在那裏。步驟erase然後告訴容器之後的東西應該丟棄你保留的東西。如果你不放棄它,那它......仍然......並且你得到了你的錯誤。

通過以上兩個功能,你可以這樣做:

int main() { 
    std::string s = "What is your name?"; 
    remove_erase(s, ' '); 
    std::cout << s << '\n'; 
} 

和你做。

另外,using namespace std;通常是bad idea。並且std::endl強制緩衝區刷新,所以我更喜歡'\n'。最後,system("pause")可以通過在一個模式下運行IDE來模擬,使您的命令窗口保持打開狀態,而不是將其添加到您的代碼中Ctrl-F5

+0

Thankyou。 我正在使用visual studio 2015,幾天後做Ctrl + F5做的工作,但現在它劑量。不管怎麼說,評論的捷徑都不起作用(Ctrl + K),關於如何解決這個問題的任何想法? 此外,你是什麼意思緩衝區沖洗? 爲什麼更好的做法是做一些像 std :: cout,std :: endl之類的東西,而不是僅僅在頂部使用命名空間標準輸入一次? –

0

這是標準庫中copy_if算法的基本應用。

#include <algorithm> 
#include <cctype> 
#include <iostream> 
#include <iterator> 
#include <string> 

int main() 
{ 
    std::string s = "What is your name?"; 
    std::copy_if(s.begin(), s.end(), std::ostream_iterator<char>(std::cout), 
     [](char c){ return !std::isspace(c); }); 
    return 0; 
} 

輸出:

Whatisyourname?

如果你確實需要從原始字符串中刪除,然後使用算法remove_if其次erase

相關問題