0

I am writing a c++ prgram and have to read some data from a .txt file.the format looks like the following: "name gender DD/MM/YYYY HH:MM:SS"

I know how to read a .txt file and cin the words one by one, but i dont know how to deal with the symbols "/" or ":".
I have to compare the ages of two person.

Please help!

Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
Bella W
  • 3
  • 1
  • 3
  • 4
    What is the data structure you're writing this to? It sounds like homework. – Albert Perrien Mar 27 '11 at 03:30
  • It's important to all us that you review the answers and ACCEPT the one that solves your problem. – karlphillip Mar 27 '11 at 07:43
  • re albert: ys this is a hw frm school, i took a C++ programming course this semester but i am really regret now >.< btw, what do u mean by "data structure"? – Bella W Mar 28 '11 at 02:26
  • re karlphillip: sorry for replying late, I know I should accept one of the ans that solving my problem, but I am still trying the codes... as this is a programming homework, i didnt type out the whole question, so I need to try the codes by myself first. besides, some of the concepts involved in the ans are not taught by the prof, I have to understand them first – Bella W Mar 28 '11 at 02:31
  • thz for each of the answers provided, I will make a decision soon. tks! – Bella W Mar 28 '11 at 02:32

5 Answers5

2

This is one of those tasks that's really a lot easier with something like sscanf:

sscanf(input_string, 
       "%s %s %d/%d/%d %d:%d:%d", 
       name, gender, &day, &month, &year, &hour, &minute, &second);

You can use iostreams, but not nearly as easily. You have a couple of basic choices. One is to use a locale with ':' and '/' defined as whitespace, which will let you just stream in the values and ignore the separators (but won't warn you about some types of mis-formatting, such as substituting a '/' for a ':' or vice versa.

Another possibility is to read those characters explicitly, and (if you choose) validate them by comparing each separator to what you expect. If you're really serious about assuring the data is in the expected format, this is probably the way to go.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I am quite surprised that there is no Boost Library for this. Boost.Format avantageously replaces `printf` family, but there does not seem to be an equivalent for `scanf` family. – Matthieu M. Mar 27 '11 at 11:51
  • i havent learnt "sscanf" or "locale" yet, so I dont know how to apply what u said, but it seems "locale" would be what I want. may u pls explain further? thanks! – Bella W Mar 28 '11 at 10:56
1

Look at the following link. It will solve your problem.

Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
1

In C you would use sscanf() or more directly, fscanf() but those functions are considered poor style in C++.

I'd suggest something like this:

struct SData 
{
    std::string sName;
    std::string sGender;
    int nDay;
    int nMon;
    int nYear;
    int nHour;
    int nMin;
    int nSec;
};

bool ReadRecord(std::istream &istr, SData &data)
{
    istr >> data.sName
         >> data.sGender
         >> data.nDay;
    istr.ignore(1);       // '/'
    istr >> data.nMon;
    istr.ignore(1);       // '/'
    istr >> data.nYear
         >> data.nHour;
    istr.ignore(1);       // ':'
    istr >> data.nMin;
    istr.ignore(1);       // ':'
    istr >> data.nSec;

    return istr.good();
}

Update

The following example simplifies use by excluding the struct

#include <iostream>
#include <fstream>
#include <string>

bool ReadRecord(std::istream &istr, 
                std::string &sName,
                std::string &sGender,
                int &nDay,
                int &nMon,
                int &nYear,
                int &nHour,
                int &nMin,
                int &nSec)
{
    istr >> sName
         >> sGender
         >> nDay;
    istr.ignore(1); // '/'
    istr >> nMon;
    istr.ignore(1); // '/'
    istr >> nYear
         >> nHour;
    istr.ignore(1); // ':'
    istr >> nMin;
    istr.ignore(1); // ':'
    istr >> nSec;

    return istr.good();
}

int main()
{
    std::string sName0, sGender0, sName1, sGender1;
    int nDay0, nMon0, nYear0, nDay1, nMon1, nYear1;
    int nHour0, nMin0, nSec0, nHour1, nMin1, nSec1;
    const char szFileName[] = "MyData.txt";

    std::ifstream istr(szFileName);
    if (!istr.is_open())
    {
        std::cerr << "Cannot open file\n";
        return 1;
    }
    if (!ReadRecord(istr, sName0, sGender0, nDay0, nMon0, nYear0, nHour0, nMin0, nSec0))
    {
        std::cerr << "Cannot read file\n";
        return 1;
    }
    if (!ReadRecord(istr, sName1, sGender1, nDay1, nMon1, nYear1, nHour1, nMin1, nSec1))
    {
        std::cerr << "Cannot read file\n";
        return 1;
    }
    std::string sYounger;

    if (nYear0 == nYear1)
    {
        if (nMon0 == nMon1)
        {
            if (nDay0 == nDay1)
            {
                if (nHour0 == nHour1)
                {
                    if (nMin0 == nMin1)
                    {
                        if (nSec0 > nSec1)
                        {
                            sYounger = sName0;
                        }
                        else if (nSec0 < nSec1)
                        {
                            sYounger = sName1;
                        }
                    } 
                    else if (nMin0 > nMin1)
                    {
                        sYounger = sName0;
                    }
                    else if (nMin0 < nMin1)
                    {
                        sYounger = sName1;
                    }
                }
                else if (nHour0 > nHour1)
                {
                    sYounger = sName0;
                }
                else if (nHour0 < nHour1)
                {
                    sYounger = sName1;
                }
            }
            else if (nDay0 > nDay1)
            {
                sYounger = sName0;
            }
            else if (nDay0 < nDay1)
            {
                sYounger = sName1;
            }
        }
        else if (nMon0 > nMon1)
        {
            sYounger = sName0;
        }
        else if (nMon0 < nMon1)
        {
            sYounger = sName1;
        }
    }
    else if (nYear0 > nYear1)
    {
        sYounger = sName0;
    }
    else if (nYear0 < nYear1)
    {
        sYounger = sName1;
    }

    if (sYounger.empty())
    {
        std::cout << "The ages are the same\n";
    }
    else
    {
        std::cout << sYounger << "is younger\n";
    }

    return 0;
}
Michael J
  • 7,631
  • 2
  • 24
  • 30
  • would not you want to check that the ignored character DOES match either `/` or `:` ? – Matthieu M. Mar 27 '11 at 11:48
  • i dont wanna be a copycat so i cant write sth that im not really understand. may i ask whats the purpose of the "struct SData" function? and how can i cin data if i write these two functions? – Bella W Mar 28 '11 at 11:03
  • @Matthieu - Probably. I was just trying to keep it simple for a novice. – Michael J Mar 28 '11 at 12:10
  • @user678593 - The struct is just to save passing 9 parameters to the function. The function will work with any stream. To call it you might use: "`bool bOK = ReadRecord(std::cin, mydata);`". If you want to read it from a data file, you open a stream on the data file and pass that. By putting it in a function you can call it repeatedly for multiple records, if you want. – Michael J Mar 28 '11 at 12:11
  • re michael: i think your codes are more clear and good styled, but the unlearnt concepts like struct, istr are too hard for me. i may probably adopt Xeo's suggestion. but thanks again for ur help :)! – Bella W Mar 28 '11 at 12:24
0

Aside from the easiest way with sscanf, like Jerry describes, it's also possible with iostreams. Just read the data you want from the file, until you hit a seperator, and discard that one into a dummy char:

char dummy;
myfile >> first_name >> last_name >> day >> dummy >> month >> dummy ... // and so on.
Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • below is the only function in my program: void main() { char name, gender; int date, month, year, hour, minute, second; char dummy; ifstream inFile; inFile.open("c:\\input.txt"); inFile >> name >> gender >> date >> dummy >> month >> dummy >> year >> hour >> dummy >> minute >> dummy >> second; cout << name << gender << date << month << year << hour << minute << second << endl; } and this is the input file: B F 22/08/1989 22:08:09 but the outputs so strange>< 2 strange characters for the name n gender and a set of 9-digit numbers for the birth date and time, wt happened? – Bella W Mar 28 '11 at 11:25
  • sorry for the above comment, i typed my program as well as the content of the input file, but i dont know how to separate them >. – Bella W Mar 28 '11 at 11:32
  • @user: You need to put in the spaces yourself in the output. `cout << name << " " << gender << " " << ...` and so on. Also, use `int main()`, `void main()` isn't standard. And might you copy the exact output? – Xeo Mar 28 '11 at 11:36
  • after adding the spaces, this is the output: ??-858993460-85899346-85899346 -85899346-85899346-85899346 – Bella W Mar 28 '11 at 12:15
  • @user: I did a quick test, using exactly your code from above and your input file and got the correct and expected output. The problem has to be somewhere else. Can you maybe copy the contents of your main.cpp to [Ideone](http://ideone.com/) (select C++ on the left side)? – Xeo Mar 28 '11 at 12:25
  • @user: Okay, that looks exactly what I had, so no idea about the corrupted output, sorry. But what if you try `std::string` instead of `char`? The names will be longer than one character anyway, like this: http://ideone.com/0y4sG – Xeo Mar 28 '11 at 12:47
  • " ?-858993460/-858993460/-858993460 -858993460:-858993460:-858993460" <--- this is my output o.o – Bella W Mar 28 '11 at 12:50
  • @Bella: Yay, found the cause: Your input.txt is encoded in Unicode. :) If I encode my test.txt the same way (UTF-8 lets say), the output matches. To fix, just save your input.txt in ANSI encoding. Also, [this question](http://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename) may be of interest. – Xeo Mar 28 '11 at 13:03
  • oh no, it doesnt work, the txt file was originally save in ANSI encoding. and i tried unicode as well as UTF-8, but the output still fails ;( – Bella W Mar 28 '11 at 13:05
0

Inspired by How to split a string? I decided to take a shot.

Given the data below:

File: dates.txt

james male 01/02/1987 04:01:02
jerry male 11/06/1965 08:03:04
jon male 21/10/1977 12:05:06

The following application parses each line of the file and stores the information on a temporary object named dude and later adds it to people_list . The parsing of date/time is done through strtok, which is nice function to split a string into tokens. At the end, we iterate on the vector printing what was found.

#include <iostream>
#include <string>
#include <fstream>
#include <iterator>
#include <sstream>
#include <vector>
#include <algorithm>
#include <string.h>

using namespace std;

typedef struct _time
{
    int h; // hour
    int m; // minute
    int s; // seconds
} Time;

typedef struct _date
{
    int y; // year
    int m; // month
    int d; // day
} Date;

class Person
{
public:
    string name;
    string gender;
    Time time;
    Date date;
};


int main()
{
    ifstream in("dates.txt");

    vector<Person> people_list;

    string line;
    while (getline(in,line))
    {
        if (line.empty())
        {
            continue;
        }

        vector<string> tmp;
        istringstream iss(line);

        copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter<vector<string> >(tmp));

        // Parsing name and gender (easy)
        Person dude;
        dude.name = tmp[0];
        dude.gender = tmp[1];

        // Date
        char delim_slash[] = "/";
        char* result = NULL;
        result = strtok(const_cast<char*>(tmp[2].c_str()), delim_slash);
        dude.date.d = atoi(result);
        result = strtok(NULL, delim_slash);
        dude.date.m = atoi(result);
        result = strtok(NULL, delim_slash);
        dude.date.y = atoi(result);

        // Time
        char delim_colon[] = ":";
        result = strtok(const_cast<char*>(tmp[3].c_str()), delim_colon);
        dude.time.h = atoi(result);
        result = strtok(NULL, delim_colon);
        dude.time.m = atoi(result);
        result = strtok(NULL, delim_colon);
        dude.time.s = atoi(result);

        people_list.push_back(dude);
    }

    for (int i = 0; i < people_list.size(); i++)
    {
        cout << "Name: " << people_list[i].name << endl;
        cout << "Gender: " << people_list[i].gender << endl;
        cout << "Date: " << people_list[i].date.d << "/" <<
                            people_list[i].date.m << "/" <<
                            people_list[i].date.y << endl;

        cout << "Time: " << people_list[i].time.h << ":" <<
                            people_list[i].time.m << ":" <<
                            people_list[i].time.s << endl << endl;
    }
}

Outputs:

Name: james
Gender: male
Date: 1/2/1987
Time: 4:1:2

Name: jerry
Gender: male
Date: 11/6/1965
Time: 8:3:4

Name: jon
Gender: male
Date: 21/10/1977
Time: 12:5:6
Community
  • 1
  • 1
karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • My eyes hurt. Never seen such a mix of C and C++, and the `const_cast` + `strtok` is really ugly! – Matthieu M. Mar 27 '11 at 11:50
  • the codes work! but i dont think it suits me as so many libraries and terms i havent seen before, but thanks for ur work! – Bella W Mar 28 '11 at 10:58