如何从其中包含 space 的文件中读取字符串 C++
How to read a string from a file with space within it c++
我写了一个在文件中搜索字符串的代码。实际上,我把我的数据放在我的文件中是这样的:
alex booth,15,1
(全名 space、年龄、身份证)。
在文件中保存数据没有任何问题,我在该文件中搜索字符串时遇到了一个大问题。
例如,我想在文件中搜索像 "alex booth" 这样的名称,但似乎搜索无法找到该词,尽管该词可用。我猜想用 space 保存名称有问题,但我不知道我应该怎么做才能修复它。事实上,我不知道我们如何才能搜索其中包含空白 space 的名称。
这是我的代码:
using namespace std;
struct product{
string name;
int price, id;
};
int main() {
product myProduct;
ofstream productList("product.csv", ios::app | ios::out);
getline(cin, myProduct.name);
cin >> myProduct.price;
cin >> myProduct.id;
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << endl;
productList.close();
cout << "----" << endl;
system("pause");
ifstream checkList("product.csv");
string dummy;
string check;
cin.ignore();
getline(cin, check);
while (!checkList.eof()) {
while (checkList >> myProduct.name >> myProduct.price >> myProduct.id) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
当您从文件中读取行时,您根本没有考虑逗号,然后 operator>>
在您的内部 while
循环中失败。
还有,your outer while
loop is wrong,也是。
尝试更像这样的东西:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <limits>
#include <cstdlib>
using namespace std;
struct product{
string name;
int price, id;
};
int main() {
product myProduct;
cout << "Enter product name: ";
getline(cin, myProduct.name);
cout << "Enter product price: ";
cin >> myProduct.price;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Enter product id: ";
cin >> myProduct.id;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
ofstream productList("product.csv", ios::app);
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << std::endl;
productList.close();
cout << "----" << endl;
system("pause");
cin.ignore();
cout << "Enter name to look for: ";
string check;
getline(cin, check);
ifstream checkList("product.csv");
string line;
char comma;
while (getline(checkList, line)) {
istringstream iss(line);
if (getline(iss, myProduct.name, ',') && (iss >> myProduct.price >> comma >> myProduct.id) && (comma == ',')) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
或者:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <limits>
#include <cstdlib>
using namespace std;
struct product{
string name;
int price, id;
};
istream& getInt(istream &in, int &value)
{
string str;
if (getline(in, str, ',')) {
try {
value = stoi(str);
}
catch (...) {
in.setstate(ios::failbit);
}
}
return in;
}
int main() {
product myProduct;
cout << "Enter product name: ";
getline(cin, myProduct.name);
cout << "Enter product price: ";
cin >> myProduct.price;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Enter product id: ";
cin >> myProduct.id;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
ofstream productList("product.csv", ios::app);
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << std::endl;
productList.close();
cout << "----" << endl;
system("pause");
cin.ignore();
cout << "Enter name to look for: ";
string check;
getline(cin, check);
ifstream checkList("product.csv");
string line;
while (getline(checkList, line)) {
istringstream iss(line);
if (getline(iss, myProduct.name, ',') && getInt(iss, myProduct.price) && getInt(iss, myProduct.id)) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
我想提出一个额外的解决方案,一个“更”现代的 C++ 和面向对象的解决方案。并且,一个完整的工作解决方案。
在 C++ 中,我们通常将数据和操作该数据的函数放入一个对象 class 中。 class 并且只有 class 应该知道如何处理它的数据。
因此在您的示例中,“id”、"name" 和“price”是对象的属性,从 std::istream
中提取它们或将它们插入 std::ostream
应该只有那个对象知道并由它处理。
因此,我们创建了一个 class/struct,定义了 3 个数据成员,即 "id"、“name” 和 "price" 作为一个 unsigned long",
std::字符串and the “price” as a
双````。然后,我们覆盖提取器运算符 (>>) 和插入器运算符。提取器运算符有点棘手,因为我们首先读取完整的一行,然后将其拆分为标记。
在 C++ 中,您的文件结构称为 CSV(逗号分隔值)。而且,阅读这是一项标准任务。首先,阅读完整的行,然后使用给定的定界符提取该字符串的标记。 “拆分”也称为“标记化”,C++ 具有用于该目的的专用功能:std::sregex_token_iterator
。
这个东西是一个迭代器。用于遍历字符串,因此是“sregex”。 begin 部分定义了我们要对什么范围的输入进行操作,然后有一个 std::regex 表示输入字符串中应该匹配/或不应该匹配的内容。匹配策略的类型由最后一个参数给出。
1 --> give me the stuff that I defined in the regex and
-1 --> give me that what is NOT matched based on the regex.
我们可以使用此迭代器将标记存储在 std::vector 中。 std::vector有一个范围构造函数,它以2个迭代器为参数,将第一个迭代器和第二个迭代器之间的数据复制到std::vector。语句
std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
将变量“tokens”定义为 std::vector
并使用 std::vector
的所谓范围构造函数。请注意:我使用的是 C++17,并且可以在没有模板参数的情况下定义 std::vector
。编译器可以从给定的函数参数中推断出参数。此功能称为 CTAD ("class template argument deduction")。
此外,您可以看到我没有明确使用 "end()" 迭代器。
此迭代器将从具有正确类型的空大括号括起来的初始值设定项列表构造,因为由于 std::vector
构造函数需要.
然后,在读取该行并将其拆分为标记后,我们检查是否恰好有 3 个标记,然后将结果存储为 "id"、“名称”和“价格”。所以,总的来说,一个非常简单的操作。
在 main
我放了一些示例驱动程序代码。例如,您可以看到我使用简单的
将新产品存储到文件中
productFile << product;
声明。读取完整的 CSV 文件并对其进行解析,只需一行代码即可完成:
std::vector productList(std::istream_iterator<Product>(productFile), {});
找元素就是用std::find_if
.
请看下面的完整示例:
(我放了很多注释和空行以使代码更易读。)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <fstream>
#include <regex>
const std::string delimiter(",");
const std::regex re(delimiter.c_str());
struct Product {
// Data
unsigned long id{};
std::string name{};
double price{};
// Member Functions
// Simple Inserter.
friend std::ostream& operator << (std::ostream& os, const Product& product) {
// Unfortunately the ostream_joiner does not work on my system . . .
return os << product.id << delimiter << product.name << delimiter << product.price << "\n";
}
// simple Extractor
friend std::istream& operator >> (std::istream& is, Product& product) {
// Read a complete line
if (std::string line{}; std::getline(is,line)) {
// Convert line into tokens. Split any number of csv columns. One-liner
std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
// if we have read 3 tokens as expected
if (3 == tokens.size()) {
// Assign values to data member
product.id = std::strtoul(static_cast<std::string>(tokens[0]).c_str(), nullptr, 10);
product.name = tokens[1];
product.price = std::strtod(static_cast<std::string>(tokens[2]).c_str(), nullptr);
}
}
return is;
}
};
int main() {
const std::string filename{ "r:\product.csv" };
// First, we ask the user to enter product data
std::cout << "\nEnter product ID, product name and product price in one line separated by comma:\n";
// Get input
if (Product product; std::cin >> product) {
// Save the user data in a file
if (std::ofstream productFile(filename,std::ios::app); productFile) {
// Write to output file stream
productFile << product;
}
else {
std::cerr << "\n\n***Could not write to file\n\n";
}
}
// Now test the search function
// Get the name to look for
std::cout << "\n\nEnter a name to look for:\n ";
if (std::string name{}; std::getline(std::cin, name)) {
// Open the file for reading
if (std::ifstream productFile(filename); productFile) {
// Read the complete file. One-liner
std::vector productList(std::istream_iterator<Product>(productFile), {});
// Search for the name
auto result = std::find_if(productList.begin(), productList.end(), [&name](const Product& p) { return p.name == name; });
// If found, print result
if (result != productList.end())
std::cout << "Found: " << *result <<"\n\n";
else
std::cout << "Name '" << name <<"' not found\n\n";
// For the fun of it: Show all data
std::copy(productList.begin(), productList.end(), std::ostream_iterator<Product>(std::cout));
}
else {
std::cerr << "\n\n***Could not read from file\n\n";
}
}
return 0;
}
我希望这能给你一个想法,如何解决这样的问题。当然还有很多其他的解决方案。 . .
我写了一个在文件中搜索字符串的代码。实际上,我把我的数据放在我的文件中是这样的:
alex booth,15,1
(全名 space、年龄、身份证)。
在文件中保存数据没有任何问题,我在该文件中搜索字符串时遇到了一个大问题。
例如,我想在文件中搜索像 "alex booth" 这样的名称,但似乎搜索无法找到该词,尽管该词可用。我猜想用 space 保存名称有问题,但我不知道我应该怎么做才能修复它。事实上,我不知道我们如何才能搜索其中包含空白 space 的名称。
这是我的代码:
using namespace std;
struct product{
string name;
int price, id;
};
int main() {
product myProduct;
ofstream productList("product.csv", ios::app | ios::out);
getline(cin, myProduct.name);
cin >> myProduct.price;
cin >> myProduct.id;
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << endl;
productList.close();
cout << "----" << endl;
system("pause");
ifstream checkList("product.csv");
string dummy;
string check;
cin.ignore();
getline(cin, check);
while (!checkList.eof()) {
while (checkList >> myProduct.name >> myProduct.price >> myProduct.id) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
当您从文件中读取行时,您根本没有考虑逗号,然后 operator>>
在您的内部 while
循环中失败。
还有,your outer while
loop is wrong,也是。
尝试更像这样的东西:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <limits>
#include <cstdlib>
using namespace std;
struct product{
string name;
int price, id;
};
int main() {
product myProduct;
cout << "Enter product name: ";
getline(cin, myProduct.name);
cout << "Enter product price: ";
cin >> myProduct.price;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Enter product id: ";
cin >> myProduct.id;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
ofstream productList("product.csv", ios::app);
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << std::endl;
productList.close();
cout << "----" << endl;
system("pause");
cin.ignore();
cout << "Enter name to look for: ";
string check;
getline(cin, check);
ifstream checkList("product.csv");
string line;
char comma;
while (getline(checkList, line)) {
istringstream iss(line);
if (getline(iss, myProduct.name, ',') && (iss >> myProduct.price >> comma >> myProduct.id) && (comma == ',')) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
或者:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <limits>
#include <cstdlib>
using namespace std;
struct product{
string name;
int price, id;
};
istream& getInt(istream &in, int &value)
{
string str;
if (getline(in, str, ',')) {
try {
value = stoi(str);
}
catch (...) {
in.setstate(ios::failbit);
}
}
return in;
}
int main() {
product myProduct;
cout << "Enter product name: ";
getline(cin, myProduct.name);
cout << "Enter product price: ";
cin >> myProduct.price;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Enter product id: ";
cin >> myProduct.id;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
ofstream productList("product.csv", ios::app);
productList << myProduct.name << ',' << myProduct.price << ',' << myProduct.id << std::endl;
productList.close();
cout << "----" << endl;
system("pause");
cin.ignore();
cout << "Enter name to look for: ";
string check;
getline(cin, check);
ifstream checkList("product.csv");
string line;
while (getline(checkList, line)) {
istringstream iss(line);
if (getline(iss, myProduct.name, ',') && getInt(iss, myProduct.price) && getInt(iss, myProduct.id)) {
if (myProduct.name == check) {
cout << "Product Name: " << myProduct.name <<
" - Product's Price: " << myProduct.price <<
" - ID Number: " << myProduct.id << endl;
}
}
}
checkList.close();
return 0;
}
我想提出一个额外的解决方案,一个“更”现代的 C++ 和面向对象的解决方案。并且,一个完整的工作解决方案。
在 C++ 中,我们通常将数据和操作该数据的函数放入一个对象 class 中。 class 并且只有 class 应该知道如何处理它的数据。
因此在您的示例中,“id”、"name" 和“price”是对象的属性,从 std::istream
中提取它们或将它们插入 std::ostream
应该只有那个对象知道并由它处理。
因此,我们创建了一个 class/struct,定义了 3 个数据成员,即 "id"、“name” 和 "price" 作为一个 unsigned long",
std::字符串and the “price” as a
双````。然后,我们覆盖提取器运算符 (>>) 和插入器运算符。提取器运算符有点棘手,因为我们首先读取完整的一行,然后将其拆分为标记。
在 C++ 中,您的文件结构称为 CSV(逗号分隔值)。而且,阅读这是一项标准任务。首先,阅读完整的行,然后使用给定的定界符提取该字符串的标记。 “拆分”也称为“标记化”,C++ 具有用于该目的的专用功能:std::sregex_token_iterator
。
这个东西是一个迭代器。用于遍历字符串,因此是“sregex”。 begin 部分定义了我们要对什么范围的输入进行操作,然后有一个 std::regex 表示输入字符串中应该匹配/或不应该匹配的内容。匹配策略的类型由最后一个参数给出。
1 --> give me the stuff that I defined in the regex and
-1 --> give me that what is NOT matched based on the regex.
我们可以使用此迭代器将标记存储在 std::vector 中。 std::vector有一个范围构造函数,它以2个迭代器为参数,将第一个迭代器和第二个迭代器之间的数据复制到std::vector。语句
std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
将变量“tokens”定义为 std::vector
并使用 std::vector
的所谓范围构造函数。请注意:我使用的是 C++17,并且可以在没有模板参数的情况下定义 std::vector
。编译器可以从给定的函数参数中推断出参数。此功能称为 CTAD ("class template argument deduction")。
此外,您可以看到我没有明确使用 "end()" 迭代器。
此迭代器将从具有正确类型的空大括号括起来的初始值设定项列表构造,因为由于 std::vector
构造函数需要.
然后,在读取该行并将其拆分为标记后,我们检查是否恰好有 3 个标记,然后将结果存储为 "id"、“名称”和“价格”。所以,总的来说,一个非常简单的操作。
在 main
我放了一些示例驱动程序代码。例如,您可以看到我使用简单的
productFile << product;
声明。读取完整的 CSV 文件并对其进行解析,只需一行代码即可完成:
std::vector productList(std::istream_iterator<Product>(productFile), {});
找元素就是用std::find_if
.
请看下面的完整示例: (我放了很多注释和空行以使代码更易读。)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <fstream>
#include <regex>
const std::string delimiter(",");
const std::regex re(delimiter.c_str());
struct Product {
// Data
unsigned long id{};
std::string name{};
double price{};
// Member Functions
// Simple Inserter.
friend std::ostream& operator << (std::ostream& os, const Product& product) {
// Unfortunately the ostream_joiner does not work on my system . . .
return os << product.id << delimiter << product.name << delimiter << product.price << "\n";
}
// simple Extractor
friend std::istream& operator >> (std::istream& is, Product& product) {
// Read a complete line
if (std::string line{}; std::getline(is,line)) {
// Convert line into tokens. Split any number of csv columns. One-liner
std::vector tokens(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
// if we have read 3 tokens as expected
if (3 == tokens.size()) {
// Assign values to data member
product.id = std::strtoul(static_cast<std::string>(tokens[0]).c_str(), nullptr, 10);
product.name = tokens[1];
product.price = std::strtod(static_cast<std::string>(tokens[2]).c_str(), nullptr);
}
}
return is;
}
};
int main() {
const std::string filename{ "r:\product.csv" };
// First, we ask the user to enter product data
std::cout << "\nEnter product ID, product name and product price in one line separated by comma:\n";
// Get input
if (Product product; std::cin >> product) {
// Save the user data in a file
if (std::ofstream productFile(filename,std::ios::app); productFile) {
// Write to output file stream
productFile << product;
}
else {
std::cerr << "\n\n***Could not write to file\n\n";
}
}
// Now test the search function
// Get the name to look for
std::cout << "\n\nEnter a name to look for:\n ";
if (std::string name{}; std::getline(std::cin, name)) {
// Open the file for reading
if (std::ifstream productFile(filename); productFile) {
// Read the complete file. One-liner
std::vector productList(std::istream_iterator<Product>(productFile), {});
// Search for the name
auto result = std::find_if(productList.begin(), productList.end(), [&name](const Product& p) { return p.name == name; });
// If found, print result
if (result != productList.end())
std::cout << "Found: " << *result <<"\n\n";
else
std::cout << "Name '" << name <<"' not found\n\n";
// For the fun of it: Show all data
std::copy(productList.begin(), productList.end(), std::ostream_iterator<Product>(std::cout));
}
else {
std::cerr << "\n\n***Could not read from file\n\n";
}
}
return 0;
}
我希望这能给你一个想法,如何解决这样的问题。当然还有很多其他的解决方案。 . .