我有一个list.txt
文件。
它包含大约 100 条记录,但如果用户cin
是一个字母,例如 A,我只想在循环中显示所有包含 A 的记录。
记录是以换行格式记录的,在shell命令中我们使用A*,但是在C++中,我们怎么做呢?
例子:
Alfred
Alpha
Augustine
Bravo
Charlie
Delta
我有一个list.txt
文件。
它包含大约 100 条记录,但如果用户cin
是一个字母,例如 A,我只想在循环中显示所有包含 A 的记录。
记录是以换行格式记录的,在shell命令中我们使用A*,但是在C++中,我们怎么做呢?
例子:
Alfred
Alpha
Augustine
Bravo
Charlie
Delta
Here's a bunch of ways to do it, chose the one you like more ;)
Crappy solution with strings and streams:
std::vector< std::string > vec;//this will hod the file data
std::ifstream ifs("test.txt");//the input file stream
std::string tmp;//a temporary string
while( ifs >> tmp )//reading the whole data from the file
vec.push_back(tmp);
for( int i = 0; i < vec.size(); i++ )
if(vec[i][0] == 'a')//vec[i][0] stands for "the first symbol of element number i in vector"
std::cout << vec[i] << std::endl;//outputting the string if it starts with 'a'
If you have c++11, you can replace the for
with this range-based for:
for( std::string & s : vec )
if(s.at(0) == 'a')
std::cout << s << std::endl;
Or, you can complicate things further and replace the for
with std::copy_if
and lambdas from c++11 (IMO it's much too complicated and hard to read for such a simple occasion, but still I'll include it):
//this will copy all strings starting with 'a' into res vector.
std::vector< std::string > res;
std::copy_if(vec.begin(), vec.end(), back_inserter(res), [](const std::string & s){ return s[0]=='a'; } );
If you don't need to store the strings anywhere, it's easier:
std::vector< std::string > vec;
std::ifstream ifs("test.txt");
std::string tmp;
while( ifs >> tmp )
if( tmp.at(0) == 'a' )
std::cout << tmp;
A more old-school solution without using streams of strings:
FILE * f = fopen("test.txt", "r");//opening the file
if( !f )//checking in case it didn't open
return -1;
char buffer[255];//buffer for the strings being read from file
while( !feof(f) )
{
fgets(buffer, 255, f);//getting a string
if(buffer[0] == 'a')//printing if it starts with 'a'
printf("%s", buffer);
}
fclose(f);//don't forget to close the file
这是一个相当优雅,可能更惯用的解决方案:
#include <algorithm> //for copy_if
#include <cctype> //for tolower
#include <fstream> //for ifstream
#include <iostream> //for cout, cin
#include <iterator> //for istream_iterator, ostream_iterator
#include <string> //for string
int main() {
char letter;
std::cout << "Enter the letter to look for: ";
std::cin >> letter; //I didn't validate it
std::ifstream fin ("names.txt");
std::istream_iterator<std::string> ifbegin (fin); //begin file iter
std::istream_iterator<std::string> ifend; //end file iter
std::ostream_iterator<std::string> obegin (std::cout, " "); //begin out iter
std::copy_if (ifbegin, ifend, obegin, //copy from file to output if
[letter] (const std::string &str) { //capture letter
return std::tolower (str [0]) == std::tolower (letter);
} //copy if starts with upper/lower case of entered letter
);
}
请注意,它确实需要 C++11copy_if
和 lambda。这将输出文件中以输入字母的大写/小写开头的每个名称,用空格分隔。它在数据排序时的执行与数据未排序时的执行相同。
但是,正如 Luc 在下面指出的那样,这将读取带有空格的行的单独名称。如果你想解决这个问题,你需要一个operator>>
读取一行的自定义替换。
第 1 步:创建替换:
struct Line {
std::string text; //note I made this public to save time
operator std::string() const {return text;} //less work later
};
第 2 步:修改operator>>
以读取结构的一行:
std::istream &operator>> (std::istream &in, Line &line) {
std::getline (in, line.text); //get whole line
return in;
}
第 3 步:更改迭代器以使用我们的自定义结构。请注意,最后一个保留一个字符串,因为它可以隐式转换为一个。让我们也用换行符分隔打印,这样我们就可以知道它是一行,而不是一个单词:
std::istream_iterator<Line> ifbegin (fin); //begin file iter
std::istream_iterator<Line> ifend; //end file iter
std::ostream_iterator<std::string> obegin (std::cout, "\n"); //begin out iter
第 4 步:更改 lambda 以满足我们的需要:
[letter] (const std::string &line) {
return !line.empty() //we introduced the possibility of ""
&& (std::tolower (line [0]) == std::tolower (letter));
}
4个简单的步骤后,我们就完成了!
如果您确实需要新列表,我建议您使用 remove_copy_if 或 for_each 使用检查谓词的 lambda 表达式(如果您不需要)。你能详细说明一下你所说的“表演”是什么意思吗?