JY078
JY078

Reputation: 403

C++ read file line by line and some lines have different columns than the others

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

Answers (4)

Francis Cugler
Francis Cugler

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

Sergey Kalinichenko
Sergey Kalinichenko

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

NickLamp
NickLamp

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

R Sahu
R Sahu

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

Related Questions