2

I'm a C++ newbie who came from Java, so I need some guidance on some really basic issues I'm stumbling upon as I go.

I'm reading lines from a file, and each line consists of 6 strings/ints, which will be sent as parameters to a temporary variable.

Example:

Local1,Local2,ABC,200,300,asphalt

However, there are two subtypes of variable. One has a string as the last parameter (like 'asphalt' in the example above). The other one has an int instead. I have a method that reads each parameter and sends it to a variable, but how do I detect if the last bit of string is an integer or a string beforehand, so I know if I should send it to a Type1 variable or a Type2 one?

Many thanks!

4
  • 2
    That's what parsing is all about... Commented Oct 27, 2013 at 18:54
  • 2
    How would you do it in java? Commented Oct 27, 2013 at 18:55
  • My bad, I forgot to change it from another question I had before I solved it, it is now fixed Commented Oct 27, 2013 at 19:04
  • You really need basic language implementation education- lexing/parsing. Commented Oct 27, 2013 at 19:13

2 Answers 2

2

Since you want to determine the type of the last column, then this ought to work:

#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
#include <sstream>
#include <cctype>
#include <algorithm>

enum Types {
    NONE,
    STRING,
    INTEGER,
    DOUBLE
};

struct Found {
    std::string string_val;
    int integer_val;
    double double_val;
    enum Types type;
};

//copied verbatim from:
//http://stackoverflow.com/a/2845275/866930
inline bool isInteger(const std::string &s) {
   if(s.empty() || ((!std::isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false;

   char * p ;
   std::strtol(s.c_str(), &p, 10);

   return (*p == 0);
}

//modified slightly for decimals:
inline bool isDouble(const std::string &s) {
   if(s.empty() || ((!std::isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;

   char * p ;
   std::strtod(s.c_str(), &p) ;

   return (*p == 0);
}

bool isNotAlpha(char c) {
    return !(std::isalpha(c));
}

//note: this searches for strings containing only characters from the alphabet
//however, you can modify that behavior yourself.
bool isString (const std::string &s) {
    std::string::const_iterator it = std::find_if(s.begin(), s.end(), isNotAlpha);
    return (it == s.end()) ? true : false;
}

void determine_last_column (const std::string& str, Found& found) {
    //reset found:
    found.integer_val = 0;
    found.double_val = 0;
    found.string_val = "";
    found.type = NONE;

    std::string temp;
    std::istringstream iss(str);

    int column = 0;
    char *p;

    while(std::getline(iss, temp, ',')) {
        if (column == 5) {
            //now check to see if the column is an integer or not:
            if (isInteger(temp)) {
                found.integer_val = static_cast<int>(std::strtol(temp.c_str(), &p, 10));
                found.type = INTEGER;
            }
            else if (isDouble(temp)) {
                found.double_val = static_cast<double>(std::strtod(temp.c_str(), &p));
                found.type = DOUBLE;
            }
            else if (isString(temp)) {
                found.string_val = temp;
                found.type = STRING;
            }
        }
        ++column;
    }

    if (found.type == INTEGER) {
        std::cout << "An integer was found: " << found.integer_val << std::endl;
    }
    else if(found.type == DOUBLE) {
        std::cout << "A double was found: " << found.double_val << std::endl;
    }
    else if(found.type == STRING) {
        std::cout << "A string was found: " << found.string_val << std::endl;
    }
    else {
        std::cout << "A valid type was not found! Something went wrong..." << std::endl;
    }
}

int main() {
    std::string line_t1 = "Local1,Local2,ABC,200,300,asphalt";
    std::string line_t2 = "Local1,Local2,ABC,200,300,-7000.3";

    Found found;

    determine_last_column(line_t1, found);
    determine_last_column(line_t2, found);

    return 0;
}

This outputs and correctly assigns the appropriate value:

A string was found: asphalt
An integer was found: -7000.3

This version works on int, double, string; does not require boost; and, is plain vanilla C++98.

REFERENCES:

UPDATE:

This version now supports both positive and negative numbers that are integers or doubles, in addition to strings.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much for the excellent input, I hope this does work out, which I believe it should. One more question, would this work with a double instead of an integer? Mainly in regard to the 'isdigit'.
I managed to get it working by adapting some of the code you provided. Many thanks once again
1

First, create an array that can store both strings and integers:

std::vector<boost::variant<std::string, int>> items;

Second, split the input string on commas:

std::vector<std::string> strings;
boost::split(strings, input, boost::is_any_of(","));

Last, parse each token and insert it into the array:

for (auto&& string : strings) {
    try {
        items.push_back(boost::lexical_cast<int>(string));
    } catch(boost::bad_lexical_cast const&) {
        items.push_back(std::move(string));
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.