cin, multiple entries to recognize

I am supposed to wrtie personal data of n students into a file.

How do I make the whole command cin>>surname>>name>>age>>placeofbirth>>telephonenumber recognize whether surname is made of letters or not, age is an integer etc.?

All of them are declared as intended, I tried putting it into an if statement but it does not help.

... whether surname is made of letters or not:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# include <iostream>
# include <string>
# include <cctype>
# include <algorithm>
# include <iomanip>

bool checkSurname (const std::string& surname)
{
    return std::find_if(surname.cbegin(), surname.cend(), [](const char c){return !std::isalpha(c);}) == surname.cend();
}

int main()
{
    std::cout << std::boolalpha << checkSurname(std::string("Smith")) << "\n"; // prints 'true'
    std::cout << std::boolalpha << checkSurname(std::string ("Smith1.0")) << "\n"; // prints 'false'

}


... age is an integer etc.?

//http://www.cplusplus.com/forum/beginner/206234/
//http://www.cplusplus.com/forum/beginner/211726/
Can you explain how does your bool function works?
Last edited on
each character of the string is checked whether it's not isalpha() and for valid surnames all characters are alpha (i.e. letters) and hence we reach one past the end of the string i.e. surname.cend()
specifically look up: std::find_if and how a lambda can be used with it
If you could only tell me...

Now those strings don't recognise the whitespaces, I know, I did not make my request precise but ignoring the spaces wasn't the point, it now misses the whole point of using strings.

How do I fix this?
Here, i used a simpler code, which can be easily understood.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <iomanip>

using namespace std;
int checkL(string a)
{
  for(int i=0;i<a.size();i++)
    if(isalpha(a[i])==0)
      return 0;
  return 1;
}
int checkN(string a)
{
  for(int i=0;i<a.size();i++)
    if(isdigit(a[i])==0)
      return 0;
  return 1;
}
int main()
{
string surname,name,age,placeofbirth,telephonenumber;
cin>>surname>>name>>age>>placeofbirth>>telephonenumber;
if(checkL(surname)==0)
  cout<<"Invalid surname"<<endl; //made only out of letters
if(checkL(name)==0)
  cout<<"Invalid name!"<<endl; //made only out of letters
if(checkN(age)==0)
  cout<<"Invalid age"<<endl; //made only out of nums
if(checkL(placeofbirth)==0)
cout<<"Invalid placeofbirth"<<endl;//made only out of letters
if(checkN(telephonenumber)==0 && telephonenumber.size()!=10)
cout<<"Invalid phone number"<<endl //is made of numbers nd isn't longer or less than 10 digits
  return 0;
}

You are welcome!
Your requirements appear to suggest that the input data is fixed-type. You only need supply the correct type for cin >> to work.

1
2
3
4
5
6
7
8
9
  string surname;
  string name;
  int age;
  string placeofbirth;
  int telephonenumber;

  ...

  cin >> surname >> name >> age >> placeofbirth >> telephonenumber;

Now, here's your reported problem: whitespace. A person's name(s) and the place of birth may have whitespace in it. This requires you to do some more careful parsing.

In fact, this leads us to one of the most obnoxious parts of I/O:

    Input is always harder than output!

I recommend you read a line (an entire record) as a string, then parse out the individual components of the string. This will make your life significantly easier.

Continuing, I'll assume a line of input that looks like this:

    De Sola, Juán Omar 22 A Coruña 777-123-456

The trick is to notice where the type of thing changes:
  1. Surnames terminate with a comma (you may generally require that.
     If your professor disagrees, ask him how to separate family and given names.)
  2. Age is composed of numeric digits.
  3. A city/province/state/country is not composed of any digits
     -OR- it is not the phone number.
  4. Phone number is composed of digits and optional + and - signs.

Let's split that up, then.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct PersonalData
{
  std::string        surname;
  std::string        given_names;
  unsigned           age;
  std::string        place_of_birth;
  unsigned long long phone_number;
};

std::istream& operator >> ( std::istream& ins, PersonalData& pdata )
{
  std::string s;
  if (!getline( ins, s )) return ins;

  // Find the important breaks in the string
  auto index_end_of_surname    = s.find( ',' );
  auto index_first_age_digit   = s.find_first_of( "0123456789" );
  auto index_end_of_age        = s.find( ' ', index_first_age_digit );
  auto index_first_phone_digit = s.find_last_not_of( "0123456789+- " ) + 1;

  // Break out the pieces
  pdata.surname        = trim( s.substr( 0, index_end_of_surname ) );
  pdata.given_names    = trim( s.substr( index_end_of_surname + 1, index_first_age_digit - index_end_of_surname - 1 ) );
  pdata.age            = string_to<decltype(pdata.age)>( s.substr( index_first_age_digit, index_end_of_age - index_first_age_digit ), 0 );
  pdata.place_of_birth = trim( s.substr( index_end_of_age, index_first_phone_digit - index_end_of_age ) );
  pdata.phone_number   = string_to<decltype(pdata.phone_number)>( strip_not_of( s.substr( index_first_phone_digit ), "0123456789" ), 0 );

  return ins;
}

You'll notice that a lot of helper routines are used.
trim: http://www.cplusplus.com/faq/sequences/strings/trim/
string_to:
1
2
3
4
5
6
7
8
template <typename T>
T string_to( const std::string& s, const T& default_value = T() )
{
  T result;
  std::istringstream ss( s );
  ss >> result >> std::ws;
  return ss.eof() ? result : default_value;
}
strip_not_of: you can create your own, or store the phone number as a string, etc.

Here's the code I used to test:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
std::ostream& operator << ( std::ostream& outs, const PersonalData& pdata )
{
  return outs << pdata.surname << ", " << pdata.given_names << " " << pdata.age << " " << pdata.place_of_birth << " " << pdata.phone_number;
}

int main()
{
  std::istringstream input
  ( 
    "De Sola, Juán Omar 22 A Coruña 777-123-456\n" 
    "Solíz, Mario 35 Seville +34 777 654 321"
  );
  
  std::vector <PersonalData> student_personal_data;
  {
    PersonalData pd;
    while (input >> pd)
      student_personal_data.emplace_back( pd );
  }
  
  for (auto pd : student_personal_data)
    std::cout << pd << "\n";
}
De Sola, Juán Omar 22 A Coruña 777123456
Solíz, Mario 35 Seville 34777654321

I know this is a lot to take in, and I've probably written more than needed, but hopefully it will help.

Notice also that I took a few liberties with error handling: basically ignoring errors and returning a record anyway.

Hope this helps.
All of you are extremely helpful, thank you.

I would like to know, however, whether I could use gunnerfunner's method on integers.

I want to force my program to output error after inputting for example "1letters". With if(cin>>n) it recognizes numbers up to a point where other symbols begin.

I can't use a string for it as I propably could with telephone number. It is later required from me to output an average of all the student's ages and I can't divide number by a string.
The best way to determine whether something "is a" is to try to convert it. If the conversion fails, it is not.
Thank you, I used your method and it worked.

That being said, I still needed to download and overwrite some files to get the stoi function, worth it though.
That is both a "strength" and a "drawback" to the design of C and C++.

    - It is a strength because there's a library to do everything.

    - It is a drawback because you need a library to do anything.

The trick is finding the right libraries for your projects. If you haven't got Boost installed, I recommend you take the time to do it. It has a lot of stuff to solve common headaches.

Hope this helps.
Topic archived. No new replies allowed.