我有一個陣列:
names[4]={john,david,jack,harry};
我想隨機洗牌,比如:
names[4]={jack,david,john,harry};
我嘗試使用它,但它只是打亂了陣列中第一個單詞的字母:
random_shuffle(names->begin(), names->end());
這是完整的代碼,它從 .txt 檔案中讀取名稱并放入一個陣列中:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
ifstream readName("names.txt");
string names[197];
int i = 0;
for (string line; getline(readName, line); ){
readName >> names[i];
i ;
}
readName.close();
random_shuffle(names->begin(), names->end());
for (int i = 0; i < 197; i ) {
cout << names[i] << endl;
}
return 0;
}
我從不同的人那里嘗試了一些其他的東西,但我無法解決它.. 任何幫助,謝謝!
uj5u.com熱心網友回復:
這是您的代碼,我認為更改量最少。有人可能會爭辯說我不需要對您的第一個 for 回圈進行太多更改,但我認為如果您有先見之明知道您正在閱讀多少個名字,那么您不妨使用這些知識。
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator> // std::begin(), std::end(); required for C-arrays
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
// using namespace std; // BAD PRACTICE
int main() {
constexpr int size = 4; // Give your magic number a name; only need to change
// a single location
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::string names[size];
// int i = 0;
for (int i = 0; i < size; i) { // Retool the loop entirely
std::getline(readName, names[i]);
}
readName.close();
// This is a fragile solution. It's only working because the array is in
// scope
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (int i = 0; i < size; i ) {
std::cout << names[i]
<< '\n'; // Don't use std::endl unless you actually need it
}
return 0;
}
不過,這不是理想的代碼。對輸入檔案大小的任何更改都需要更改代碼并重新編譯。最大的單一變化是擺脫std::random_shuffle并使用std::shuffle()。std::random_shuffle在 C 14 中被棄用并在 C 17 中洗掉。使用它是不好的。std::shuffle()確實增加了提供 PRNG 的要求,但還不錯。如果您的 PRNG 需要在更大的程式中隨機化許多不同的東西,它會產生更好的代碼。這是因為擁有一個 PRNG 并讓它在您的程式中持續存在是很好的,而不是不斷構建新的。
而 C 陣列只會讓事情變得有點笨拙。輸入std::vector。
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
#include <vector>
int main() {
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::vector<std::string> names;
std::string name;
while (std::getline(readName, name)) { // Retool the loop entirely
names.push_back(name);
}
readName.close();
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (const auto& i : names) {
std::cout << i << '\n';
}
return 0;
}
The vector can grow as needed, so you see how much simpler the loop that reads the names becomes. It's also more flexible since you don't have to know ahead of time how many entries to expect. It will "just work." With the call to std::shuffle() I kept the std::begin(names) syntax because many consider this a best practice, but you could have also used names.begin() if you wanted since the vector class provides its own iterators.
uj5u.com熱心網友回復:
讓我們來看看你的主要問題:
我嘗試使用它,但它只是打亂了陣列中第一個單詞的字母:
random_shuffle(names->begin(), names->end());
之所以只混洗第一個詞是因為型別和用法。
names字串陣列也是如此。
string names[197];
問題源于C世界。陣列是否非常容易衰減為指標(僅通過在運算式中使用)。所以這里names->已經衰變成了一個指向陣列第一個元素的指標。這允許您使用->通常僅適用于指標的運算子。因此,您正在呼叫函式begin()和end()指向陣列中第一個元素的指標。因此,只有名字被改組。
修復這個問題的使用std::begin()方法。
// here std::begin / std::end find the beginning and end
// of the array. So you are shuffling the array.
random_shuffle(std::begin(names), std::end(names));
但我會注意到這random_shuffle()已經過時了。正如@sweenish 所提到的,您應該使用std::shuffle()See his answer了解詳細資訊。
我們可以改進以下幾點:
您使用 C 陣列來存盤名稱。當然它可以作業,但它容易受到一些問題的影響,因為它無法重新調整大小(除非您認為檔案永遠不會被更改,否則這可能是一個問題)。對于遙遠的維護者來說,這可能是一個隱藏的問題。
std::vector<std::string> names; // resizeable container.
我會注意到當前的實作忽略了第一行。然后從每個后續行中讀取第一個單詞。還有一個小問題,最后一行可能是空的,并且您將空名稱讀入陣列的最后一個元素(但您不會跟蹤讀取的名稱數量,因此除非您使用陣列中的所有元素,否則您可能永遠不會請注意)。
我會改變的。因為不明顯。我會故意并單獨忽略第一行。然后我會簡單地將所有第一個單詞讀入一個向量(這樣你就知道大小了)。
std::string line
std::getline(file, line); // Ignore the first line.
std::string word
while(file >> word) {
names.push_back(word);
std::getline(file, line); // ignore the rest of the line.
}
我們可以得到幻想。使用迭代器直接創建陣列。
class Line
{
std::string firstWord;
friend std::istream& operator>>(std::istream& stream, Line& data) {
stream >> data.firstWord;
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); retrun stream;
}
operator std::string() const {
return firstWord;
}
};
現在您可以在一行中創建和加載向量:
std::vector<std::string> names(std::istream_iterator<Line>(file),
std::istream_iterator<Line>{});
最后,使用 foreach 回圈可以更輕松地復制名稱。也不要std::endl在這樣的回圈中使用。它會在每個新行之后強制重繪 底層 bug。這是非常低效的。
for(auto const& name: names) {
std::cout << name << "\n";
}
所以結果是:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
class Line
{
std::string firstWord;
friend std::istream& operator>>(std::istream& stream, Line& data) {
stream >> data.firstWord;
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); return stream;
}
operator std::string() const {
return firstWord;
}
};
int main()
{
std::ifstream file("names.txt");
std::string line
std::getline(file, line); // Ignore the first line.
std::vector<std::string> names(std::istream_iterator<Line>(file),
std::istream_iterator<Line>{});
random_shuffle(std::begin(names), std::end(names));
for(auto const& name: names) {
std::cout << name << "\n";
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/346680.html
