c++: parsing and reading text file into multiple arrays separated by delimters - c++

Good afternoon everyone,
Firstly, thank you for taking your time into inputting and helping me understand my problem. I will disclose that this is a Assignment problem so i would like to understand it rather than be given the full code.
As per the title of the question, I am trying to design a program for my assignment. I have included the information below.
Write a program to manage DVD rental in a video rental store. Create
an abstract data type that represents a DVD in this store. Consider all
the data and operations that may be necessary for the DVD type to
work well within a rental management system. Include a print()
member function that displays all the information about the DVD. Test
your data type by creating an array of ten DVD instances and filling
them using information read from a test input file that you create.
Display the DVD information.
Here is what i have so far:
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
string mArrayO[10]; //create array of 10 string objects
string filename = "testfile.txt";
ifstream file(filename.c_str()); //// open file constructor because the constructor for an ifstream takes a const char*, not a string in pre C++11
// go through array till index 10
for( int x=0; x< 10; x++)
{
getline(file, mArrayO[x]); //get line and store into array
cout << "Line "<< x<<":"<< mArrayO[x]<<endl; //display
}
return 0;
}
So far my output is :
Line:0 Lord of the rings|2005|Fantasy|126 minutes|PG-13
Line:1 Titanic|1997|Romance|104 minutes|R
Line:2 Shawshank Redemption|1993|Drama|120 minutes|R
Line:3 The Lion King|1987|Cartoon|84 minutes|PG-10
Line:4 The God Father|1988|Violence|146 minutes|R
Line:5 Bruce Almighty|2009|Comedy|78 minutes|R
Line:6 Spiderman|2006|Action|84 minutes|PG-13
Line:7 Finding Nemo|2007|Cartoon|87 minutes|E
Line:8 The Martian|2015|SciFi|104 minutes|PG-13
Line:9 Insidious 3|2015|Horror|97 minutes|R
So as you can see i have put the data into one array. Now here is what i am trying to do. I want to be able to parse through this data and feed it into my DVD class. Specifically as my question outlines I want to be able to put all the movie names in one array, dates in another and so on until every data set is matched.Once that is done i want to be able to prompt the user to pick an option and from there have the corresponding function occur.
I also have made the following template for my DVD class:
class movie
{
public:
/// These are our methods to get the information
string getMovieName()
{
return moviename;
}
string getYear()
{
return year;
}
string getGenre()
{
return genre;
}
string getTime()
{
return time;
}
string getRating()
{
return rating;
}
private: // these are the members of this class. They will store all the information we need for this class
string moviename;
string year;
string genre;
string time;
string rating;
};
Here is another portion of my code. I have put it here incase people needed to see if i have attempted
bool done= false; // Client wants to continue.
while (!done)
{
cout<< "This is a test";
cin.ignore();
done=ask_to_continue();
}
//finish
cout<< "Thank you for using the DVD rental management system";
return 0;
bool ask_to_continue()
{
char again; //using keystroke
cout << "Do you need more help? Y or N? " << endl;
cin >> again;
if(again == 'n')
return true; // Exit program
return false; // Still using program
}

You have successfully read in the data from your text file and populated your array of strings. Now what you need to do is to write a parsing function that will parse each string or line in your array. Then while parsing each line you will then need to save each content into your class.
I can offer you a generic function that will parse strings for you provided that you are using the same delimiter; it is encapsulated in a Utility class as a static method along with a few other useful string manipulation functions.
Utility.h
#ifndef UTILITY_H
#define UTILITY_H
class Utility {
public:
static void pressAnyKeyToQuit();
static std::string toUpper( const std::string& str );
static std::string toLower( const std::string& str );
static std::string trim( const std::string& str, const std::string elementsToTrim = " \t\n\r" );
static unsigned convertToUnsigned( const std::string& str );
static int convertToInt( const std::string& str );
static float convertToFloat( const std::string& str );
static std::vector<std::string> splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true );
private:
Utility(); // Private - Not A Class Object
Utility( const Utility& c ); // Not Implemented
Utility& operator=( const Utility& c ); // Not Implemented
template<typename T>
static bool stringToValue( const std::string& str, T* pValue, unsigned uNumValues );
template<typename T>
static T getValue( const std::string& str, std::size_t& remainder );
}; // Utility
#include "Utility.inl"
#endif // UTILITY_H
Utility.inl
// stringToValue()
template<typename T>
static bool Utility::stringToValue(const std::string& str, T* pValue, unsigned uNumValues) {
int numCommas = std::count(str.begin(), str.end(), ',');
if (numCommas != uNumValues - 1) {
return false;
}
std::size_t remainder;
pValue[0] = getValue<T>(str, remainder);
if (uNumValues == 1) {
if (str.size() != remainder) {
return false;
}
}
else {
std::size_t offset = remainder;
if (str.at(offset) != ',') {
return false;
}
unsigned uLastIdx = uNumValues - 1;
for (unsigned u = 1; u < uNumValues; ++u) {
pValue[u] = getValue<T>(str.substr(++offset), remainder);
offset += remainder;
if ((u < uLastIdx && str.at(offset) != ',') ||
(u == uLastIdx && offset != str.size()))
{
return false;
}
}
}
return true;
} // stringToValue
Utility.cpp
#include "stdafx.h"
#include "Utility.h"
// pressAnyKeyToQuit()
void Utility::pressAnyKeyToQuit() {
std::cout << "Press any key to quit" << std::endl;
_getch();
} // pressAnyKeyToQuit
// toUpper()
std::string Utility::toUpper(const std::string& str) {
std::string result = str;
std::transform(str.begin(), str.end(), result.begin(), ::toupper);
return result;
} // toUpper
// toLower()
std::string Utility::toLower(const std::string& str) {
std::string result = str;
std::transform(str.begin(), str.end(), result.begin(), ::tolower);
return result;
} // toLower
// trim()
// Removes Elements To Trim From Left And Right Side Of The str
std::string Utility::trim(const std::string& str, const std::string elementsToTrim) {
std::basic_string<char>::size_type firstIndex = str.find_first_not_of(elementsToTrim);
if (firstIndex == std::string::npos) {
return std::string(); // Nothing Left
}
std::basic_string<char>::size_type lastIndex = str.find_last_not_of(elementsToTrim);
return str.substr(firstIndex, lastIndex - firstIndex + 1);
} // trim
// getValue()
template<>
float Utility::getValue(const std::string& str, std::size_t& remainder) {
return std::stof(str, &remainder);
} // getValue <float>
// getValue()
template<>
int Utility::getValue(const std::string& str, std::size_t& remainder) {
return std::stoi(str, &remainder);
} // getValue <int>
// getValue()
template<>
unsigned Utility::getValue(const std::string& str, std::size_t& remainder) {
return std::stoul(str, &remainder);
} // getValue <unsigned>
// convertToUnsigned()
unsigned Utility::convertToUnsigned(const std::string& str) {
unsigned u = 0;
if (!stringToValue(str, &u, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
throw strStream.str();
}
return u;
} // convertToUnsigned
// convertToInt()
int Utility::convertToInt(const std::string& str) {
int i = 0;
if (!stringToValue(str, &i, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
throw strStream.str();
}
return i;
} // convertToInt
// convertToFloat()
float Utility::convertToFloat(const std::string& str) {
float f = 0;
if (!stringToValue(str, &f, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
throw strStream.str();
}
return f;
} // convertToFloat
// splitString()
std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {
std::vector<std::string> vResult;
if ( strDelimiter.empty() ) {
vResult.push_back( strStringToSplit );
return vResult;
}
std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
while ( true ) {
itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
std::string strTemp( itSubStrStart, itSubStrEnd );
if ( keepEmpty || !strTemp.empty() ) {
vResult.push_back( strTemp );
}
if ( itSubStrEnd == strStringToSplit.end() ) {
break;
}
itSubStrStart = itSubStrEnd + strDelimiter.size();
}
return vResult;
} // splitString
The includes required for this class that are found in stdafx.h are: vector, string, conio.h, and maybe tchar.h and I also use this enum for main's return values which is also found in stdafx.h
enum ReturnCode {
RETURN_OK = 0,
RETURN_ERROR = 1,
}; // ReturnCode
As for your Movie class you have defined your functions to retrieve data, but you have not shown that you have defined any setting methods nor constructors. Also your getters should be declared as const functions since these functions will not change the data stored in your class object. Your class should look as such:
Movie.h
#ifndef MOVIE_H
#define MOVIE_H
class Movie {
private:
std::string m_strTitle;
std::string m_strYear;
std::string m_strGenre;
std::string m_strLength;
std::string m_Rating;
public:
Movie(); // Default Constructor
Movie( const std::string& strTitle, const std::string& strYear, const std::string& strGenre,
const std::string& strLength, const std::string& strRating();
std::string getTitle() const;
std::string getYear() const;
std::string getGenre() const;
std::string getLength() const;
std::string getRating() const;
void setTitle( const std::string& strTile );
void setYear( const std::string& strYear );
void setGenre( const std::string& strGenre );
void setLength( const std::string& strLength );
void setRating( const std::string& strRating );
private:
Movie( const Movie& c ); // Not Implemented
Movie& operator=( const Movie& c ); // Not Implemented
}; // Movie
#endif // MOVIE_H
Movie.cpp
#include "stdafx.h"
#include "Movie.h"
// Movie()
Movie::Movie() {
} // Movie
// Movie()
Movie::Movie( const std::string& strTitle, const std::string& strYear, const std::string& strGenre,
const std::string& strLength, const std::string& strRating ) :
m_strTitle( strTitle ),
m_strYear( strYear ),
m_strGenre( strGenre ),
m_strLength( strLength ),
m_strRating( strRating ) {
} // Movie
// getTitle()
std::string Movie::getTitle() const {
return m_strTitle;
} // getTitle
// getYear()
std::string Movie::getYear() const {
return m_strYear;
} // getYear
// getGenre()
std::string Movie::getGenre() const {
return m_strGenre;
} // GetGenre
// getLength()
std::string Movie::getLength() const {
return m_strLength;
} // getLength
// getRating()
std::string Movie::getRating() const {
return m_strRating;
} // getRating
// setTitle()
void Movie::setTitle( const std::string& strTile ) {
m_strTitle = strTile;
} // setTitle
// setYear()
void Movie::setYear( const std::string& strYear ) {
m_strYear = strYear;
} // setYear
// setGenre()
void Movie::setGenre( const std::string& strGenre ) {
m_strGenre = strGenre;
} // setGenre
// setLength()
void Movie::setLength( const std::string& strLength ) {
m_strLength = strLength;
} // setLength
// setRating()
void Movie::setRating( const std::string& strRating ) {
m_strRating = strRating;
} // setRating
Now as for parsing your string the function of use in the Utility class is Utility::splitString() You will need to go through a for loop for as many entries as there are in your array and for each pass you will have to parse that string then construct a Movie object either by using its default constructor then populating each member or by constructing the object with the appropriate constructor. I will show you an example of how to use the splitString() function.
main.cpp
#include "stdafx.h"
#include "Utility.h"
#include "Movie.h"
int main() {
std::string test( "Hello World How Are You Today" );
std::vector<std::string>> vResults;
vResults = Utility::splitString( test, " " );
Utility::pressAnyKeyToQuit();
return RETURN_OK;
} // main
Here splitString will take as its first parameter the string that you want to parse, as its second parameter the delimiting string or char as in your case it would be the "|" character and it will save the results into a vector of strings. From here on each pass of your for loop then it is just a matter of constructing your Movie objects while assigning them the appropriate values.

To split a string, use the find or find_first_of function from std::string to get the positions of the delimiters. Then use substr to get the substrings between these delimiters.

Related

Storing vector of doubles into a text file [duplicate]

How do I do the following with std::cout?
double my_double = 42.0;
char str[12];
printf_s("%11.6lf", my_double); // Prints " 42.000000"
I am just about ready to give up and use sprintf_s.
More generally, where can I find a reference on std::ostream formatting that lists everything in one place, rather than spreading it all out in a long tutorial?
EDIT Dec 21, 2017 - See my answer below. It uses features that were not available when I asked this question in 2012.
std::cout << std::fixed << std::setw(11) << std::setprecision(6) << my_double;
You need to add
#include <iomanip>
You need stream manipulators
You may "fill" the empty places with whatever char you want. Like this:
std::cout << std::fixed << std::setw(11) << std::setprecision(6)
<< std::setfill('0') << my_double;
std::cout << boost::format("%11.6f") % my_double;
You have to #include <boost\format.hpp>
In C++20 you can to do
double my_double = 42.0;
char str[12];
std::format_to_n(str, sizeof(str), "{:11.6}", my_double);
or
std::string s = std::format("{:11.6}", my_double);
In pre-C++20 you can use the {fmt} library that provides an implementation of format_to_n.
Disclaimer: I'm the author of {fmt} and C++20 std::format.
In general, you want to avoid specifying things like 11 and 6 at the
point of output. That's physical markup, and you want logical markup;
e.g. pressure, or volume. That way, you define in a single place
how pressure or volume are formatted, and if that formatting changes,
you don't have to search through out the program to find where to change
the format (and accidentally change the format of something else). In
C++, you do this by defining a manipulator, which sets the various
formatting options, and preferrably restores them at the end of the full
expression. So you end up writing things like:
std::cout << pressure << my_double;
Although I definitly wouldn't use it in production code, I've found the
following FFmt formatter useful for quicky jobs:
class FFmt : public StateSavingManip
{
public:
explicit FFmt(
int width,
int prec = 6,
std::ios::fmtflags additionalFlags
= static_cast<std::ios::fmtflags>(),
char fill = ' ' );
protected:
virtual void setState( std::ios& targetStream ) const;
private:
int myWidth;
int myPrec;
std::ios::fmtflags myFlags;
char myFill;
};
FFmt::FFmt(
int width,
int prec,
std::ios::fmtflags additionalFlags,
char fill )
: myWidth( width )
, myPrec( prec )
, myFlags( additionalFlags )
, myFill( fill )
{
myFlags &= ~ std::ios::floatfield
myFlags |= std::ios::fixed
if ( isdigit( static_cast< unsigned char >( fill ) )
&& (myFlags & std::ios::adjustfield) == 0 ) {
myFlags |= std::ios::internal
}
}
void
FFmt::setState(
std::ios& targetStream ) const
{
targetStream.flags( myFlags )
targetStream.width( myWidth )
targetStream.precision( myPrec )
targetStream.fill( myFill )
}
This allows writing things like:
std::cout << FFmt( 11, 6 ) << my_double;
And for the record:
class StateSavingManip
{
public:
StateSavingManip(
StateSavingManip const& other );
virtual ~StateSavingManip();
void operator()( std::ios& stream ) const;
protected:
StateSavingManip();
private:
virtual void setState( std::ios& stream ) const = 0;
private:
StateSavingManip& operator=( StateSavingManip const& );
private:
mutable std::ios* myStream;
mutable std::ios::fmtflags
mySavedFlags;
mutable int mySavedPrec;
mutable char mySavedFill;
};
inline std::ostream&
operator<<(
std::ostream& out,
StateSavingManip const&
manip )
{
manip( out );
return out;
}
inline std::istream&
operator>>(
std::istream& in,
StateSavingManip const&
manip )
{
manip( in );
return in;
}
StateSavingManip.cc:
namespace {
// We maintain the value returned by ios::xalloc() + 1, and not
// the value itself. The actual value may be zero, and we need
// to be able to distinguish it from the 0 resulting from 0
// initialization. The function getXAlloc() returns this value
// -1, so we add one in the initialization.
int getXAlloc();
int ourXAlloc = getXAlloc() + 1;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1;
assert( ourXAlloc != 0 );
}
return ourXAlloc - 1;
}
}
StateSavingManip::StateSavingManip()
: myStream( NULL )
{
}
StateSavingManip::StateSavingManip(
StateSavingManip const&
other )
{
assert( other.myStream == NULL );
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != NULL ) {
myStream->flags( mySavedFlags );
myStream->precision( mySavedPrec );
myStream->fill( mySavedFill );
myStream->pword( getXAlloc() ) = NULL;
}
}
void
StateSavingManip::operator()(
std::ios& stream ) const
{
void*& backptr = stream.pword( getXAlloc() );
if ( backptr == NULL ) {
backptr = const_cast< StateSavingManip* >( this );
myStream = &stream;
mySavedFlags = stream.flags();
mySavedPrec = stream.precision();
mySavedFill = stream.fill();
}
setState( stream );
}
#include <iostream>
#include <iomanip>
int main() {
double my_double = 42.0;
std::cout << std::fixed << std::setw(11)
<< std::setprecision(6) << my_double << std::endl;
return 0;
}
For future visitors who prefer actual printf-style format specs with std::ostream, here is yet another variation, based on Martin York's excellent post in another SO question: https://stackoverflow.com/a/535636:
#include <iostream>
#include <iomanip>
#include <stdio.h> //snprintf
class FMT
{
public:
explicit FMT(const char* fmt): m_fmt(fmt) {}
private:
class fmter //actual worker class
{
public:
explicit fmter(std::ostream& strm, const FMT& fmt): m_strm(strm), m_fmt(fmt.m_fmt) {}
//output next object (any type) to stream:
template<typename TYPE>
std::ostream& operator<<(const TYPE& value)
{
// return m_strm << "FMT(" << m_fmt << "," << value << ")";
char buf[40]; //enlarge as needed
snprintf(buf, sizeof(buf), m_fmt, value);
return m_strm << buf;
}
private:
std::ostream& m_strm;
const char* m_fmt;
};
const char* m_fmt; //save fmt string for inner class
//kludge: return derived stream to allow operator overloading:
friend FMT::fmter operator<<(std::ostream& strm, const FMT& fmt)
{
return FMT::fmter(strm, fmt);
}
};
usage example:
double my_double = 42.0;
cout << FMT("%11.6f") << my_double << "more stuff\n";
or even:
int val = 42;
cout << val << " in hex is " << FMT(" 0x%x") << val << "\n";
it's me, the OP, Jive Dadson - five years on. C++17 is becoming a reality.
The advent of variadic template parameters with perfect forwarding has made life so much simpler. The chained madness of ostream<< and boost::format% can be dispensed with. The function oprintf below fills the bill. Work in progress. Feel free to chime in on error-handling, etc...
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <string_view>
namespace dj {
template<class Out, class... Args>
Out& oprintf(Out &out, const std::string_view &fmt, Args&&... args) {
const int sz = 512;
char buffer[sz];
int cx = snprintf(buffer, sz, fmt.data(), std::forward<Args>(args)...);
if (cx >= 0 && cx < sz) {
return out.write(buffer, cx);
} else if (cx > 0) {
// Big output
std::string buff2;
buff2.resize(cx + 1);
snprintf(buff2.data(), cx, fmt.data(), std::forward<Args>(args)...);
return out.write(buff2.data(), cx);
} else {
// Throw?
return out;
}
}
}
int main() {
const double my_double = 42.0;
dj::oprintf(std::cout, "%s %11.6lf\n", "My double ", my_double);
return 0;
}
Some great answers already; kudos to those!
This is based on some of them. I have added type assertions for POD types, since they are the only safe types usable with printf().
#include <iostream>
#include <stdio.h>
#include <type_traits>
namespace fmt {
namespace detail {
template<typename T>
struct printf_impl
{
const char* fmt;
const T v;
printf_impl(const char* fmt, const T& v) : fmt(fmt), v(v) {}
};
template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, std::ostream& >::type
operator<<(std::ostream& os, const printf_impl<T>& p)
{
char buf[40];
::snprintf(buf, sizeof(buf), p.fmt, p.v, 40);
return os << buf;
}
} // namespace detail
template<typename T>
inline typename std::enable_if<std::is_pod<T>::value, detail::printf_impl<T> >::type
printf(const char* fmt, const T& v)
{
return detail::printf_impl<T>(fmt, v);
}
} // namespace fmt
Example usage it as below.
std::cout << fmt::printf("%11.6f", my_double);
Give it a try on Coliru.

C++ string help, Spitting a string

I am writing a program which is suppose to be able to open a file, read each line in and separate a LAST NAME, First name: 3 test scores. the format of the file being read is Mark Titan: 80 80 85. I am suppose to output TITAN, Mark: 80 80 85. We are using strings, and so far using my teachers code i have gotten the file to separate completely. it would show the test scores in order from 1-100(100 comes first because it starts with 1 but i can fix that after) and then alphabetically the names. I need help creating a substr of the line, creating a string of just the full name and splitting that into first and last and then sort them correctly. I have been messing with .find but im not sure how to split this vector into smaller vectors. please help and thank you.
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
using namespace std;
void openFile(ifstream &in);
void processFile(ifstream &in, vector<string> &list);
void display(const vector<string> &list);
void sort(vector<string> &list);
int main(int argc, char *argv[])
{
ifstream in;
string line;
vector<string> words;
openFile(in);
processFile(in, words);
display(words);
return 0;
}
void openFile(ifstream &in)
{
string fileName;
bool again;
do
{
again = false;
cout<<"What is the name of the file to you wish to sort? (.txt will be added if no extension is included): ";
cin>>fileName;
if(fileName.find('.') >= fileName.size() )
fileName += ".txt";
in.open(fileName.c_str());
if(in.fail())
{
in.close();
in.clear();
again = true;
cout<<"That file does not exist! Please re-enter"<<endl;
}
}while(again);
}
void processFile(ifstream &in, vector<string> &list)
{
string line, word;
int s1,s2,s3;
stringstream ss;
while(getline(in, line, ':'))
{
ss<<line.substr(line.find(' ') + 1);
while(ss>>word)
list.push_back(word);
ss.clear();
}
sort(list);
}
void sort(vector<string> &list)
{
for(unsigned int i = 0; i < list.size(); ++i)
for(unsigned int j = 0; j < list.size(); ++j)
if(list[i] < list[j])
{
string temp = list[i];
list[i] = list[j];
list[j] = temp;
}
}
void display(const vector<string> &list)
{
cout<<"The file read had "<<list.size()<<" words in it"
<<endl<<"In sorted order, they are:"<<endl;
for(unsigned int i = 0; i < list.size();++i)
cout<<list[i]<<endl;}
You can Tokenize the line twice. First, split on the colon, then split the first token on the space. Also, your teacher will throw empty and malformed lines at you. For instance missing colon or missing name (or missing first/last name). Handle those errors by counting the tokens returned when you split a string or sub string.
void Tokenize(const std::string& theSourceString, std::vector<std::string>& theTokens, const std::string& theDelimiter)
{
// If no delimiter is passed, tokenize on all white space.
if (theDelimiter.empty())
{
std::string aBuffer; // Have a buffer string
std::stringstream ss(theSourceString); // Insert the string into a stream
while (ss >> aBuffer)
{
theTokens.push_back(aBuffer);
}
return; //?
}
// Skip delimiters at beginning.
std::string::size_type aLastPosition = theSourceString.find_first_not_of(theDelimiter, 0);
// Find first "non-delimiter".
std::string::size_type aPosition = theSourceString.find_first_of(theDelimiter, aLastPosition);
while (aPosition != std::string::npos || aLastPosition != std::string::npos)
{
// Found a token, add it to the vector.
theTokens.push_back(theSourceString.substr(aLastPosition, aPosition - aLastPosition));
// Skip delimiters. Note the "not_of"
aLastPosition = theSourceString.find_first_not_of(theDelimiter, aPosition);
// Find next "non-delimiter"
aPosition = theSourceString.find_first_of(theDelimiter, aLastPosition);
}
}
Example use:
std::vector<std::string> tokens;
Tokenize("{ 1, 2, 3, 4, 5 }", tokens, "{}, " );
I have this Utility class that has a bunch of methods for string manipulation. I will show the class function for splitting strings with a delimiter. This class has private constructor so you can not create an instance of this class. All the methods are static methods.
Utility.h
#ifndef UTILITY_H
#define UTILITY_h
// Library Includes Here: vector, string etc.
class Utility {
public:
static std::vector<std::string> splitString( const std::string& strStringToSplit,
const std::string& strDelimiter,
const bool keepEmpty = true );
private:
Utility();
};
Utility.cpp
std::vector<std::string> Utility::splitString( const std::string& strStringToSplit,
const std::string& strDelimiter,
const bool keepEmpty ) {
std::vector<std::string> vResult;
if ( strDelimiter.empty() ) {
vResult.push_back( strStringToSplit );
return vResult;
}
std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
while ( true ) {
itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
std::string strTemp( itSubStrStart, itSubStrEnd );
if ( keepEmpty || !strTemp.empty() ) {
vResult.push_back( strTemp );
}
if ( itSubStrEnd == strStringToSplit.end() ) {
break;
}
itSubStrStart = itSubStrEnd + strDelimiter.size();
}
return vResult;
}
Main.cpp -- Usage
#include <string>
#include <vector>
#include "Utility.h"
int main() {
std::string myString( "Hello World How Are You Today" );
std::vector<std::string> vStrings = Utility::splitString( myString, " " );
// Check Vector Of Strings
for ( unsigned n = 0; n < vStrings.size(); ++n ) {
std::cout << vStrings[n] << " ";
}
std::cout << std::endl;
// The Delimiter is also not restricted to just a single character
std::string myString2( "Hello, World, How, Are, You, Today" );
// Clear Out Vector
vStrings.clear();
vStrings = Utility::splitString( myString2, ", " ); // Delimiter = Comma & Space
// Test Vector Again
for ( unsigned n = 0; n < vStrings.size(); ++n ) {
std::cout << vStrings[n] << " ";
}
std::cout << std::endl;
return 0;
}

C++ 2D Struct Array data

I want to be able to store values from a file into a user defined struct. I want to have columns that hold different types of data such as: a std::string for Character names, a float for Character health and an int for Character experience.
My desired output is to have a 6 by 3 array as shown below
Alice 23.4 3210
Xander 45.3 1110
Bernard 12.9 2024
Yanni 23.7 1098
Craw 50.5 980
Zack 11.9 1024
Here is what I have tried so far:
struct charData
{
string charName;
float charHealth;
int charExp;
};
int main() {
const int NUM_COLS = 3;
int NUM_ROWS = 6;
int charNumber = 0;
int userInput;
int loop = 0;
int i,j;
string line;
ifstream myIn; // Declare read file stream
myIn.open("party.dat"); // Open file
struct charData charArray[NUM_ROWS][NUM_COLS];
while( !myIn.eof() ) {
for ( j = 0; j < NUM_COLS ; j++) {
for ( i = 0; i < NUM_ROWS ; i++) {
myIn >> charArray[i][j].charName;
myIn >> charArray[i][j].charHealth;
myIn >> charArray[i][j].charExp;
}
}
}
return 0;
}
I am also planning on to allow the user to sort the data by each column type. To sort names alphabetically, sort by health and or experience: I am thinking of using a 2D array. Would this be the best option?
I think you are confusing the number of fields in your structure with the number of columns of data. You show data for only 6 records of 3 fields each. Try setting NUM_COLS to 1 and see if it works any better for you.
Or, get rid of the second dimension (j) of your array entirely.
I don't know if you are reading your file as binary or as a text; but within my answer I'm reading from a text file where each field on a line of text is separated by a space character. I will include all files necessary to compile and build a proper working program of what you are looking for. Some of these includes may not be needed but are within my solution for I have larger project that depends on these to properly build.
stdafx.h
#ifndef STDAFX_H
#define STDAFX_H
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <conio.h>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <array>
#include <memory>
#include <queue>
#include <functional>
#include <algorithm>
// User Application Specific
// #include "ExceptionHandler.h" // Required For My Solution But Isn't Used In Yours
namespace util {
enum ReturnCode {
RETURN_OK = 0,
RETURN_ERROR = 1,
}; // ReturnCode
extern const unsigned INVALID_UNSIGNED;
extern const unsigned INVALID_UNSIGNED_SHORT;
} // namespace util
#endif // STDAFX_H
stdafx.cpp
#include "stdafx.h"
namespace util {
const unsigned INVALID_UNSIGNED = static_cast<const unsigned>( -1 );
const unsigned INVALID_UNSIGNED_SHORT = static_cast<const unsigned short>( -1 );
} // namespace util
Utility.h
#ifndef UTILITY_H
#define UTILITY_H
namespace util {
class Utility {
public:
static void pressAnyKeyToQuit();
static std::string toUpper(const std::string& str);
static std::string toLower(const std::string& str);
static std::string trim(const std::string& str, const std::string elementsToTrim = " \t\n\r");
static unsigned convertToUnsigned(const std::string& str);
static int convertToInt(const std::string& str);
static float convertToFloat(const std::string& str);
static std::vector<std::string> splitString(const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true);
private:
Utility(); // Private - Not A Class Object
Utility(const Utility& c); // Not Implemented
Utility& operator=(const Utility& c); // Not Implemented
template<typename T>
static bool stringToValue(const std::string& str, T* pValue, unsigned uNumValues);
template<typename T>
static T getValue(const std::string& str, std::size_t& remainder);
}; // Utility
#include "Utility.inl"
} // namespace util
#endif // UTILITY_H
Utility.inl
// ----------------------------------------------------------------------------
// stringToValue()
template<typename T>
static bool Utility::stringToValue(const std::string& str, T* pValue, unsigned uNumValues) {
int numCommas = std::count(str.begin(), str.end(), ',');
if (numCommas != uNumValues - 1) {
return false;
}
std::size_t remainder;
pValue[0] = getValue<T>(str, remainder);
if (uNumValues == 1) {
if (str.size() != remainder) {
return false;
}
}
else {
std::size_t offset = remainder;
if (str.at(offset) != ',') {
return false;
}
unsigned uLastIdx = uNumValues - 1;
for (unsigned u = 1; u < uNumValues; ++u) {
pValue[u] = getValue<T>(str.substr(++offset), remainder);
offset += remainder;
if ((u < uLastIdx && str.at(offset) != ',') ||
(u == uLastIdx && offset != str.size()))
{
return false;
}
}
}
return true;
} // stringToValue
Utility.cpp
#include "stdafx.h"
#include "Utility.h"
namespace util {
// ----------------------------------------------------------------------------
// pressAnyKeyToQuit()
void Utility::pressAnyKeyToQuit() {
std::cout << "Press any key to quit" << std::endl;
_getch();
} // pressAnyKeyToQuit
// ----------------------------------------------------------------------------
// toUpper()
std::string Utility::toUpper( const std::string& str ) {
std::string result = str;
std::transform( str.begin(), str.end(), result.begin(), ::toupper );
return result;
} // toUpper
// ----------------------------------------------------------------------------
// toLower()
std::string Utility::toLower( const std::string& str ) {
std::string result = str;
std::transform( str.begin(), str.end(), result.begin(), ::tolower );
return result;
} // toLower
// ----------------------------------------------------------------------------
// trim()
// Removes Elements To Trim From Left And Right Side Of The str
std::string Utility::trim( const std::string& str, const std::string elementsToTrim ) {
std::basic_string<char>::size_type firstIndex = str.find_first_not_of( elementsToTrim );
if ( firstIndex == std::string::npos ) {
return std::string(); // Nothing Left
}
std::basic_string<char>::size_type lastIndex = str.find_last_not_of( elementsToTrim );
return str.substr( firstIndex, lastIndex - firstIndex + 1 );
} // trim
// ----------------------------------------------------------------------------
// getValue()
template<>
float Utility::getValue( const std::string& str, std::size_t& remainder ) {
return std::stof( str, &remainder );
} // getValue <float>
// ----------------------------------------------------------------------------
// getValue()
template<>
int Utility::getValue( const std::string& str, std::size_t& remainder ) {
return std::stoi( str, &remainder );
} // getValue <int>
// ----------------------------------------------------------------------------
// getValue()
template<>
unsigned Utility::getValue( const std::string& str, std::size_t& remainder ) {
return std::stoul( str, &remainder );
} // getValue <unsigned>
// ----------------------------------------------------------------------------
// convertToUnsigned()
unsigned Utility::convertToUnsigned( const std::string& str ) {
unsigned u = 0;
if ( !stringToValue( str, &u, 1 ) ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
throw strStream.str();
}
return u;
} // convertToUnsigned
// ----------------------------------------------------------------------------
// convertToInt()
int Utility::convertToInt( const std::string& str ) {
int i = 0;
if ( !stringToValue( str, &i, 1 ) ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
throw strStream.str();
}
return i;
} // convertToInt
// ----------------------------------------------------------------------------
// convertToFloat()
float Utility::convertToFloat(const std::string& str) {
float f = 0;
if (!stringToValue(str, &f, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
throw strStream.str();
}
return f;
} // convertToFloat
// ----------------------------------------------------------------------------
// splitString()
std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {
std::vector<std::string> vResult;
if ( strDelimiter.empty() ) {
vResult.push_back( strStringToSplit );
return vResult;
}
std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
while ( true ) {
itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
std::string strTemp( itSubStrStart, itSubStrEnd );
if ( keepEmpty || !strTemp.empty() ) {
vResult.push_back( strTemp );
}
if ( itSubStrEnd == strStringToSplit.end() ) {
break;
}
itSubStrStart = itSubStrEnd + strDelimiter.size();
}
return vResult;
} // splitString
} // namspace util
CharacterData.h
#ifndef CHARACTER_DATA_H
#define CHARACTER_DATA_H
class CharacterData {
private:
std::string m_name;
float m_health;
unsigned m_exp;
public:
CharacterData();
CharacterData( const std::string& name, float health, unsigned exp );
void setName( const std::string& name );
void setHealth( float health );
void setExperience( unsigned exp );
std::string getName() const;
float getHealth() const;
unsigned getExperience() const;
private:
CharacterData( const CharacterData& c ); // Not Implemented
CharacterData& operator=( const CharacterData& c ); // Not Implemented
}; // CharacterData
#endif // CHARACTER_DATA_H
CharacterData.cpp
#include "stdafx.h"
#include "CharacterData.h"
// ----------------------------------------------------------------------------
// CharacterData()
CharacterData::CharacterData() :
m_name( std::string() ),
m_health( 0.0f ),
m_exp( 0 ) {
} // CharacterData
// ----------------------------------------------------------------------------
// CharacterData()
CharacterData::CharacterData( const std::string& name, float health, unsigned exp ) :
m_name( name ),
m_health( health ),
m_exp( exp ) {
} // CharacterData
// ----------------------------------------------------------------------------
// setName()
void CharacterData::setName( const std::string& name ) {
m_name = name;
} // setName
// ----------------------------------------------------------------------------
// getName()
std::string CharacterData::getName() const {
return m_name;
} // getName
// ----------------------------------------------------------------------------
// setHealth()
void CharacterData::setHealth( float health ) {
m_health = health;
} // setHealth
// ----------------------------------------------------------------------------
// getHealth()
float CharacterData::getHealth() const {
return m_health;
} // getHealth
// ----------------------------------------------------------------------------
// setExperience()
void CharacterData::setExperience( unsigned exp ) {
m_exp = exp;
} // setExperience
// ----------------------------------------------------------------------------
// getExperience()
unsigned CharacterData::getExperience() const {
return m_exp;
} // getExperience
CharacterDatabase.h
#ifndef CHARACTER_DATABASE_H
#define CHARACTER_DATABASE_H
class CharacterData;
class CharacterDatabase {
private:
std::string m_filename;
std::vector<std::shared_ptr<CharacterData>> m_vpCharacters;
public:
explicit CharacterDatabase( const std::string& filename );
// ~CharacterDatabase(); // Default Okay
void displayByOption( unsigned option = 0 );
private:
CharacterDatabase( const CharacterDatabase& c ); // Not Implemented
CharacterDatabase& operator=( const CharacterDatabase& c ); // Not Implemented
void parseFile();
void display() const;
static bool sortByName( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2);
static bool sortByHealth( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2);
static bool sortByExperience(const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2);
}; // CharacterDataase
#endif // CHARACTER_DATABASE_H
CharacterDatabase.cpp
#include "stdafx.h"
#include "CharacterDatabase.h"
#include "CharacterData.h"
#include "Utility.h"
// ----------------------------------------------------------------------------
// CharacterDatabase()
CharacterDatabase::CharacterDatabase( const std::string& filename ) :
m_filename( filename ) {
parseFile();
} // CharacterDatabase
// ----------------------------------------------------------------------------
// parseFile()
void CharacterDatabase::parseFile() {
using namespace util;
if ( m_filename.empty() ) {
std::cout << "Missing or invalid filename." << std::endl;
return;
}
std::string line;
std::vector<std::string> results;
std::ifstream in;
// Try To Open File For Reading
in.open( m_filename.c_str(), std::ios_base::in );
if ( !in.is_open() ) {
std::cout << "Can not open file(" << m_filename << ") for reading.";
return;
}
// Read Line By Line And Store Contents Into String & Parse Each Line One At A Time.
while ( !in.eof() ) {
std::getline( in, line );
results = Utility::splitString( line, " " );
// On Each Pass We Want To Construct A CharacterData Object & Save It Into Our Container
m_vpCharacters.push_back( std::make_shared<CharacterData>( results[0], Utility::convertToFloat( results[1] ), Utility::convertToUnsigned( results[2] ) ) );
}
// Close File Pointer
in.close();
} // parseFile
// ----------------------------------------------------------------------------
// display()
void CharacterDatabase::display() const {
for (unsigned u = 0; u < m_vpCharacters.size(); u++) {
std::cout << m_vpCharacters[u]->getName() << "\t"
<< m_vpCharacters[u]->getHealth() << "\t"
<< m_vpCharacters[u]->getExperience() << std::endl;
}
std::cout << std::endl;
} // display
// ----------------------------------------------------------------------------
// displayByOption() Default 0 = Order In Which File Is Read In,
// 1 = Sorted By Name, 2 = Sorted By Health, 3 = Sorted By Experience
void CharacterDatabase::displayByOption( unsigned option ) {
switch ( option ) {
case 1: { // Sorted By Name
std::sort( m_vpCharacters.begin(), m_vpCharacters.end(), sortByName );
display();
break;
}
case 2: { // Sorted By Health
std::sort( m_vpCharacters.begin(), m_vpCharacters.end(), sortByHealth );
display();
break;
}
case 3: { // Sorted By Experience
std::sort( m_vpCharacters.begin(), m_vpCharacters.end(), sortByExperience );
display();
break;
}
default: { // Unsorted - Order Read In By File
display();
}
}
} // displayByOption
// ----------------------------------------------------------------------------
// sortByName()
bool CharacterDatabase::sortByName( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2 ) {
return d1->getName() < d2->getName();
} // sortByName
// ----------------------------------------------------------------------------
// sortByHealth()
bool CharacterDatabase::sortByHealth( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2 ) {
return d1->getHealth() < d2->getHealth();
} // sortByHealth
// ----------------------------------------------------------------------------
// sortByExperience()
bool CharacterDatabase::sortByExperience( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2) {
return d1->getExperience() < d2->getExperience();
} // sortByExperience
characterData.txt
Alice 23.4 3210
Xander 45.3 1110
Bernard 12.9 2024
Yanni 23.7 1098
Craw 50.5 980
Zack 11.9 1024
main.cpp
#include "stdafx.h"
#include "Utility.h"
#include "CharacterDatabase.h"
int main() {
using namespace util;
CharacterDatabase cd( "characterData.txt" );
cd.displayByOption();
cd.displayByOption( 1 );
cd.displayByOption( 2 );
cd.displayByOption( 3 );
Utility::pressAnyKeyToQuit();
return RETURN_OK;
} // main
Just make sure that you are calling the file from the correct path, also when using the Utility::splitString() method make sure the second parameter or the delimiter matches what you have in your stored file. The splitString() method will divide the string from each line of the file every time it sees that character. If you happen to have multiple names such as a first & last name that you want stored to a single string then a little more care needs to be taken.
for sorting on the basis of any col you can use sort() in algorithm header with your own compare function.
Define your compare function like this:
on the basis of Health:
bool MyCompHealth(struct charData a,struct charData b)
{
return (a.charHealth>b.charHealth);
}
on the basis of Exp:
bool MyCompExp(struct charData a,struct charData b)
{
return (a.charExp > b.charExp);
}
on the basis of Name:
bool MyCompName(struct charData a,struct charData b)
{
int r = a.charName.compare(b.charName);
return (r > 0);
}
code:
#include<algorithm>
#include<iostream>
#include<fstream>
using namespace std;
struct charData
{
string charName;
float charHealth;
int charExp;
};
bool MyCompHealth(struct charData a,struct charData b)
{
return (a.charHealth<b.charHealth);
}
//on the basis of Exp:
bool MyCompExp(struct charData a,struct charData b)
{
return (a.charExp < b.charExp);
}
//on the basis of Name:
bool MyCompName(struct charData a,struct charData b)
{
int r = a.charName.compare(b.charName);
return !(r > 0);
}
int main() {
const int NUM_COLS = 3;
int NUM_ROWS = 6;
int charNumber = 0;
int userInput;
int loop = 0;
int i,j;
string line;
ifstream myIn; // Declare read file stream
myIn.open("party.dat",ios::in); // Open file
struct charData charArray[NUM_ROWS];
while( !myIn.eof() ) //taking input
{
for ( i = 0; i < NUM_ROWS ; i++)
{
myIn >> charArray[i].charName;
myIn >> charArray[i].charHealth;
myIn >> charArray[i].charExp;
}
}
cout<<"How you want to sort?(1:health 2:Exp 3:Name)\n";
cin>>userInput; //input from user how he wants to sort
if(userInput==1)
{
//sorting on the basis of health
sort(charArray,charArray+NUM_ROWS,MyCompHealth);
}
else if(userInput==1)
{
//sorting on the basis of Experience
sort(charArray,charArray+NUM_ROWS,MyCompExp);
}
else
{
//sorting on the basis of Name
sort(charArray,charArray+NUM_ROWS,MyCompName);
}
//display result
for ( i = 0; i < NUM_ROWS ; i++)
{
cout<<charArray[i].charName<<" ";
cout<<charArray[i].charHealth<<" ";
cout<<charArray[i].charExp<<endl;
}
return 0;
}

Split String into parts in c++

I have just started learning c++ and I was thinking is their any way of splitting strings. Lemme make it more clear.
Suppose user enters the string, date of birth in the following format dd-mm-yy. Now I wish to store the date, month and year in 3 different variables. So how do I go about ??
P.S : I googled a bit and found that this can be accomplished using the boot::regex. But still, I was wondering if there was any easier way of doing the same. Being a beginner hampers me. :P
Anyways, Any help would be appreciated.
To Brief:
I want something like this.
Enter date: //Accept the date
22-3-17 //Input by user
Date : 22 //Output
Month: 3 //Output
Year : 17 //Output
You can use sscanf function:
http://en.cppreference.com/w/cpp/io/c/fscanf
#include <iostream>
#include <string>
using namespace std;
int main(int argc, const char * argv[]) {
string date;
cin>>date;
int day, month, year;
sscanf(date.c_str(), "%d-%d-%d", &day, &month, &year);
cout << day << ' ' << month << ' ' << year;
return 0;
}
There are different methods you could do this. For the 'easiest' way I would suggest using std::string::find() methods( http://www.cplusplus.com/reference/string/string/find/) in combination with std::string::substr() method (http://www.cplusplus.com/reference/string/string/substr/)
For the use of regex you do not need boost: http://www.cplusplus.com/reference/regex/
You can use the operator [] to get the characters of the string.
http://www.cplusplus.com/reference/string/string/operator[]/
Here is a full working program that has been compiled, built and successfully ran on an Intel Core 2 Quad Extreme running Windows 7 64-bit using Visual Studio 2015 Community edition that has retrieved the information the OP had asked for in a particular format, parsed the string of information or data appropriately, and displayed the results in the requested or required format.
stdafx.h
#ifndef STDAFX_H
#define STDAFX_H
#include <vector>
#include <algorithm>
#include <iterator>
#include <tchar.h>
#include <conio.h>
#include <string>
#include <iostream>
#include <iomanip>
enum ReturnCode {
RETURN_OK = 0,
RETURN_ERROR = 1,
}; // ReturnCode
#endif // STDAFX_H
stdafx.cpp
#include "stdafx.h"
Utility.h
#ifndef UTILITY_H
#define UTILITY_H
class Utility {
public:
static void pressAnyKeyToQuit();
static std::string toUpper( const std::string& str );
static std::string toLower( const std::string& str );
static std::string trim( const std::string& str, const std::string elementsToTrim = " \t\n\r" );
static unsigned convertToUnsigned( const std::string& str );
static int convertToInt( const std::string& str );
static float convertToFloat( const std::string& str );
static std::vector<std::string> splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true );
private:
Utility(); // Private - Not A Class Object
Utility( const Utility& c ); // Not Implemented
Utility& operator=( const Utility& c ); // Not Implemented
template<typename T>
static bool stringToValue( const std::string& str, T* pValue, unsigned uNumValues );
template<typename T>
static T getValue( const std::string& str, std::size_t& remainder );
}; // Utility
#include "Utility.inl"
#endif // UTILITY_H
Utility.inl
// ----------------------------------------------------------------------------
// stringToValue()
template<typename T>
static bool Utility::stringToValue(const std::string& str, T* pValue, unsigned uNumValues) {
int numCommas = std::count(str.begin(), str.end(), ',');
if (numCommas != uNumValues - 1) {
return false;
}
std::size_t remainder;
pValue[0] = getValue<T>(str, remainder);
if (uNumValues == 1) {
if (str.size() != remainder) {
return false;
}
}
else {
std::size_t offset = remainder;
if (str.at(offset) != ',') {
return false;
}
unsigned uLastIdx = uNumValues - 1;
for (unsigned u = 1; u < uNumValues; ++u) {
pValue[u] = getValue<T>(str.substr(++offset), remainder);
offset += remainder;
if ((u < uLastIdx && str.at(offset) != ',') ||
(u == uLastIdx && offset != str.size()))
{
return false;
}
}
}
return true;
} // stringToValue
Utility.cpp
#include "stdafx.h"
#include "Utility.h"
// ----------------------------------------------------------------------------
// pressAnyKeyToQuit()
void Utility::pressAnyKeyToQuit() {
std::cout << "Press any key to quit" << std::endl;
_getch();
} // pressAnyKeyToQuit
// ----------------------------------------------------------------------------
// toUpper()
std::string Utility::toUpper(const std::string& str) {
std::string result = str;
std::transform(str.begin(), str.end(), result.begin(), ::toupper);
return result;
} // toUpper
// ----------------------------------------------------------------------------
// toLower()
std::string Utility::toLower(const std::string& str) {
std::string result = str;
std::transform(str.begin(), str.end(), result.begin(), ::tolower);
return result;
} // toLower
// ----------------------------------------------------------------------------
// trim()
// Removes Elements To Trim From Left And Right Side Of The str
std::string Utility::trim(const std::string& str, const std::string elementsToTrim) {
std::basic_string<char>::size_type firstIndex = str.find_first_not_of(elementsToTrim);
if (firstIndex == std::string::npos) {
return std::string(); // Nothing Left
}
std::basic_string<char>::size_type lastIndex = str.find_last_not_of(elementsToTrim);
return str.substr(firstIndex, lastIndex - firstIndex + 1);
} // trim
// ----------------------------------------------------------------------------
// getValue()
template<>
float Utility::getValue(const std::string& str, std::size_t& remainder) {
return std::stof(str, &remainder);
} // getValue <float>
// ----------------------------------------------------------------------------
// getValue()
template<>
int Utility::getValue(const std::string& str, std::size_t& remainder) {
return std::stoi(str, &remainder);
} // getValue <int>
// ----------------------------------------------------------------------------
// getValue()
template<>
unsigned Utility::getValue(const std::string& str, std::size_t& remainder) {
return std::stoul(str, &remainder);
} // getValue <unsigned>
// ----------------------------------------------------------------------------
// convertToUnsigned()
unsigned Utility::convertToUnsigned(const std::string& str) {
unsigned u = 0;
if (!stringToValue(str, &u, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
throw strStream.str();
}
return u;
} // convertToUnsigned
// ----------------------------------------------------------------------------
// convertToInt()
int Utility::convertToInt(const std::string& str) {
int i = 0;
if (!stringToValue(str, &i, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
throw strStream.str();
}
return i;
} // convertToInt
// ----------------------------------------------------------------------------
// convertToFloat()
float Utility::convertToFloat(const std::string& str) {
float f = 0;
if (!stringToValue(str, &f, 1)) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
throw strStream.str();
}
return f;
} // convertToFloat
// ----------------------------------------------------------------------------
// splitString()
std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {
std::vector<std::string> vResult;
if ( strDelimiter.empty() ) {
vResult.push_back( strStringToSplit );
return vResult;
}
std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
while ( true ) {
itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
std::string strTemp( itSubStrStart, itSubStrEnd );
if ( keepEmpty || !strTemp.empty() ) {
vResult.push_back( strTemp );
}
if ( itSubStrEnd == strStringToSplit.end() ) {
break;
}
itSubStrStart = itSubStrEnd + strDelimiter.size();
}
return vResult;
} // splitString
main.cpp
#include "stdafx.h"
#include "Utility.h"
int main () {
std::string date;
std::cout << "Enter Date in DD-MM-YY Format.\n" << std::endl;
std::getline( std::cin, date );
std::vector<std::string> vResults = Utility::splitString( date, "-" );
std::cout << "\nDate : " << vResults[0] << std::endl
<< "Month: " << vResults[1] << std::endl
<< "Year : " << vResults[2] << std::endl << std::endl;
Utility::pressAnyKeyToQuit();
return 0;
} // main

c++ template issue [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I'm currently using the class given in this tutorial: http://www.dreamincode.net/forums/topic/183191-create-a-simple-configuration-file-parser/
Initially it worked fine, but since I split the single source file into seperate header and cpp files I've been unable to call the getValueOfKey function
header:
#ifndef CONFIGFILE_H
#define CONFIGFILE_H
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <fstream>
#include <typeinfo>
class ConfigFile
{
private:
std::map<std::string, std::string> contents;
std::string fName;
void removeComment(std::string &line) const;
bool onlyWhitespace(const std::string &line) const;
bool validLine(const std::string &line) const;
void extractKey(std::string &key, size_t const &sepPos, const std::string &line) const;
void extractValue(std::string &value, size_t const &sepPos, const std::string &line) const;
void extractContents(const std::string &line);
void parseLine(const std::string &line, size_t const lineNo);
void ExtractKeys();
public:
ConfigFile(const std::string &fName);
bool keyExists(const std::string &key) const;
template <typename ValueType>
ValueType getValueOfKey(const std::string &key, ValueType const &defaultValue) const;
};
#endif /* CONFIGFILE_H */
cpp:
#include "ConfigFile.h"
std::map<std::string, std::string> contents;
std::string fName;
template <typename T>
static std::string T_to_string(T const &val)
{
std::ostringstream ostr;
ostr << val;
return ostr.str();
}
template <typename T>
static T string_to_T(std::string const &val)
{
std::istringstream istr(val);
T returnVal;
if (!(istr >> returnVal))
std::cout << "CFG: Not a valid " << (std::string)typeid (T).name() << " received!\n" << std::endl;
return returnVal;
}
template <>
std::string string_to_T(std::string const &val)
{
return val;
}
void ConfigFile::removeComment(std::string &line) const
{
if (line.find(';') != line.npos)
line.erase(line.find(';'));
}
bool ConfigFile::onlyWhitespace(const std::string &line) const
{
return (line.find_first_not_of(' ') == line.npos);
}
bool ConfigFile::validLine(const std::string &line) const
{
std::string temp = line;
temp.erase(0, temp.find_first_not_of("\t "));
if (temp[0] == '=')
return false;
for (size_t i = temp.find('=') + 1; i < temp.length(); i++)
if (temp[i] != ' ')
return true;
return false;
}
void ConfigFile::extractKey(std::string &key, size_t const &sepPos, const std::string &line) const
{
key = line.substr(0, sepPos);
if (key.find('\t') != line.npos || key.find(' ') != line.npos)
key.erase(key.find_first_of("\t "));
}
void ConfigFile::extractValue(std::string &value, size_t const &sepPos, const std::string &line) const
{
value = line.substr(sepPos + 1);
value.erase(0, value.find_first_not_of("\t "));
value.erase(value.find_last_not_of("\t ") + 1);
}
void ConfigFile::extractContents(const std::string &line)
{
std::string temp = line;
temp.erase(0, temp.find_first_not_of("\t "));
size_t sepPos = temp.find('=');
std::string key, value;
extractKey(key, sepPos, temp);
extractValue(value, sepPos, temp);
if (!keyExists(key))
contents.insert(std::pair<std::string, std::string > (key, value));
else
std::cout << "CFG: Can only have unique key names!\n" << std::endl;
}
void ConfigFile::parseLine(const std::string &line, size_t const lineNo)
{
if (line.find('=') == line.npos)
std::cout << "CFG: Couldn't find separator on line: " << T_to_string(lineNo) << "\n" << std::endl;
if (!validLine(line))
std::cout << "CFG: Bad format for line: " << T_to_string(lineNo) << "\n" << std::endl;
extractContents(line);
}
void ConfigFile::ExtractKeys()
{
std::ifstream file;
file.open(fName.c_str());
if (!file)
std::cout << "CFG: File " << fName << " couldn't be found!\n" << std::endl;
std::string line;
size_t lineNo = 0;
while (std::getline(file, line))
{
lineNo++;
std::string temp = line;
if (temp.empty())
continue;
removeComment(temp);
if (onlyWhitespace(temp))
continue;
parseLine(temp, lineNo);
}
file.close();
}
ConfigFile::ConfigFile(const std::string &fName)
{
this->fName = fName;
ExtractKeys();
}
bool ConfigFile::keyExists(const std::string &key) const
{
return contents.find(key) != contents.end();
}
template <typename ValueType>
ValueType ConfigFile::getValueOfKey(const std::string &key, ValueType const &defaultValue = ValueType()) const
{
if (!keyExists(key))
return defaultValue;
return string_to_T<ValueType> (contents.find(key)->second);
}
I am attempting to call it using the same method as when it was a single file, something like std::cout << Config.getValueOfKey<std::string>("test");, but now I am getting the following compiler error
main.cpp: In function 'int main(int, char**)':
main.cpp:29:71: error: no matching function for call to 'ConfigFile::getValueOfKey(const char [5])'
main.cpp:29:71: note: candidate is:
In file included from main.h:17:0,
from main.cpp:9:
ConfigFile.h:35:12: note: template<class ValueType> ValueType ConfigFile::getValueOfKey(const string&, const ValueType&) const
ConfigFile.h:35:12: note: template argument deduction/substitution failed:
main.cpp:29:71: note: candidate expects 2 arguments, 1 provided
Given my poor grasp on templates I can't really see what this error is trying to tell me, I have tried passing a direct string instead of a char array to no avail. Any help or explanation would be greatly appreciated, my heads worn a nice hole in the desk over the past few hours.
You declared the method taking 2 arguments:
ValueType getValueOfKey(const std::string &key, ValueType const &defaultValue) const;
// | |
// first parameter second parameter
and only supply one:
Config.getValueOfKey<std::string>("test");
I've yet to encounter a compiler that guesses what you mean without any help.
You'll need to move the default to the header file, where you declare the method:
ValueType getValueOfKey(const std::string &key, ValueType const &defaultValue = ValueType()) const;
// | |
// first parameter second parameter
You'll probably get a linker error afterwards, so you might want to check this.