How to split using getline() in c++

89 views Asked by At

My Input file,

 AMZN~Amazon.Com Inc~1402.05~+24.10~+1.75%~4854900~6806813~01/26/18~1523
 AAPL~Apple Inc~171.51~+0.40~+0.23%~39128000~6710843~01/26/18~1224`

My code,

 #include <iostream>
 #include <fstream>
 #include <string>
 #include <iomanip>
 using namespace std;

 int main()
 {
    string line1[30];
    string line2[30];
    ifstream myfile("sp03HWstock.txt");
    int a = 0;

    if(!myfile) 
    {
    cout<<"Error opening output file"<<endl;
    system("pause");
    return -1;
    }
    cout
       << left
       << setw(10)
       << "Stock Name              "
       << left
       << setw(5)
       << " Value  "
       << left
       << setw(8)
       << "Quantity   "
       << left
       << setw(5)
       << "   Total Worth  "
       << endl;
  while(!myfile.eof())
  {
   getline(myfile,line1[a],'~');

    cout
        << left
        << setw(10);


   cout<<  line1[a];
  }
}

Desired output,

Stock Name               Value  Quantity      Total Worth  
Amazon.Com Inc          1402.05 +24.10        6806813   
Apple Inc               171.51  +0.23%        6710843

The output I got,

Stock Name               Value  Quantity      Total Worth  
AMZN     Amazon.Com Inc1402.05   +24.10    +1.75%    4854900   6806813   01/26/18  1523
AAPLApple Inc 171.51    +0.40     +0.23%    39128000  6710843   01/26/18  1224`

I am using getline(myfile,line1[a],'~'); to split by '~' and I am not able to perform further split on it. Can some one please help me to figure it out. Thanks!!

2

There are 2 answers

0
paddy On BEST ANSWER

A basic approach is to first give yourself more flexibility and less hard-coding related to the columns. Something like this:

const int MAX_COLUMNS = 6;

struct {
    const char* name;
    int width;
} columns[MAX_COLUMNS] = {
    { NULL },                 // Don't care
    { "Stock Name", 16 },
    { "Value", 10 },
    { "Quantity", 10 },
    { NULL },                 // Don't care
    { "Total Worth", 12 },
};

Then, you can easily output your column headers as follows:

    // Print column headings
    cout << left;
    for (int i = 0; i < MAX_COLUMNS; i++)
    {
        if (columns[i].name)
        {
            cout << setw(columns[i].width) << columns[i].name;
        }
    }
    cout << endl;

For the actual data, a convenient way to handle it is to first read an entire line and then use a std::istringstream to split the contents of that line:

    // Read and process lines
    string line;
    while (getline(myfile, line))
    {
        istringstream iss(line);
        string item;
        int count = 0;
        while (count < MAX_COLUMNS && getline(iss, item, '~'))
        {
            if (columns[count].name)
            {
                cout << setw(columns[count].width) << item;
            }
            ++count;
        }
        cout << endl;
    }

Output:

Stock Name      Value     Quantity  Total Worth 
Amazon.Com Inc  1402.05   +24.10    4854900     
Apple Inc       171.51    +0.40     39128000    

If you need to display your columns in a different order, then you can first split the values into a vector<string> and then output that in whatever order you want.

0
tbxfreeware On

I am using getline(myfile,line1[a],'~'); to split by '~' and I am not able to perform further split on it.

The problem is not splitting the input lines. As was pointed out in the comments that is working fine.

The problem is output format. When you display titles, you use a different width for each field. In addition, you have added padding after each field name which effectively causes the widths set by function setw to be ignored.

// From the OP:
cout
    << left
    << setw(10)
    << "Stock Name              "   // Wider than 10
    << left
    << setw(5)
    << " Value  "                   // Wider than 5
    << left
    << setw(8)
    << "Quantity   "                // Wider than 8
    << left
    << setw(5)
    << "   Total Worth  "           // Wider than 5
    << endl;

Then, when you output the field values, you use a single width:

// From the OP:
cout
    << left
    << setw(10);
cout << line1[a];

It should not surprise you that the columns do not line up.

Read one record at a time

The easiest way to make the columns line up is to strip out all padding, and control the column width using std::setw.

I recommend reading each line (i.e., record) into a string, and then using a stringstream to further parse it:

    std::string line;
    while (std::getline(myfile, line))    // Keep looping when you 
    {                                     //    read a line successfully.
        std::stringstream sst{ line };    // Load the line into a `stringstream`.

        // Parse individual fields by reading from `stringstream`...
    }

Use named fields

Using named fields makes it easier to keep track of the ones you want to output.

// If your course has covered `struct`, you can place 
// these fields within a `struct`.

std::string ticker_symbol;
std::string stock_name;
std::string value;
std::string quantity;
std::string percent_change;
std::string unknown_field1;
std::string total_worth;
std::string trade_date;
std::string unknown_field2;

Read each field separately

// Use an if-statement to verify that each of the fields 
// was successfully extracted from the `stringstream`.

if (std::getline(sst, ticker_symbol, '~') &&
    std::getline(sst, stock_name, '~') &&
    std::getline(sst, value, '~') &&
    std::getline(sst, quantity, '~') &&
    std::getline(sst, percent_change, '~') &&
    std::getline(sst, unknown_field1, '~') &&
    std::getline(sst, total_worth, '~') &&
    std::getline(sst, trade_date, '~') &&
    std::getline(sst, unknown_field2))  // no '~' here.
{
    // Extraction successful. Output the fields you need, 
    // followed by `std::endl` (or '\n'). Use `std::setw` 
    // to set a separate width for each field.
}
else
{
    // Extraction failed. Output an error message.
}

For each field, use the same std::setw command that was used to output its field name.

As this seems to be some sort of homework assignment, I will stop here, and leave the rest of the details up to you.