ImANoobPls
ImANoobPls

Reputation: 1

How to obtain certain information from a specified format in c++?

Beginner programmer here, Say I want to obtain initial starting coordinates in the form (x,y), so I ask the user to enter in a point using the specific form "(x,y)". Is there a way that I could recognize the format and parse the string so that I could obtain the x and y values?

Upvotes: 0

Views: 55

Answers (3)

R Sahu
R Sahu

Reputation: 206627

Read a line of text using:

char line[200]; // Make it large enough.
int x;
int y;

fgets(line, sizeof(line), stdin);

Then, use sscanf to read the numbers from the line of text.

if ( sscanf(line, "(%d,%d)", &x, &y) != 2 )
{
   // Deal with error.
}
else
{
   // Got the numbers.
   // Use them.
}

If you want to use iostreams instead of stdio, use getline instead of fgets.

Upvotes: 1

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

In order to parse stuff you need a parser. There are many ways to write a parser, but generally parsers read tokens and decide what to do next based on which token it is. (Emphasized words are important, look them up).

In your case you don't have to explicitly introduce a separate token entity. Reading elements from the input stream with the >> operator will do.

You need to:

  • read a character; verify it's a '('
  • read a number
  • read a character; verify it's a ','
  • read a number
  • read a character; verify it's a ')'

If any step fails, the entire parsing fails.

You can see the same basic step is done three times, so you can write a function for it.

bool expect_char(std::istream& is, char what)
{
  char ch;
  return is >> ch && ch == what;
}

This works because is >> ch returns the stream after the read operation, and the stream can be viewed as a boolean value: true if the last operation succeeded, false otherwise.

Now you can compose your parser:

bool get_vector (std::istream& is, int& x, int& y)
{
   return expect_char(is, '(') &&
          is >> x              &&
          expect_char(is, ',') &&
          is >> y              &&
          expect_char(is, ')');
}

This method has a nice property that blanks are allowed between the numbers and the symbols.

Now this may look like a lot of stuff to type compared to the solution that uses sscanf:

bool get_numbers2 (std::istream& is, int& x, int& y)
{
   std::string s;
   return std::getline(in, s) &&
          (std::sscanf(s.c_str(), "(%d,%d)", &x, &y) == 2);
}

But sscanf is:

  • dangerous (there is no typechecking of its arguments)
  • less powerful (the input format is very rigid)
  • not generic (doesn't work in a template)

It's OK to use the scanf functions family where appropriate, but I don't recommend it for new C++ programmers.

Upvotes: 0

Blaz Bratanic
Blaz Bratanic

Reputation: 2279

You can use regex to find the matching sequence anywhere in the input.

#include <iostream>
#include <string>
#include <regex>

int main() {
  std::string line;
  std::getline(std::cin, line);

  std::regex e(R"R(\(([-+]?(?:\d*[.])?\d+)\s*,\s*([-+]?(?:\d*[.])?\d+)\))R");

  std::smatch sm;
  if (std::regex_search(line, sm, e)) {
    auto x = std::stod(sm[1]);
    auto y = std::stod(sm[2]);
    std::cout << "Numbers are: " << x << ", " << y << std::endl;
  }

  return 0;
}

Upvotes: 0

Related Questions