ファイルがありlist.txt
ます。
約 100 のレコードが含まれていますが、ユーザーがcin
A などの文字の場合、ループ内に A を含むすべてのレコードを表示したいだけです。
レコードは改行形式で記録されます。シェル コマンドでは A* を使用しますが、C++ ではどのように行うのでしょうか。
例:
Alfred
Alpha
Augustine
Bravo
Charlie
Delta
ファイルがありlist.txt
ます。
約 100 のレコードが含まれていますが、ユーザーがcin
A などの文字の場合、ループ内に A を含むすべてのレコードを表示したいだけです。
レコードは改行形式で記録されます。シェル コマンドでは A* を使用しますが、C++ ではどのように行うのでしょうか。
例:
Alfred
Alpha
Augustine
Bravo
Charlie
Delta
方法はたくさんありますので、お好きな方法をお選びください ;)
文字列とストリームを使用したくだらないソリューション:
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'
C++11 を使用している場合はfor
、この範囲ベースの for に置き換えることができます。
for( std::string & s : vec )
if(s.at(0) == 'a')
std::cout << s << std::endl;
または、物事をさらに複雑にして、c ++ 11 のfor
withstd::copy_if
と lambda を置き換えることもできます (IMO は複雑すぎて、このような単純な場合には読みにくいですが、それでも含めます)。
//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'; } );
文字列をどこにも保存する必要がない場合は、次のほうが簡単です。
std::vector< std::string > vec;
std::ifstream ifs("test.txt");
std::string tmp;
while( ifs >> tmp )
if( tmp.at(0) == 'a' )
std::cout << tmp;
文字列のストリームを使用しない、より古い学校のソリューション:
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
);
}
copy_if
およびラムダには C++11 が必要であることに注意してください。これにより、ファイル内のすべての名前が、入力された文字の大文字/小文字で始まり、スペースで区切られて出力されます。データがソートされていない場合と同様に、データがソートされている場合も同じように実行されます。
ただし、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: 必要に応じてラムダを変更します。
[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 を使用することをお勧めします。「見せる」の意味を少し詳しく説明していただけますか?