Converting a string to multiple integers

I am trying to take an input formatted like 11-5-2008, 01-01-2020 or 1-1-2020 and turn that into three integer values, one for the day, month and year.

I am trying to use a loop to take the first number (or two) and place it into a new integer variable and to break when it finds a hyphen, remove the hyphen and repeat the process two times.

I understand how to output what I want to but I don't know how to store the getchar outputs into an int variable or how to tell it to stop when it recognizes '-'.

I was hoping to use something like this to turn the string into just integers, but I cannot figure out how to get this to work.

1
2
3
4
5
6
7
for (int i = 0; i < strLength; i++)
    {
        if (string1[i] == '-')
        {
            string1.erase(string1[i]+0);
        }
    }


This is basically what I have so far. Not that useful but this is about what I understand to.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    string string1;
    int month;
    int day;
    int year;
    
    cout<<"what date?" << endl;
    getline(cin, string1);
    
    month = stoi(string1);
    
    int strLength = string1.length();

    
    for (int i = strLength; i > (strLength-5); i--)
    {
        string1.erase(string1.end()-1);
    }


I can think of multiple approaches, but don't know how to facilitate any of them.

I can remove the hyphens so it is just a string of numbers but I am not sure how I would turn that into a 10 character string to extract three precise integers from it with single digit months and days.

I can do a loop to add a string to a new string/int and break whenever I see a hyphen. I think this is what was intended.

I can do a sort of combination like my current code. I can stoi the month value really easy and remove the year value. If I could store that year to an integer before removing it then I would be left with 1-1 or 01-01 and I still don't know how to get rid of the first two or three digits and break when the hyphen is found.
Last edited on
This is one way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{
    std::cout << "enter a date in the format day-month-year: " ;

    int day = 0 ;
    int month = 0 ;
    int year = 0 ;
    char sep = 0 ;

    if( std::cin >> day >> sep && sep == '-' && std::cin >> month >> sep && sep == '-' && std::cin >> year )
    {
        // validate that day, month, year have sane values
        // ...
    }

    else { /* invalid input */ }
}
That is exactly what I was trying to do. Thank you!
Also consider std::get_time when used as a stream manipulator.

https://en.cppreference.com/w/cpp/io/manip/get_time
https://en.cppreference.com/w/cpp/io/manip/put_time
https://en.cppreference.com/w/cpp/chrono/c/tm

Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <iomanip>

int main() {
	std::tm t {};

	std::cout << "Enter a date in the format day-month-year: ";

	if (std::cin >> std::get_time(&t, "%d-%m-%Y")) {
		std::cout << std::put_time(&t, "%d %b %Y") << '\n';
		std::cout << t.tm_mday << "  " << t.tm_mon + 1 << "  " << t.tm_year + 1900 << '\n';
	} else
		std::cout << "Invalid input\n";
}


This makes to easy to change the required input format etc to also include a time as well etc.

The elements of tm can be accessed to obtain the individual values.
Note that months start at 0 and years start from 1900.


Enter a date in the format day-month-year: 11-5-2008
11 May 2008
11  5  2008


@lewi0027 - what do you want to do with the date once it has been obtained?
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main()
{
   string datestr;
   cout << "Enter a date (day-month-year): ";   cin >> datestr;
   int d, m, y;
   stringstream( datestr ) >> d >> m >> y;
   m = -m;   y = -y;
   cout << "Day:   " << d << '\n'
        << "Month: " << m << '\n'
        << "Year:  " << y << '\n';  
}
:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

int main() {
	int d {}, m {}, y {};

	std::cout << "Enter a date (day-month-year): ";
	if ((std::cin >> d >> m >> y) && d > 0 && m < 0 && y < 0) {
		m = -m;
		y = -y;

		std::cout << "Day:   " << d << '\n'
			<< "Month: " << m << '\n'
			<< "Year:  " << y << '\n';
	} else
		std::cout << "Bad format\n";
}


without range checking.
Last edited on
> std::cout << "Enter a date (day-month-year): ";

std::cout << "Enter a date (day-month-year, with no white spaces in between): ";

Dealing with input date strings has always been (and will always be) a mess. My advice: do your best, but clearly define fail points and articulate to the user when it does.

1
2
3
4
5
6
7
#include <cctype>
#include <ciso646>
#include <ctime>
#include <iostream>
#include <sstream>
#include <string>
#include <tuple> 
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
auto string_to_day_month_year( const std::string & s ) -> std::tuple <int, int, int>
{
  // Input is guaranteed over ASCII character range (#33..#126) only.
  // Example inputs we will accept:
  //   03142022
  //   3-14-22
  //   3/14/2022
  //   03 14 22
  // The separator character can be anything as long as it is non-alphanumeric.
  // A string should have either ZERO or TWO separator characters.
  // Two-digit years are corrected to four-digit years, wrapping
  // centuries 10 years from TODAY.
  // Leading and trailing whitespace is OK.
  // Returns (day, month, year)

  std::istringstream ins( s );

  auto get = [&ins]( int n ) -> int
  {
    int result = 0;
    while (ins and (n --> 0))
      if (isdigit( ins.peek() ))
        result = result * 10 + ins.get() - '0';
  };

  auto accept_separator = [&ins]() -> void
  {
    if (ins and !isalnum( ins.peek() )) ins.get();
  };

  ins >> std::ws;
  int day   = get( 2 );  accept_separator();
  int month = get( 2 );  accept_separator();
  int year  = get( 4 );
  ins >> std::ws;

  if (!ins.eof()) throw "Invalid date";

  time_t t = time( nullptr );
  struct tm date = *localtime( &t );

  // 2-digit years --> 4-digit years
  if (year < 100)
  {
    int today_year = date.tm_year + 1900;
    date.tm_year = today_year / 100 * 100 + year - 1900;
    if ((year + 10) > (today_year % 10) + 10) date.tm_year -= 100;
  }
  date.tm_mday = day;
  date.tm_mon  = month;

  if (mktime( &date ) == -1) throw "Invalid date";

  return std::make_tuple( day, month, year );
}


[edit] Example of use:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main()
{
  std::cout << "Enter a date (day-month-year): ";
  std:string s;
  getline( std::cin, s );
  try
  {
    auto [day, month, year] = string_to_day_month_year( s );
    std::cout
      << "Day:   " << day << '\n'
      << "Month: " << day << '\n'
      << "Year:  " << day << '\n';
  }
  catch (...)
  {
    std::cout << "Bad input.\n";
  }
}

(I have not tested this code.)
Last edited on
if different date formats are required to be accepted, then multiple 'tries' with different get_time() formats can be used. Eg:

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
35
36
37
38
39
40
41
42
//   03142022
//   3-14-22
//   3/14/2022
//   03 14 22

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>

int main() {
	std::string str;
	std::tm t {};

	std::cout << "Enter a date: ";
	std::getline(std::cin, str);

	std::istringstream iss(str);

	if (!(iss >> std::get_time(&t, "%m-%d-%Y"))) {
		iss.clear();
		iss.str(str);

		if (!(iss >> std::get_time(&t, "%m/%d/%Y"))) {
			iss.clear();
			iss.str(str);

			if (!(iss >> std::get_time(&t, "%m%t%d%t%Y"))) {
				iss.clear();
				iss.str(str);

				if (!(iss >> std::get_time(&t, "%m%d%Y"))) {
					std::cout << "Invalid input\n";
					return 1;
				}
			}
		}
	}

	std::cout << std::put_time(&t, "%d %b %Y") << '\n';
	std::cout << t.tm_mday << "  " << t.tm_mon + 1 << "  " << t.tm_year + 1900 << '\n';
}

Use an array.

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
const char * const ACCEPTABLE_INPUT_DATE_FORMATS[] =
{
  "%m-%d-%Y",
  ...
};

int year{}, month{}, day{};

for (auto fmt : ACCEPTABLE_INPUT_DATE_FORMATS)
{
  struct tm t;
  if (std::istringsteam(str) >> std::get_time( &t, fmt ))
  {
    day   = t.tm_day;
    month = t.tm_mon + 1;
    year  = t.tm_year + 1900;
    break;
  }
}

if (!month) fooey();

std::cout
  << "Day:   " << day   << '\n'
  << "Month: " << month << '\n'
  << "Year:  " << year  << '\n';

Beware that tricky 2-digit year!
Yes - much better. I was in a hurry and just did some copy/pasting with brain in neutral. Before engaging fingers, always enable brain...

Perhaps (without sanity checks...):

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
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <optional>

constexpr const char* DateFrmts[] { "%m-%d-%Y", "%m/%d/%Y", "%m%t%d%t%Y", "%m%d%Y" };

std::optional<tm> getDate(std::string& str) {
	tm t;

	if (str.size() >= 6)
		for (auto fmt : DateFrmts)
			if (std::istringstream(str) >> std::get_time(&t, fmt))
				return std::optional(t);

	return std::nullopt;
}

int main() {
	std::string str;

	std::cout << "Enter a date: ";
	std::getline(std::cin, str);

	if (auto d { getDate(str) }; d) {
		std::cout << std::put_time(&*d, "%d %b %Y") << '\n';
		std::cout << d->tm_mday << "  " << d->tm_mon + 1 << "  " << d->tm_year + 1900 << '\n';
	} else
		std::cout << "Date format not recognised\n";
}



Last edited on
Topic archived. No new replies allowed.