Alphabetical string arrays sorting - c++

I have posted previously and got some very helpful responses. I need to read in people's info (such as ID, age, etc.) from a text file into different arrays.
Records are like this
2012317109 Jamie Carles Smith Male 65 (different bits of info are separated by TAB, lines ending with newline)
However, regarding the ID numbers, I am told to use extraction operator (<<) to get the ID number as an integer and not as a string.
Then I must sort these records by alphabetical string order and then put these in an output file. 
So far, I have the following. How should I proceed? I am not to use maps or vectors, I need to use arrays.
#include <iostream>
#include <fstream>
using namespace std;
void selection_sort( double x[], int length)
{
int i, j, k;
double t;
for (i=0; i<length-1; ++i) {
k = i; //find next smallest elm, x[k]
for (j=i+1; j< length; ++j)
if (x[k] > x[j]) k = j;
//swap x[i] with x[k]
t = x[i]; x[i] = x[k]; x[k] = t;
}
}
int main () {
ifstream fin;
ofstream fout;
fin.open("input.txt");
fout.open("output.txt");
if (fin.fail()) {
cout << "Fail to open inout.txt" << endl;
exit(1);
}
struct row{string ID, name, rest;};
int x;
fout << x << endl;
row *rows=new row[x];
for (int i=0; i<x; ++i) {
getline (fin, rows[i].ID, '\t'); // ID
getline (fin, rows[i].name, '\t'); // name
getline (fin, rows[i].rest );
}
selection_sort (row[], x);
//complier error this line: expected primary expression before [ token.
}

The easiest approach is probably to use std::sort (from the header <algorithm>), and define an
bool operator<(row const& lhs, row const& rhs)
{
// delegates to operator< for std::string
return lhs.name < rhs.name;
}
and then call the STL algorithm
// uses the above defined operator< for row objects
std::sort(rows, rows + x);
and then write the sorted output.

Related

HW Help - Reading from file character by character C++

For a project I am currently working on, I have to read from a file and depending on the certain character in the file, output either a 1 or 0 to an array.
So here is an example of file input:
* * *
* * *
** ** **
*** *
And here is the function I have written to handle this:
void input (int cellGrid[][MAX]) //takes info from a .txt and puts it into an array
{
ifstream infile; //declare a file variable
int row;
int column;
int number;
infile.open("life.txt"); //open a file
while(infile>>row>>column) { //inserts bacteria places into array
cout << row << " " << column << endl;
cellGrid[row][column]=1; //makes it equal one if bacteria is present
}
infile.close(); //closes file
}
My thinking was that the function needs to see if there is a character that exists and if so, place a 1 in its respective position ([row][column]) in the array. However with this current code, nothing is input into my array.
Generally in C++ use std::vector wherever possible.
Array example:
You have to go through the file and record position of each *. Then set it to 1 for that position. Something like the following (we use getline and i as counter for rows, then we loop through the line using j as counter for columns):
#include <fstream>
#include <string>
using namespace std;
void input(int cellGrid[][100]) {
ifstream infile;
infile.open("life.txt");
int i = 0;
for (string line; getline(infile, line); ++i)
{
for (size_t j = 0; j < line.size(); ++j) {
if (line[j] == '*') {
cellGrid[i][j] = 1;
}
}
}
infile.close();
}
Vector example #1:
Here you can find a solution using std::vector. It will be always in a size rows x columns. One thing it requires is to pass default constructed vector and not constructed with vector(size_type count); c-tor. You can write your own version that doesn't have this problem:
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void input(vector<vector<int>> &cellGrid) {
ifstream infile;
infile.open("life.txt");
int i = 0;
for (string line; getline(infile, line); ++i)
{
cellGrid.push_back(vector<int>(line.size()));
for (size_t j = 0; j < line.size(); ++j) {
if (line[j] == '*') {
cellGrid[i][j] = 1;
}
}
}
infile.close();
}
int main() {
vector<vector<int>> cellGrid;
vector<vector<int>> cellGrid2(100);
input(cellGrid);
//input(cellGrid2); - THIS WILL THROW AN EXCEPTION
return 0;
}
Vector example #2:
It would be even better for you function to return a newly created and populated vector:
#include <fstream>
#include <string>
#include <vector>
using namespace std;
vector<vector<int>> input() {
ifstream infile;
infile.open("life.txt");
vector<vector<int>> cell_grid;
int i = 0;
for (string line; getline(infile, line); ++i)
{
cell_grid.push_back(vector<int>(line.size()));
for (size_t j = 0; j < line.size(); ++j) {
if (line[j] == '*') {
cell_grid[i][j] = 1;
}
}
}
infile.close();
return cell_grid;
}
int main() {
auto vec = input();
return 0;
}
My thinking goes like this:
set row to 0
while can read a line from file
set column to 0
for each character on line
if character is '*'
set cellGrid(row,column) to 1
else
set cellGrid(row,column) to 0
increment column
increment row
You may want additional logic to trap row or column trying to go out of bounds or characters that aren't ' ' or '*'.

How do I fit in a comma separated number matrix into a dynamically allocated array in C++?

I have a file that stores a number matrix of unknown shape in format like
-1,4,12,5.7
2.1,3,-10,3.3
7.1,1.11,12,10
I attempt to store the matrix in a dynamically allocated array because I cannot hard code the number of row and column. For this part, I used pointer-to-pointer and a demo is as below,
#include <iostream>
using namespace std;
int main()
{
// Rather than user input, I need to change this part to deciding the shape of the matrix myself
int row, col;
cout << "Enter row number and column number, separated with a space:\n";
cin >> row >> col;
int** p_p_grid = new int* [row];
for(int i = 0; i < row; i++)
{
p_p_grid[i] = new int[col];
}
// Fill in the entries
for(int i = 0; i < row; i++)
{
for(int j = 0; j < col; j++)
{
// (i + 1) * (j + 1) needs to be replaced by true entries in the matrix
p_p_grid[i][j] = (i + 1) * (j + 1);
}
}
return 0;
}
But what is an efficient way to decide the shape of a comma separated number block before assigning the number one by one? And also how do I import a CSV-structured matrix in C++? (For some reason, I don't want to use a vector type, so please focus on an array)
what is an efficient way to decide the shape of a comma separated
number block before assigning the number one by one?
Assuming you're reading from a file stream, the easiest way, would be to read the file twice: one for counting rows and commas, on for doing the real input.
Here an example of how to detect the end of the matrix, stoping when the number of elements of a new line don't match the format of the matrix:
int nrows=1, ncols=0;
string line;
while (getline(ifs, line)) {
int n=1;
for (auto x: line) // count commas in the line
if (x==',')
n++;
if (!ncols)
ncols = n; // first line sets th enumber of columns
else if (n == ncols) // subsequent lines increase the row count
nrows++;
else break; // unless the format does'n match anymore
}
ifs.clear(); // remove eof
ifs.seekg (0, ifs.beg); // rewind
Online demo
What causes lack of efficiency in this approach, is that you read the file twice. For this reason, by the way, you can't use this approach for reading cin: you can't rewind.
You can optimize this either by caching the lines read (but you'd need to manage a rowing array of strings, as you're not allowed to use vectors), or by letting the matrix grow dynamically (which no longer correspond to your question as this would not provide the matrix size upfront).
how do I import a CSV-structured matrix in C++
Within each line, just read the doubles, followed by a char (which should be the ','):
char forget_me;
for (int i=0; i<nrows; i++)
for (int j=0; j<ncols; j++) {
cin >> p_p_grid[i][j];
if (j<ncols-1)
cin>>forget_me;
}
I know you didn't want a vector-based solution, but here's one anyway
int main() {
ifstream input("input.txt");
if(!input) {
cerr << "couldn't open file" << endl;
exit(1);
}
double number;
vector<vector<double>> matrix;
vector<double> current_row;
while(input >> number) { // loop once for each number in the file
current_row.push_back(number);
int next_char = input.get(); // should be ',' or '\n'
if(next_char == '\n') {
// current row is finished
matrix.push_back(current_row);
current_row.clear();
}
}
// now print the matrix back out again
for(auto const & one_row : matrix) {
for(auto one_number : one_row) {
cout << "\t," << one_number;
}
cout << endl;
}
}
It a bit fiddly. Use either C++ iostream or C stdio.h, and read in a whole line. So if using getline / fgets, you need a very big buffer, say 8k. Now for the first line, parse into fields. For a first attempt, simply count the commas, but the actual rules are more complicated than that.
The go through line by line, extracting the data. Since you've no say over the number of rows, you've no choice other than to dynamically grow the buffer for each line. An STL vector makes this easy for you - just push it back and the buffer will grow. However if you want you can use this structure
int **p_p_grid = 0;
int Nrows = 0;
int Ncolumns = 0;
/* ( for first line, fill the number of columns) */
/* for each line */
p_p_grid = realloc((Nrows + 1) * sizeof(int *));
if(!p_p_grid)
memory_failure();
p_p_grid[Nrows] = malloc(Ncolums * sizeof(int));
if(!p_p_grid[Nrows])
memory_failure();
for(i=0;i<Ncolumns;i++)
p_p_grid[Nrows][i] = /* parse logic here */
Nrows++;
As noted, I would use std::vector instead. Considering that each row have a fixed number of elements, I would also be using std::array. Perhaps doing something like this:
#include <vector> // For std::vector
#include <array> // For std::array
#include <string> // For std::string and std::getline
#include <fstream> // For std::ifstream
#include <sstream> // For std::isstream
int main()
{
std::vector<std::array<double, 4>> grid;
std::ifstream input{"input.txt"};
std::string line;
// Outer loop reads the rows
while(std::getline(input, line))
{
int i = 0;
std::istringstream iss{line};
std::array<double, 4> values;
double value;
// Inner loop extracts the values on each row
while (iss >> value)
{
values[i] = value;
// Skip over the comma
iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
}
grid.push_back(values);
}
// Now print the values
int row_number = 1;
for (auto const& row : grid)
{
std::cout << "Row " << row_number++ << ": ";
for (auto const value : row)
{
std::cout << value << ' ';
}
std::cout << '\n';
}
}
Note that I haven't actually tested the code above. It compiles cleanly though.

How to calculate something in c++?

I am brand new in C++ and data structure. So I am learning and any advice is appreciated. I am trying to extract a csv file which looks like the following in notepad.
Sig1,Sig2,Sig3,Sig4,Sig5,Sig6,Sig7,Sig8
45,200,45,200,45,200,45,200
45,200,45,200,45,200,45,200
45,200,45,200,45,200,45,200
45,200,45,200,45,200,45,200
45,200,45,200,45,200,45,200
45,200,45,200,45,200,45,200
45,200,45,200,45,200,45,200
45,200,45,200,45,200,45,200
I want to calculate the moving average for each column and print out the results for each column. I do know how to read and print the whole csv file by rows and I also know how to calculate the moving average. But I am finding it difficult to put the two things together because I want to calculate the results by "columns" and not rows.I want to use vector(queue(string)) to read the file.
My idea: Suppose I want to read the 1st cell of the row and put it in queue1, the next in queue2 and so on, then I move on to the 2nd row and repeat the process. So the first column would be vector of queue1, then 2nd column would be vector of queue2 and so on. Then I perform moving average for each vector of queue (or column).
Does this sound like a viable idea? Check my code below.
I used the last code of this link to get an idea about how to extract the table from a csv file:
How to read-write into/from text file with comma separated values
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <queue>
using namespace std;
void readCSV(istream &input, std::vector< std::queue<std::string> > &output)
{
//fstream file("c://data//test_data.csv", ios::in);
string csvLine;
// read every line from the stream
while( getline(input, csvLine) )
{
istringstream csvStream(csvLine);
queue<string> csvColumn;
string csvElement;
// read every element from the line that is seperated by commas
// and put it into the vector or strings
while( getline(csvStream, csvElement, ',') )
{
csvColumn.push(csvElement);
}
output.push_back(csvColumn);
}
}
int main()
{
ofstream myfile;
string sig;
fstream file("c://data//test_data.csv", ios::in);
if(!file.is_open())
{
cout << "File not found!\n";
return 1;
}
// typedef to save typing for the following object
typedef queue<string> Q;
typedef vector<Q> csvVector;
csvVector csvData;
const int Number_Size = 8;
int n =8;
double sum1 = 0.0;
double movingAverage = 0.0;
readCSV(file, csvData);
// Read data and perform moving average for each column
for(csvVector::iterator i = csvData.begin(); i != csvData.end(); ++i)
{
for(vector<Q>::iterator j = i ->begin(); j !=i ->end(); ++j)
{
for (int i = 0; i <= (Number_Size - n); i++)
{
sum1 = 0.0;
for( int i=0; int j = i; j < i + n; j++)
{
sum1 += sig[j];
movingAverage = sum1/n;
cout << movingAverage << endl;
}
}
}
}
myfile.close();
system("pause");
}
The problem with your approach is the way how you parse the csv file and store it. Your code creates a queue holding strings for each row and adds all created queues in a vector. This becomes problematic when you now want to iterate through a column.
Try to create a vector for each element you find in the first row. When processing any other row than the first, access the already existing vectors and add the strings. If you follow this approach, you will end up with vectors holding the content of the columns instead of the rows as it stands now. Here is some sample code:
void readCSV(istream &input, vector<vector<string>>& vOutput)
{
int iRowCnt = 0;
string csvLine;
// read every line from the stream
while( getline(input, csvLine) )
{
int iColCnt = 0;
istringstream inpStreamLine(csvLine);
string strElement;
// read every element from the line that is separated by commas
while( getline(inpStreamLine, strElement, ',') )
{
// if first line, create vector for each column
if (iRowCnt == 0)
{
vector<string> vColumn;
vOutput.push_back(vColumn);
}
// access the vector with index iColCnt and add sub string
vOutput.at(iColCnt).push_back(strElement);
iColCnt++;
}
iRowCnt++;
}
}
void getNumericValues(vector<vector<string>> &vColumns, vector<vector<int>>& vOutput)
{
// iterate across rows (signals)
for (vector< vector<string> >::iterator iterCol = vColumns.begin() ; iterCol != vColumns.end() ; ++iterCol)
{
vector<int> vColumn;
vector<string> *vCol = &(*iterCol);
// iterate across columns (signal values) while skipping first line
// convert strings to integer values and add them to vNumValues
for (vector<string>::iterator iterRow = vCol->begin()+1; iterRow < vCol->end(); ++iterRow)
{
string strElem = *iterRow;
string::size_type sz;
vColumn.push_back(stoi(strElem, &sz));
}
vOutput.push_back(vColumn);
}
}
void getMovingAveragesColumn(vector<int> &vNumValues, int iWndSize, vector<int>& vOutput)
{
if (vNumValues.size()<iWndSize) // check if we have enough values
return;
for (vector<int>::iterator iter = vNumValues.begin() ; iter < vNumValues.end()-iWndSize ; ++iter)
{
int iMovAvg = 0;
for (int ii=0; ii<iWndSize; ii++)
{
iMovAvg+= *(iter+ii);
}
iMovAvg /= iWndSize;
vOutput.push_back(iMovAvg);
}
}
void getMovingAveragesMatrix(vector<vector<int>> &vNumValues, int iWndSize, vector<vector<int>>& vOutput)
{
for (vector<vector<int>>::iterator iterCol = vNumValues.begin() ; iterCol < vNumValues.end() ; ++iterCol)
{
vector<int> *vCol = &(*iterCol);
vector<int> vMovAvg;
getMovingAveragesColumn(*vCol, iWndSize, vMovAvg);
vOutput.push_back(vMovAvg);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
ofstream myfile;
fstream file("c://tmp//test_data.csv", ios::in);
if(!file.is_open())
{
cout << "File not found!\n";
return 1;
}
// vector of vectors for storing strings in matrix form
vector<vector<string>> vData;
readCSV(file, vData);
// convert to integers
vector<vector<int>> vNumValues;
getNumericValues(vData, vNumValues);
// get moving average values
vector<vector<int>> vMovAverages;
getMovingAveragesMatrix(vNumValues, 3, vMovAverages);
// print the moving average values
for (vector<vector<int>>::iterator iterCol = vMovAverages.begin() ; iterCol < vMovAverages.end() ; ++iterCol)
{
vector<int> *vCol = &(*iterCol);
for (vector<int>::iterator iterRow= vCol->begin() ; iterRow < vCol->end() ; ++iterRow)
{
cout<< *iterRow << " ";
}
cout<<"\n";
}
myfile.close();
system("pause");
}

Reading the specific data from the file using Vector function C++

I am a new in C++ and have difficulties in importing specific data (numbers) from the file.
My input looks like:
Open High Low Close
1.11476 1.11709 1.10426 1.10533
1.10532 1.11212 1.10321 1.10836
1.10834 1.11177 1.10649 1.11139
1.09946 1.10955 1.09691 1.10556
1.10757 1.11254 1.09914 1.10361
1.10359 1.12162 1.10301 1.11595
1.09995 1.10851 1.09652 1.10097
I use the following code which works fine for me to read the second column entirely, however I need to read specific data only. For example the third row/ third column which is 1.10649How can I read specific data? Do I need to use the string to get the row/column and then convert it to int in order to read it in a vector? I am open for any suggestions and would be greatly appreciated if any could help me with this issue.
// Data import 2nd Column
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <vector>
using namespace std;
int main()
{
const int columns = 4;
vector< vector <double> > data;
ifstream market_data("market_data.txt");
if (market_data.is_open()) {
double num;
vector <double> line;
while (market_data >> num) {
line.push_back(num);
if (line.size() == columns) {
data.push_back(line);
line.clear();
}
}
}
vector <double> column;
double col = 2;
for (double i = 0; i < data.size(); ++i) {
column.push_back(data[i][col - 1]);
cout << column[i] << endl;
}
system ("pause");
return 0;
}
You need to use a integer value for indexing (size_t to be precise), change
for (double i = 0; i < data.size(); ++i) {
to
for( size_t i = 0; i < data.size(); ++i) {
// ^^^^^^
Otherwise everything seems fine from your code sample.
If your numbers will always contain 7 characters (i assume it's not binary file), then you could make this simple.
Use seekg() method of ifstream.
Each number fills 10 characters (7 of number, 3 spaces). So, if you have table ROWS x COLUMNS, then to get specific number, you can do this:
const int ROW_LEN = 4
const int DATA_LEN = 10
...
int row,column;
double num;
std::cin >> row; //assume first row is 0
std::cin >> column //assume first column is 0
marked_data.seekg((column*ROW_LEN + row)*DATA_LEN);
marked_data >> num // here is your number
Thank you for replies.. I have solved the issue. So instead of:
vector <double> column;
double col = 2;
for (double i = 0; i < data.size(); ++i) {
column.push_back(data[i][col - 1]);
cout << column[i] << endl;
}
enough to write:
cout << data[2][2] << endl;

For-Loop - value of i is not 0 even if code says 'int i = 0'. i = BIG number, why? How to fix?

I'm designing a program to clean up a text file that contains code. It removes comments, excess spaces/lines, and creates a new line for lines in the file with multiple semi-colons.
I actually got this program to work, but it used arrays. Since I am working on another program that builds on this, except with a more diverse size of data inputs, I'm converting it to use vectors instead of standard arrays so I can re-purpose the program...which is kind of the point.
My problem is that after the program iterates through the first for-loop, the rest of the for-loops initialize the iterator with a value of '3435973836', regardless of proper declaration ('int i = 0', 'int k = 0', etc). I declare them unsigned and omitting singed/unsigned still initializes the value incorrectly (-858993460).
This does 1 of 2 things:
unsigned the loop never starts as the value is too high to start the loop.
omitting makes the loop run for a long, long time.
Any thoughts? I've posted the code below. Please ignore any other errors I've made other than this, as I have been unable to get past this to debug anything else.
EDIT --> SOLVED: the problem that I was passing the vectors by value. But even when I changed it to pass by reference the program would still not work. The actual problem was with Microsoft Visual Studio 2012. Once I PBV once, it corrupted my project. I had to start a new VS project and insert the code. If you do this, be careful you don't run the program while still PBV or you'll have to do it again. I don't know why this happens. Maybe somebody who knows MS Visual Studio could answer that.
Thanks again community!
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void removeComments(vector<string> row, ifstream & myfile);
void sortLines (vector<string> row);
//void cleanCode (vector<string> row);
int main()
{
vector<string> row;
ifstream myfile;
//Open txt file
myfile.open("newdata.txt");
if(myfile.is_open())
{
//Remove comments, create proper lines, and remove/add spaces.
removeComments(row, myfile);
sortLines(row);
//cleanCode(row);
}
else
{
cout<< "ERROR: No file was able to open. Check the file name or location and try again."<< endl << endl;
}
for (unsigned int i = 0; i < row.size(); i++)
{
cout<< row[i] << endl;
}
cout<< endl;
myfile.close();
system("PAUSE");
return 0;
}
//FUNCTIONS
//Removes all comments.
void removeComments(vector<string> row, ifstream & myfile)
{
string line;
while(getline(myfile, line))
{
string tempString;
for(unsigned int i = 0; i < line.length(); i++)
{
//Copy characters to row string array until "//".
//All character following and including "//" will be ignored.
if(line.at(i) == '/' && line.at(i+1) == '/')
{
break;
}
else
{
tempString += line.at(i);
}
}
row.push_back(tempString);
}
}
//Creates a new line after every semi-colon.
void sortLines (vector<string> row)
{
vector<string> tempRow;
string tempLine;
string tempString;
for (unsigned int i = 0; i < row.size(); i++)
{
tempLine = row [i];
for (unsigned int j = 0; j < tempLine.length(); j++)
{
tempString += tempLine[j];
if (tempLine[j] == ';')
{
tempRow.push_back(tempString);
}
}
}
//Revalue row array elements.
//DEBUGGING OUTPUT
for (unsigned int i = 0; i < tempRow.size(); i++)
{
cout<< tempRow[i] << endl;
}
row.clear();
row = tempRow;
}
Okay, this is my by-reference edit:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
void removeComments(vector<string> &row, ifstream & myfile);
void sortLines (vector<string> &row);
//void cleanCode (vector<string> &row);
int main()
{
vector<string> row;
ifstream myfile;
//Open txt file
myfile.open("newdata.txt");
if(myfile.is_open())
{
//Remove comments, create proper lines, and remove/add spaces.
removeComments(row, myfile);
sortLines(row);
//cleanCode(row);
}
else
{
cout<< "ERROR: No file was able to open. Check the file name or location and try again."<< endl << endl;
}
for (unsigned int i = 0; i < row.size(); i++)
{
cout<< row[i] << endl;
}
cout<< endl;
myfile.close();
system("PAUSE");
return 0;
}
//FUNCTIONS
//Removes all comments.
void removeComments(vector<string> &row, ifstream & myfile)
{
string line;
while(getline(myfile, line))
{
string tempString;
for(unsigned int i = 0; i < line.length(); i++)
{
//Copy characters to row string array until "//".
//All character following and including "//" will be ignored.
if(line.at(i) == '/' && line.at(i+1) == '/')
{
break;
}
else
{
tempString += line.at(i);
}
}
row.push_back(tempString);
}
}
//Creates a new line after every semi-colon.
void sortLines (vector<string> &row)
{
vector<string> tempRow;
string tempLine;
string tempString;
for (unsigned int i = 0; i < row.size(); i++)
{
tempLine = row [i];
for (unsigned int j = 0; j < tempLine.length(); j++)
{
tempString += tempLine[j];
if (tempLine[j] == ';')
{
tempRow.push_back(tempString);
}
}
}
//Revalue row array elements.
//DEBUGGING OUTPUT
for (unsigned int i = 0; i < tempRow.size(); i++)
{
cout<< tempRow[i] << endl;
}
row.clear();
row = tempRow;
}
As others have noted you're:
Passing vectors by value
Using something possibly uninitialized out-of-my-scope (this is nowhere declared/defined in your question) as increment variable
//Creates a new line after every semi-colon.
void sortLines (vector<string> row)
{
vector<string> tempRow;
string tempLine;
string tempString;
for (unsigned int i = 0; i < row.size(); k++) // <-- what is k??
{
Why pass "string line" to removeComments? It should be local to that function cos you don't use it outside. It looks dodgy for the same reason that the passed vectors did.