Reputation: 403
My text.txt looks like this:
1 52 Hayden Smith 18:16 15 M Berlin
2 54 Mark Puleo 18:25 15 M Berlin
3 97 Peter Warrington 18:26 29 M New haven
4 305 Matt Kasprzak 18:53 33 M Falls Church
5 272 Kevin Solar 19:17 16 M Sterling
6 394 Daniel Sullivan 19:35 26 M Sterling
7 42 Kevan DuPont 19:58 18 M Boylston
8 306 Chris Goethert 20:00 43 M Falls Church
The problem is, for line three, the name of town is New Haven, there is a space between New and haven. And also for line four, there is a space between Falls and Church. This creates a problem when I read the file line by line.
So, my code looks like this:
ifstream infile("text.txt", ios::in);
if(!infile)
{
cerr<<"File could not be opend"<<endl;
}
SortedLinked mylist;
int a;
int b;
string c;
string d; // I have eight different variables, representing
string e; // each column, but some lines have nine columns.
int f;
char g;
string h;
string mystr;
int mymin;
int mysec;
while(infile>>a>>b>>c>>d>>e>>f>>g>>h)
{
// This does not work, because as you get to line three, h is New, there
// is no variable to store Haven. Therefore my code can only get to line
// 3.
mystr = c+d;
mymin = convertString<int>(e.substr(0,2));
mysec = convertString<int>(e.substr(3, 4));
Runner M(mystr, f, mymin, mysec);
//M.print();
mylist.additem(M);
}
Also, if you try to do something like this:
replace(h.begin(), h.end(), ' ', '_')
try to fill a underscore between New and haven, it will not work. because this attempts to replace a variable with another, h in here is New, it is not New haven, you cannot get to haven, because haven is another variable. So there is no way for you to replace a space with a underscore.
Also, if you try to do something like this:
while(infile>>a>>b>>c>>d>>e>>f>>g>>h>>i)
This also does not work, because when you get to line 1, there are only right columns, there is nothing to store into variable i. Code like this will only get you to line 1.
if you have some other way to do this, like skipping town name column, column 8 and column 9 (for some lines), because i don't even need the name of town.
Or, you can get rid of the space between New haven and the space between Fall Church. Anything would be appreciated.
Upvotes: 2
Views: 3429
Reputation: 7905
I already have an existing Utility class to work with strings and after working with your file structure for some time this is what I was able to come up with. However, I did have to modify your existing text file for it to work properly. All of your columns have to have a single space (this could be changed to a single tab), and for the last entry where some of the town names have two words, I encapsulated them with double quotes. So now your text.txt
file looks like this:
text.txt
1 52 Hayden Smith 18:16 15 M Berlin
2 54 Mark Puleo 18:25 15 M Berlin
3 97 Peter Warrington 18:26 29 M "New Haven"
4 305 Matt Kasprzak 18:53 33 M "Falls Church"
5 272 Kevin Solar 19:17 16 M Sterling
6 394 Daniel Sullivan 19:35 26 M Sterling
7 42 Kevan DuPont 19:58 18 M Boylston
8 306 Chris Goethert 20:00 43 M "Falls Church"
Here is the working program.
stdafx.h
#ifndef STDAFX_H
#define STDAFX_H
#include <stdio.h>
#include <tchar.h>
#include <conio.h>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
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"
struct Data {
int a;
int b;
std::string c;
std::string d;
std::string e;
int f;
char g;
std::string h;
};
int main() {
std::string strFilename( "text.txt" );
std::ifstream file;
std::string strLine;
std::vector<std::string> vTemp;
std::vector<std::string> vResult;
std::vector<Data> vData;
// Open File For Reading
file.open( strFilename.c_str() );
// Check For Error Of Opening File
if ( !file.is_open() ) {
std::cout << "Error opening file (" << strFilename << ")" << std::endl;
return RETURN_ERROR;
}
// Continue Until End Of File
while( !file.eof() ) {
// Get Single Full Line Save To String
std::getline( file, strLine );
// Split String Using A Double Quote Delimiter
vTemp = Utility::splitString( strLine, "\"" );
// Check To See If vTemp Has More Than One String
if ( vTemp.size() > 1 ) {
// Store The Last Value Of vResult -
// We Need To Use Pop Back To Account For Last Double Quote
vTemp.pop_back(); // Remove Last Double Quote
std::string temp = vTemp.back();
vTemp.pop_back(); // Remove Wanted String From vTemp.
// At This Point We Need To Parse vTemp Again Using Space Delimiter
vResult = Utility::splitString( vTemp[0], " " );
// Need To Account For Last Space In Vector
vResult.pop_back();
// Now We Can Push Our Last String Back Into vResult
vResult.push_back( temp );
} else if ( vTemp.size() == 1 ) {
// Just Parse vTemp Using Space Delimiter
vResult = Utility::splitString( vTemp[0], " " );
}
// Print Out Results For Validity
for ( unsigned u = 0; u < vResult.size(); u++) {
std::cout << vResult.at(u) << " ";
}
std::cout << std::endl;
// Here Is Where You Would Populate Your Variables, Structures Or Classes On Each Pass Of The While Loop.
// With This Structure There Should Only Be 8 Entries Into Our vResult
Data temp;
temp.a = Utility::convertToInt( vResult[0] );
temp.b = Utility::convertToInt( vResult[1] );
temp.c = vResult[2];
temp.d = vResult[3];
temp.e = vResult[4];
temp.f = Utility::convertToInt( vResult[5] );
temp.g = vResult[6].at( 0 ); // Should Only Be One Character In This String
temp.h = vResult[7];
vData.push_back( temp );
}
std::cout << std::endl << std::endl;
// Print Using Structure For Validity
std::cout << "---------------------------------------\n";
for ( unsigned u = 0; u < vData.size(); u++ ) {
std::cout << vData[u].a << " "
<< vData[u].b << " "
<< vData[u].c << " "
<< vData[u].d << " "
<< vData[u].e << " "
<< vData[u].f << " "
<< vData[u].g << " "
<< vData[u].h << std::endl;
}
// Close File
file.close();
Utility::pressAnyKeyToQuit();
return RETURN_OK;
} // main
To the best of my knowledge this is a full working program that was successfully compiled and built on an Intel Quad Core 3.0Ghz running Windows 7 64bit with 8GB Ram using MSVS 2015 Community Edition. There were no compile or build errors and expected data printed properly to the console window.
Upvotes: 0
Reputation: 726589
You can use a little trick: since spaces that you wish to ignore occur only on the last entry, you could use std::getline
instead of >>
to read the name of the town.
string str;
while (getline(infile, str)) { // Read the entire line
stringstream ss(str);
ss>>a>>b>>c>>d>>e>>f>>g;
getline(ss, h);
... // The rest of your code
}
Upvotes: 1
Reputation: 862
You should consider using getline. That way you can read in line by line and then parse the input.
Once you've read the line into a string you can just parse it like you normally would, splitting each substring by a space until you get to the name of the town, wherein you would just split the substring from there to the end of the line into your town name variable.
Upvotes: 2
Reputation: 206607
You can use getline
to read rest of the line.
while( (infile>>a>>b>>c>>d>>e>>f>>g) && getline(infile, h))
Upvotes: 4