web-dev-qa-db-ja.com

C ++でgetlineコマンドを使用する方法は?

C++でcoutコマンドをgetlineコマンドに変換しようとしています。

これは私が変更しようとしている私のコードです...

for (int count=0; count < numberOfEmployees; count++)
    {
        cout << "Name: ";
        cin >> employees[count].name; 

        cout << "Title: ";
        cin >> employees[count].title;

        cout << "SSNum: ";
        cin >> employees[count].SSNum;

        cout << "Salary: ";
        cin >> employees[count].Salary;

        cout << "Withholding Exemptions: ";
        cin >> employees[count].Withholding_Exemptions; 
    }

私はこの行を変更しようとしています:cin >> employees[count].name;およびこの行:cin >> employees[count].title;getlinesに。誰か助けてもらえますか?

ありがとう

6
Gvegas222

C++でのcin.getline()のフラッシュの問題

C++の入力ストリームから無関係な文字を削除したい場合、それは通常、フォーマットされた入力メソッドとフォーマットされていない入力メソッドが混在しているためです。フォーマットされたメソッドはストリームに改行を残し、フォーマットされていないメソッドはそれを消費して正常に終了しますが、必要なことを完全に実行できません。

    #include <iostream>  
 int main() {   
std::cout<<"Enter the letter A: ";  
 std::cin.get();   
std::cout<<"Enter the letter B: "; 
  std::cin.get();   
std::cout<<"Too late, you can't type anymore\n";
 }

多くの場合、この質問は、プログラムが終了する前にプログラムを一時停止する方法である別の質問から生じます。 cin.get()の使用は、ストリームに文字が残っていない場合にのみ機能します。 cin >> fooを投げた瞬間。コードでは、突然ソリューションが失敗します。ストリームが再び機能する前に、ストリームから残っている文字をすべて削除する必要があります。

では、どのように問題を解決しますか?良いニュースは、うるさくなりたくない限り、ループのように単純なことです。C++構文(プレーンテキストの切り替え)

    #include <istream>  
 void ignore_line ( std::istream& in ) { 
  char ch;
        while ( in.get ( ch ) && ch != '\n' );
 } 

このループは、ファイルの終わりまたは改行が読み取られるまで文字を読み取るだけです。一般に、C++の対話型入力は行指向であり、改行を読み取った後はクリーンなバッファーがあることが保証されていると想定されています。それは真実ではありませんが(入力は行指向である必要はありません)、このスレッドの目的のためにそれを想定できるほど十分に広く普及しています。

では、このアプローチの何が問題になっていますか?何もありません。実際、これは、掘り下げて微妙な問題を修正したい場合を除いて、ほぼ同じくらい良いものです。しかし、問題を検討する前に、同じことを別の方法で行う別の解決策を次に示します。

    #include <ios>
    #include <istream>
    #include <limits>   
void ignore_line ( std::istream& in ) {   
in.ignore ( std::numeric_limits<std::streamsize>::max(), '\n' );
 } 

Std :: istreamのメンバー無視関数は、最大N文字を読み取り、区切り文字まで破棄します。上記の例では、Nはstreamsizeデータ型の最大値で表され、区切り文字は改行です。大きな値(80が一般的)でも同様に機能します。C++構文(プレーンテキストの切り替え)in.ignore(80、 '\ n');ただし、streamsizeデータ型は、ストリームが使用しているバッファーのサイズを正確に表す可能性が高く、常に機能する可能性が高くなります。これが私がお勧めするソリューションです。

では、これの何が問題になっていますか? 2つの注目すべき問題があります。 1つ目は修正が簡単で、istreamがあまり柔軟ではないという事実に起因しています。 istreamは、実際にはbasic_istreamのtypedefです。ワイドストリームをignore_lineで機能させたい場合は、istreamでSOLです。したがって、代わりにbasic_istream <>を使用するのがコツです。

    #include <ios>
    #include <istream>
    #include <limits>   
template <typename CharT> 
void ignore_line ( std::basic_istream<CharT>& in ) { 
  in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) ); 
}

現在、ignore_lineは、ストリームに含まれる文字のタイプを最初の引数から導出するテンプレート関数です。 basic_istreamから派生した、またはbasic_istreamに特化した任意のストリームを渡すことができ、問題はなくなりました。 '\ n'だけでなく、リテラルでwidenを使用して、必要に応じてより広い型に適切に変換されるようにすることをお勧めします。素晴らしくて簡単。

2番目の問題はもっと難しいです。はるかに難しい。標準のiostreamは、移植性の欠如や望ましくない機能により、あらゆる場面で邪魔をしているように見えるため、より困難です。実際、問題を完全に修正することは不可能です。問題は、ストリームの内容によって動作が異なることです。例えば:

    #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits>  
 template <typename CharT> 
void ignore_line ( std::basic_istream<CharT>& in ) { 
  in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );
 }  
 int main() {  
 std::cout<<"First input: "; 
  std::cin.get();  
 std::cout<<"Clearing cin.\n"; 
  std::cin.clear();   
ignore_line ( std::cin ); 
  std::cout<<"All done.\n"; 
} 

このプログラムを3回実行します。

入力: "asdf"出力:プログラムは、ユーザーからの入力なしで終了します。

入力:Enterキーを押すだけ出力:プログラムは、Enterキーをもう一度押すのを待ちます。

入力:シグナルEOF出力:プログラムは、Enterキーをもう一度押すのを待ちます。

問題は、ストリームが空であるということです。すぐにEnterキーを押すと、改行がストリームに配置され、cin.getによって消費されます。同様に、EOFのシグナリングについても同様です。その時点では、ストリームには何も残っておらず、cin.ignoreは、さらに文字を入力するまですべてを停止します。これは、cin.ignoreがブロッキング読み取りであるためです。読むものがない場合は待機します。

私たちが望んでいるのは、これら3つのケースのいずれに対してもブロックしないことです。良いニュースは、iostreamライブラリがいくつかの可能な解決策をサポートしていることです。悪いニュースは、これらが行き止まりであるということです。一般的なものは次の2つです。

同期メンバー関数istreamクラスは、syncと呼ばれるメンバー関数をサポートします。なぜそれがそのような機能を持っているのかは議論されています。なぜなら、それが何をすべきかについて誰も同意できないからです。 Bjarne Stroustrup自身でさえ、ストリーム内のすべての文字を破棄すると誤って述べています。

  #include <iostream>  
 int main() {   
std::cout<<"First input: ";  
 std::cin.get();   
std::cout<<"Clearing cin.\n";  
 std::cin.clear();   
std::cin.sync();   
std::cout<<"All done.\n"; 
} 

これが機能するとき、それは美しく機能します。悪いニュースは、C++標準では、無関係な文字を破棄するようなことを行うために同期を必要としないことです。このソリューションは移植性がありません。

In_availメンバー関数次のステップは、istreamのストリームバッファーのin_availメンバー関数を確認することです。一見すると、このメンバー関数はストリーム内の文字数を示しているように見えます。0が返された場合は、ignoreを呼び出さないでください。

  #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits>  
 template <typename CharT> 
void ignore_line ( std::basic_istream<CharT>& in ) {
   if ( in.rdbuf()->in_avail() > 0 )
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );
 }  
 int main() { 
  std::cout<<"First input: "; 
  std::cin.get();   
std::cout<<"Clearing cin.\n"; 
  std::cin.clear();  
 ignore_line ( std::cin ); 
  std::cout<<"All done.\n";
 }

同期と同様に、これが機能すると、うまく機能します。しかし、繰り返しになりますが、標準では、ストリーム内の文字を正確に表現するためにin_availは必要ないということで、壁が高くなっています。実際、いくつかの一般的な実装には、常に0を返す厳密に準拠したin_availがあります。あまり役に立ちません。今、私たちは創造的にならなければなりません。

プットバックメンバー関数

 #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits> 
  template <typename CharT>
 void ignore_line
 ( std::basic_istream<CharT>& in ) { 
  if ( !in.putback ( in.widen ( '\n' ) ) )
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );   else
        in.ignore(); }   
int main() 
{   std::cout<<"First input: ";   
std::cin.get();   
std::cout<<"Clearing cin.\n"; 
  std::cin.clear();  
 ignore_line ( std::cin );  
 std::cout<<"All done.\n";
 } 

一見すると、改行をプッシュバックしようと試みることができるように見えるので、これは非常に有望に見えます。操作が失敗した場合、最後に読み取られた文字は改行ではなく、ブロックせずにignoreを自由に呼び出すことができます。操作が成功すると、改行が元に戻り、1文字無視して削除できます。

悲しいことに、それは機能しません。プットバックは、これを予測どおりに行う必要はありません。そのため、なぜそれが利用可能であるのかという疑問が生じます。

しかし、プットバックは実際には、ほとんどの場合に機能するのに十分妥当と思われる解決策に近づきます。失敗するかどうかをプットバックに依存する代わりに、ストリームのバッファーのsungetcメンバー関数を使用して、最後に読み取られた文字が確実に戻されるようにすることができます。秘訣は、最後の文字を取得してから、もう一度読み、改行に対してテストすることです。

  #include <iostream>
    #include <ios>
    #include <istream>
    #include <limits>  
 template <typename CharT>
 void ignore_line ( std::basic_istream<CharT>& in ) { 
  if ( in.rdbuf()->sungetc() != std::char_traits<CharT>::eof()
        && in.get() != in.widen ( '\n' ) )   {
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );  
 } 
}   
int main() {   
std::cout<<"First input: ";  
 std::cin.get();   
std::cout<<"Clearing cin.\n";   
std::cin.clear();   
ignore_line ( std::cin );   
std::cout<<"All done.\n";
 }

Istreamのungetの代わりにsungetcを使用する理由は、ungetがストリームを返すのに対し、sungetcはプッシュバックされた文字またはEOFのいずれかを返すためです。このようにして、関数が失敗したかどうかを簡単に判断できます。

Sungetcが失敗した場合、次のいずれかが当てはまります。

1)ストリームがエラー状態です。 2)忘れるキャラクターはありません。 3)ストリームは文字の取得をサポートしていません。

Sungetcが成功した場合、改行に対して読み取ってテストする文字が常に存在します。その文字が改行と一致する場合、最後に読み取られた文字も改行であり、ignoreを呼び出す必要はありません。文字が一致しない場合は、全行が読み取られていないため、ブロックせずに無視を安全に呼び出すことができます。

ストリームがエラー状態にある場合、それは呼び出し元のコードが処理する必要があるものです。取得する文字がない場合、それはまさにこのソリューションが適切に処理するように設計されているものです。ただし、ストリームが文字の取得をサポートしていない場合、それは問題です。 ignore_line関数は常に文字の破棄に失敗するため、文字の取得をサポートしない実装の場合は、無視を強制するフラグを追加できます。無視された文字の数も知っておくと便利な場合があるので、それも追加して、最終的な解決策を見つけましょう。

   #include <ios>
    #include <istream>
    #include <limits>   
template <typename CharT> 
std::streamsize ignore_line (   std::basic_istream<CharT>& in, bool always_discard = false ) { 
  std::streamsize nread = 0;
        if ( always_discard || ( in.rdbuf()->sungetc() != std::char_traits<CharT>::eof()
        && in.get() != in.widen ( '\n' ) ) )  
 {
        // The stream is good, and we haven't
        // read a full line yet, so clear it out
        in.ignore ( std::numeric_limits<std::streamsize>::max(), in.widen ( '\n' ) );
        nread = in.gcount();   }
        return nread; 
}

念のため、ignore_lineを呼び出すマニピュレータと、ignore_lineを使用してプログラムを一時停止するマニピュレータも含めます。そうすれば、洗浄されていない大衆はシステムの使用をやめることができます(「一時停止」)。およびgetch();:

  class ignoreline { 
  bool _always_discard;
   mutable std::streamsize _nread; 
public:  
 ignoreline ( bool always_discard = false )
        : _always_discard ( always_discard ), _nread ( 0 )   {}
        std::streamsize gcount() const { return _nread;
 }
        template <typename CharT>  
 friend std::basic_istream<CharT>& operator>> (        std::basic_istream<CharT>& in, const ignoreline& manip )  
 {
        manip._nread = ignore_line ( in, manip._always_discard );
        return in;  
 } 
};  
 class pause { 
  ignoreline _ignore; 
public:   
pause ( bool always_discard = false )        : _ignore ( always_discard )   {}
        std::streamsize gcount() 
const { return _ignore.gcount(); 
}
        template <typename CharT> 
  friend std::basic_istream<CharT>& operator>> (        std::basic_istream<CharT>& in, const pause& manip )   
{
        if ( !( in>> manip._ignore ) )
          in.clear();

        std::cout<<"Press Enter to continue . . .";

        return in.ignore();  
 } 
}; 

これで、3つのケースすべてが同じように動作します。

     int main() 
{   std::cout<<"First input: "; 
  std::cin.get();   
std::cout<<"Clearing cin.\n";  
 std::cin>> ignoreline();  
 std::cout<<"All done.\n";  
 std::cin>> pause();
 } 

そして、この話の教訓は次のとおりです。見た目ほど単純ではなく、必要なことを実行するポータブルコードを作成することは非常に困難であり、iostreamライブラリは非常に混乱しています。

注:初心者の場合は、すべてを忘れて、フラッシングの問題があることを理解し、cinを使用してください

9

入力ストリームを最初の引数として受け取るgetline関数を使用できます。プロトタイプは次のようになります:basic_istream<...>& getline(basic_istream<...>& _Istr, basic_string<...>& _Str)

ただし、std::stringを処理していることを考慮し、実際に必要な型(char*intなど)として解析する必要があります。

#include <iostream>
#include <sstream>
#include <string>
using namespace std;
//...

for (int count=0; count < numberOfEmployees; count++)
{
    string name, title, sSSnum, sSalary, s;

    cout << "Name: ";
    getline(cin, name); 
    employees[count].name = name.c_str();

    cout << "Title: ";
    getline(cin, title);
    employees[count].title = name.c_str();

    cout << "SSNum: ";
    getline(cin, sSSnum);
    istringstream is(sSSnum);
    is >> employees[count].SSNum;

    cout << "Salary: ";
    getline(cin, sSalary);
    is.clear();
    is.str(sSalary);
    is >> employees[count].Salary;
    //...
}
1
LihO

getline プロトタイプを見ると、次の2つが必要であることがわかります。

  1. istreamオブジェクト
  2. 結果の文字列
  3. (オプションで)「改行」区切り文字(デフォルトは_'\n'_です。

あなたの場合、区切り文字がデフォルトである可能性があります:_'\n'_;そうでない場合は、各値を区切る文字を指定できます(スペース_' '_など)。 cinストリームはistreamオブジェクトになり、_employees[count]_の各プロパティが2番目のパラメーターとして機能します。要約すると、getline(cin, employees[count].name);でうまくいくはずです。

istream&getline(char * s、streamsize n); istream&getline(char * s、streamsize n、char delim);ストリームからラインを取得

入力シーケンスから文字を抽出し、sで始まる配列にc文字列として格納します。

文字は、(n-1)文字が抽出されるか、区切り文字が見つかるまで抽出されます(このパラメーターが指定されている場合はdelim、それ以外の場合は '\ n')。入力シーケンスでファイルの終わりに達した場合、または入力操作中にエラーが発生した場合も、抽出は停止します。

区切り文字が見つかった場合は、抽出されて破棄されます。つまり、区切り文字は保存されず、その後に次の入力操作が開始されます。この文字を抽出したくない場合は、代わりにmembergetを使用できます。

データが抽出された後、c文字列の終わりを示す終了ヌル文字がsに自動的に追加されます。

この関数によって読み取られる文字数は、メンバー関数gcountを呼び出すことによって取得できます。

同じ名前のグローバル関数がヘッダーに存在します。このグローバル関数は同様の動作を提供しますが、c文字列の代わりに標準のC++文字列オブジェクトを使用します。getline(文字列)を参照してください。

パラメータ■文字列がc文字列として格納されている文字の配列へのポインタ。 n格納する最大文字数(終了ヌル文字を含む)。これは、streamsize型の整数値です。このサイズに達したために関数が読み取りを停止すると、フェイルビット内部フラグが設定されます。 delim区切り文字。この文字を読み取ると、連続する文字を抽出する操作が停止します。このパラメーターはオプションです。指定されていない場合、関数は「\ n」(改行文字)を区切り文字と見なします。

戻り値関数は* thisを返します。

MSDNの例:

// istream getline
#include <iostream>
using namespace std;

int main () {
  char name[256], title[256];

  cout << "Enter your name: ";
  cin.getline (name,256);

  cout << "Enter your favourite movie: ";
  cin.getline (title,256);

  cout << name << "'s favourite movie is " << title;

  return 0;
}

また、構造体オブジェクトの状況では、objectname.datamemberを使用してオブジェクトデータにアクセスする必要があります。

for (int count=0; count < numberOfEmployees; count++)
    {
        cout << "Name: ";
       cin.getline (employees[count].name,100); 

        cout << "Title: ";
        cin.getline (employees[count].title,100); 

        cout << "SSNum: ";
        cin.getline (employees[count].SSNum); 

        cout << "Salary: ";
        cin.getline( employees[count].Salary);

        cout << "Withholding Exemptions: ";
       cin.getline (employees[count].Withholding_Exemptions);
    }
0
for (int count=0; count < numberOfEmployees; count++)
    {
        cout << "Name: ";
       cin.getline (employees[count].name,100); 

        cout << "Title: ";
        cin.getline (employees[count].title,100); 

        cout << "SSNum: ";
       //error cin.getline (employees[count].SSNum); 
       cin>>employees[count].SSNum;

        cout << "Salary: ";
        // error cin.getline( employees[count].Salary);
        cin>>employees[count].Salary;


        cout << "Withholding Exemptions: ";
       //error cin.getline (employees[count].Withholding_Exemptions);
       cin>>employees[count]..Withholding_Exemptions;
    }
0
venkat