Let's say we have a .txt file with data like this:
6
Paris New_York 1
London Berlin 1
Moskow Kiev 1
Paris London 1
New_York Moscow 1
Where 6 is number of Citys and than it means Paris and New_York are connected with value 1, it will always be 1.
Now i would like to turn this into 2D dynamic array. I did it with numbers like this, but i don't know how should i do this with strings.
For numbers:
ifstream myfile("info.txt");
if (myfile.is_open()) {
getline(myfile, line);
istringstream(line) >> Number;
}
int **matrix= new int*[Number];
for (int i = 0; i < Number; i++) {
matrix[i] = new int[Number];
}
while (getline(myfile, line)) {
cout << line << '\n';
std::stringstream linestream(line);
int row;
int column;
int value;
if (linestream >> row >> column >> value)
{
a[row-1][column-1] = value;
a[column-1][row-1] = value;// mirror
}
So how can i do this for strings?
Thank you for your helpful answers
You need an unordered_map<string, int>, beside the matrix, to map string to indexes. Here is my solution:
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <unordered_map>
using namespace std;
int main() {
string line;
int Number;
ifstream myfile("info.txt");
if (myfile.is_open()) {
getline(myfile, line);
istringstream(line) >> Number;
}
int **matrix= new int*[Number];
for (int i = 0; i < Number; i++) {
matrix[i] = new int[Number](); // note () at the end for initialization to 0
}
unordered_map<string, int> citiesMap; // to map cities (string) to indexes (int)
int cityIndex = 0;
while (getline(myfile, line)){
std::stringstream linestream(line);
string row;
string column;
int value;
if (linestream >> row >> column >> value) {
if(citiesMap.find(row) == citiesMap.cend())
citiesMap[row] = cityIndex++; // add city to the map if it doesn't exist
if(citiesMap.find(column) == citiesMap.cend())
citiesMap[column] = cityIndex++; // add city to the map if it doesn't exist
matrix[citiesMap[row]][citiesMap[column]] = value;
matrix[citiesMap[column]][citiesMap[row]] = value;// mirror
}
}
for(auto x: citiesMap) {
cout << x.first << ": " << x.second << endl;
}
cout << endl;
for(int i = 0; i < Number; i++) {
for(int j = 0; j < Number; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
// matrix should be freed here
}
You may optionally keep unique cities in a vector (or array) to access cities from their indexes. Don't forget to free the memory. Also, you may use std::array for the matrix and don't bother with memory issues.
Related
I am learning C++, and one of my lessons is having me read from a file into an an array of structs(is that how I refer to this?). I am having issues where my code compiles fine, but sits and spins on a blank screen. I am fairly sure that the issue lies with how I read the file in.
#include <fstream>
#include <iomanip>
#include <iostream>
using namespace std;
int main() {
ifstream inputFile;
const int SIZE = 150;
int count = 0;
int index[150] = {};
inputFile.open("lab9input.dat");
struct Student {
string name;
int studentID;
int credits;
int gpa;
} students[SIZE];
while (count < SIZE && inputFile >> students[count].name >>
students[count].studentID >>
students[count].credits >> students[count].gpa) {
count++;
}
cout << endl << "The highest GPA student(s) is: " << endl;
for (int i = 0; i < count; i++) {
int high = 0, j = 0;
if (students[i].gpa > high) {
high = students[i].gpa;
for (int k = 0; k < 150; k++) {
index[k] = 0;
}
index[j] = i;
}
if (students[i].gpa == high) {
j++;
index[j] = i;
}
}
for (int i = 0; i < 150; i++) {
if (index[i] != 0) {
cout << students[index[i]].name << " "
<< students[index[i]].studentID << " "
<< students[index[i]].credits << " " << setprecision(2)
<< students[index[i]].gpa;
}
}
}
For reference, my text file looks like this:
Wesley s012980520 30 3.5
Allen s094589012 120 3.29
Jim s980469026 145 3.85
Luke s098419346 180 3.6
Helen s124598670 60 3.85
Chole s123309870 60 3.0
Am I missing something glaringly obvious? Thank you for the help!
Here are some of the problems in your code:
Data type of different variables: Your data (say Wesley s012980520 30 3.5) is of the format string string int float, which clearly does not match with the types in your struct Students.
To fix this, simply change your struct to:
struct Student {
string name;
string studentID;
int credits;
float gpa;
} students[SIZE];
Also, high should be float (as it stores gpa).
high and j become 0 on each iteration. Move high above the loop and j to init-list of the loop, like this:
float high = 0;
for (int i = 0, j = 0; i < count; i++)
index[j] = i; (first-occurrence) assigns index of student with highest gpa to index[j] but prior to this, j = 0 (although the index array is reset).
The second if condition (if (students[i].gpa == high)) inside the loop assigns the index of student with highest gpa to index[1], if the conditional block above it has executed. Make it else if to prevent redundancy.
Suggestions:
There is a setprecision(2), if you have put it display 3.6 as 3.60 then you need to add fixed before that.
There is no need to traverse whole index array, you simply need to do that from 0 to count.
Remove magic number 150 from your code, use the constant you have declared instead.
Put an endline ('\n') after printing each Student object.
Don't use std::endl to put simple newline characters. Use it only if you also need to flush the stream.
Avoid putting using namespace std; at top of your code.
Why put " when ' can do the job ;).
ifstream inputFile and inputFile.open can be written in a single line like : ifstream inputFile("...").
Corrected Code (doesn't work if name has spaces in it):
#include <fstream>
#include <iomanip>
#include <iostream>
using namespace std; // <-- (10), remove this yourself
int main() {
ifstream inputFile("lab9input.dat"); // <-- (12)
const int SIZE = 150;
int count = 0;
int index[SIZE] = {}; // <-- (7)
struct Student {
string name;
string studentID; // <-- (1)
int credits;
float gpa; // <-- (1)
} students[SIZE];
while (count < SIZE && inputFile >> students[count].name >>
students[count].studentID >>
students[count].credits >> students[count].gpa)
count++;
cout << "The highest GPA student(s) is(are): \n"; // <-- (9)
float high; // <-- (1), (2)
for (int i = 0, j = 0; i < count; i++) { //<-- (2)
if (students[i].gpa > high) {
high = students[i].gpa;
for (int k = 0; k < 150; k++) index[k] = 0;
j = 0; // <-- (3)
index[j] = i;
} else if (students[i].gpa == high) // <-- (4)
index[++j] = i;
}
for (int i = 0; i < count; i++) // <--(6)
if (index[i] != 0)
cout << students[index[i]].name << ' ' << students[index[i]].studentID
<< ' ' << students[index[i]].credits << ' ' << fixed // <-- (5)
<< setprecision(2) << students[index[i]].gpa << '\n'; // <--(8)
}
Better code to do the same thing:
#include <algorithm>
#include <cctype>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
struct Student {
std::string name, studentID;
int credits;
float gpa;
friend std::ostream &operator<<(std::ostream &, const Student &);
friend std::istream &operator>>(std::istream &, Student &);
};
std::ostream &operator<<(std::ostream &out, const Student &s) {
out << s.name << ' ' << s.studentID << ' ' << s.credits << ' ' << s.gpa
<< '\n';
return out;
}
std::istream &operator>>(std::istream &in, Student &s) {
s.name.clear();
std::string str;
while (in >> str && std::none_of(str.begin(), str.end(), ::isdigit))
s.name += str + ' ';
s.name.pop_back();
s.studentID = str;
in >> s.credits >> s.gpa;
return in;
}
int main() {
std::ifstream is("lab9input.dat");
std::vector<Student> v(std::istream_iterator<Student>(is), {});
auto max_gpa = std::max_element(v.begin(), v.end(), [](auto &a, auto &b) {
return b.gpa > a.gpa;
})->gpa;
std::cout << "The highest GPA student(s) is(are): \n";
for (auto &i : v) if (i.gpa == max_gpa) std::cout << i;
}
(C++) I've created a function to open the text file and assign the contents to an array. The first 2 elements in the array are the size of the grid. However, if either or both of the first 2 numbers are double digits, it doesnt read them in as double digits. Is there any way of doing this?
int openMap()
{
std::string fileName;
std::cout << "Please enter the file name with extension that you want to open: ";
std::cin >> fileName;
system("CLS");
std::ifstream file(fileName); //OPENS MAP FILE
int tmp;
int i = 0;
if (!file.is_open()) //CHECKS IF THE MAP FILE HAS OPENED CORRECTLY
{
std::cout << "Error Occured!\nCould not open file.";
return 0;
}
while (!file.eof()) //READS THE MAP FILE AND PASSES THE INFORMATION INTO AN ARRAY
{
file >> tmp;
checkNumber(tmp);
if (valid == true) //IF THE CHARACTER IS NOT A NUMBER THEN IT WONT BE PASSED INTO THE ARRAY
{
tmpArray[i] = tmp;
i++;
valid = false;
}
row = tmpArray[1]; //ASSIGNS THE FIRST 2 NUMBERS OF THE MAP FILE TO ROW AND COL VARIABLES
col = tmpArray[0];
}
return row, col;
}
I would assume I have to rewrite
file >> tmp
in some sort of different way, but not sure how.
Is there a way to scan through the text file until it hits a whitespace?
The text file contents looks like this
6 4 0 0 1 0 0 0 2 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0 3 0
(the 6 or 4 or both can be double digits instead)
Edit:
for (int j = 0; j < row; j++)
{
for (int k = 0; k < col; k++)
{
_map[j][k] = tmpArray[l];
std::cout << _map[j][k] << " ";
l++;
}
}
There's quite a number of bugs in the code, you should probably use a debugger to step through and identify which parts of your program don't behave as expected.
while(!file.eof())
file >> tmp;
checkNumber(tmp);
if (valid == true) //IF THE CHARACTER IS NOT A NUMBER THEN IT WONT BE PASSED INTO THE ARRAY
{
tmpArray[i] = tmp;
i++;
valid = false;
}
row = tmpArray[1]; //ASSIGNS THE FIRST 2 NUMBERS OF THE MAP FILE TO ROW AND COL VARIABLES
col = tmpArray[0];
You set row=tmpArray[1] and col = tmpArray[0] every iteration of the loop which is not only unnecessary but also incorrect, especially since row=tmpArray[1] is being executed at i=0 when nothing has been placed in tmpArray[1] yet.
EDIT: This is a lot smaller, less error prone due to less variables and type conversions, and easier to read:
int row,col;
//Add error checking here
cin >> col;
cin >> row;
cout << "Cols: " << col << " Rows: " << row << endl;
vector<vector<int> >_map(row, vector<int>(col,0));
for(int j=0; j < row; j++)
{
for(int k=0; k < col; k++)
{
int tmp;
cin >> tmp;
//Add error checking for tmp
_map[j][k] = tmp;
cout << _map[j][k] << endl;
}
}
There are some problems with your code. First the return type of your function is int but you are returning multiple values. Here is a complete running code which should solve your problem.
#include <iostream>
#include <fstream>
#include <vector>
std::vector< std::vector<int> > openMap() {
std::string fileName;
std::cout << "Please enter the file name with extension that you want to open: ";
std::cin >> fileName;
std::fstream myfile(fileName, std::ios_base::in);
int row, col;
myfile >> row;
myfile >> col;
int a;
std::vector< std::vector<int> > retval;
for (int i = 0; i < row; i++) {
std::vector<int> v1;
for (int j = 0; j < col; j++) {
myfile >> a;
v1.push_back(a);
}
retval.push_back(v1);
}
return retval;
}
int main(int argc, char * argv[])
{
std::vector< std::vector<int> > _map = openMap();
for(int i = 0; i < _map.size(); i++) {
for (int j = 0; j < _map[i].size(); j++) {
std::cout << _map[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
I guess that not so many people will be interested. But please see below a possible solution to your problem.
The code uses modern C++ algorithms.
It is very simple and straightforward.
#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>
#include <algorithm>
int main() {
// Ask user, to give a filename
std::cout << "Please enter the file name with extension that you want to open: ";
// Get the filename from the user
if (std::string fileName; std::cin >> fileName) {
// Open the file and check, if it is open
if (std::ifstream sourceFile(fileName); sourceFile) {
// Read the number of rows and columns of the matrix
if (size_t numberOfColumns, numberOfRows; sourceFile >> numberOfColumns >> numberOfRows) {
// Create a matrix with the given number of rows and columns
std::vector<std::vector<int>> result(numberOfRows, std::vector<int>(numberOfColumns, 0));
// Read data from the input stream and put it into the matrix
for (size_t i = 0; i < numberOfRows; ++i) {
std::copy_n(std::istream_iterator<int>(sourceFile), numberOfColumns, result[i].begin());
}
// Print result. Go through all lines and then copy line elements to std::cout
std::for_each(result.begin(), result.end(), [](std::vector<int>& c) {
std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << "\n"; });
}
}
else {
std::cerr << "\n*** Error: Could not open source File\n\n";
}
}
return 0;
}
I want to retrieve the first value of speed in from the typedef struct variable which I have added into the map. Right now my code is returning all the values from the CSV file which I have read. How do I get it to return only the first value to do a comparison to get the max value from the entire column?
I have tried using map.begin()->((*it).second).speed) but it does not work.
#include <iostream>
#include <fstream>
#include <string>
#include "Date.h"
#include "Time.h"
#include <stdlib.h>
#include <map>
using namespace std;
typedef struct
{
Time t;
float speed;
int solar;
}
WindLogType;
date d;
multimap<date, WindLogType> windlog;
ifstream input , csv;
ofstream output;
string filename;
int number,choice;
string *filelist = NULL;
WindLogType T1;
int main()
{
output.open("data/met_index.txt");
cout << "Enter number of file to read : " << endl;
cin >> number ;
for(int i =0; i< number ; i++)
{
cout << "Enter File name : " << endl;
cin >> filename;
output << filename << endl;
}
filelist = new string[number];
output.close();
input.open("data/met_index.txt", ios::in);
if(!input.is_open())
{
cout<< "File not found."<<endl;
return 0;
}
else
{
string line, line2;
while(getline(input, line, '\n'))
{
//cout << line << endl;
line = "data/" + line;
for(int i =0; i<number; i++)
{
filelist[i] = line;
cout << filelist[i];
csv.open(filelist[i].c_str());
string line,sDay, sMonth, sYear, sHH, sMM;
while(getline(csv,line2, '\n' ))
{
//cout << line2 << endl;
getline(csv, sDay,'/');
getline(csv, sMonth,'/');
getline(csv, sYear,' ');
getline(csv, sHH,':');
getline(csv, sMM,',');
int day1 = atoi(sDay.c_str());
int month1 = atoi(sMonth.c_str());
int year1 = atoi(sYear.c_str());
int hour1 = atoi(sHH.c_str());
int min1 = atoi(sMM.c_str());
float s1 = 0.0;
int sr = 0;
for (int i=0; i<10; i++)
{
csv >> s1;
csv.ignore(100, ',');
}
for(int j =0; j<18; j++)
{
csv >> sr;
csv.ignore(50,',');
}
T1.t.setTime(hour1, min1);
T1.speed = s1;
T1.solar = sr;
d.setDate(day1, month1, year1);
windlog.insert(pair<date, WindLogType>(d, T1));
multimap<date, WindLogType> :: iterator it;
for(it =windlog.begin(); it!= windlog.end(); ++it)
{
int max_value = ((*it).second).speed;
if((*it).second.speed > max_value){
max_value = ((*it).second).speed;
}
cout << max_value<<endl;
}
}
csv.close();
}
/**/
}
input.close();
input.clear();
//input.open(filelist[0].c_str(), ios::in);
}
return 0;
}
Your are printing max_value everytime.
Move the lines that find the maximum value after you've inserted everything, i.e., after the csv.close() for example. Also, do not print the maximum while searching for it but after you've iterated over all the elements.
multimap<date, WindLogType> :: iterator it =windlog.begin();
int max_value = ((*it).second).speed;
for(++it ; it!= windlog.end(); ++it)
{
if((*it).second.speed > max_value){
max_value = ((*it).second).speed;
}
}
cout << max_value<<endl;
Of course, be sure the map is not empty.
EDIT
WindLogType.speed is a float and you're using an integer when finding the maximum, it should be float too. Probably you already know it, but since C++11 you can use the auto specifier to let the compiler deduce automatically the correct type based on the assignment expression. It is available since Visual Studio 2010 and gcc 4.4 (for gcc you have to include the --std=c++11 option).
if (!windlog.empty()) {
auto it = windlog.begin(); // 'it' is an iterator
auto max_value = it->second.speed; // you're now sure it uses the same type
for(++it; it!= windlog.end(); ++it) {
max_value = std::max(it->second.speed, max_value);
}
std::cout << max_value << std::endl;
} else {
std::cout << "Empty map" << std::endl;
}
I'm working through a program where I have an input file containing state names, and three separate taxes for each state: sales tax, property tax, and income tax. I'm attempting to read the tax values (read as double variables) into an array of type double. Here is my code:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
using namespace std;
int main()
{
double a = 0,
b = 0,
c = 0;
double array[5][3];
string state_name;
ifstream fin;
fin.open("test.dat");
for (; fin >> state_name >> a >> b >> c;)
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 3; j++)
{
fin >> array[i][j];
cout << array[i][j] << "\t";
}
cout << endl;
}
}
return 0;
}
Here is the data file:
TEXAS .0825 .02 -.03
CALIFORNIA .065 .04 .05
MARYLAND .03 .025 .03
MAINE .095 .055 .045
OHIO .02 .015 .02
And from the this, the program outputs the array, except that each position reads -9.25596e+061. I was wondering if this was because the program was trying to read the string into the array. I was also wondering if there was a way to overlook the string in the file line by line so that only the double values are read into the array.
You read in the entire line in the for loop. You don't need to do fin >> array[i][j] later. Instead you should be doing this:
for (int i = 0; i < 5; i++)
{
fin >> state_name;
for(int j = 0; j < 3; ++j)
{
fin >> array[i][j];
cout << array[i][j] << '\t';
}
cout << endl;
if(!fin)
{
// handle an error reading the file
}
}
This should do the job:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
double array[5][3];
string state_name;
ifstream fin;
fin.open("test.dat");
// Read the file row by row
int row =0;
while(fin >> state_name >> array[row][0] >> array[row][1] >> array[row][2]) {
++row;
}
// Print the result
for(int i = 0; i < 5; i++) {
for(int j = 0; j < 3; j++) {
cout << array[i][j] << "\t";
}
cout << endl;
}
return 0;
}
If you allow me to think one step further, you probably prefer to push each row into a vector rather than a static array. Otherwise you need to rewrite your code if the file will have more than 5 rows.
I've been working on this program for a very long time now and I think I'm close to being done. However, my code is outputting something strange and I cannot find the issue.
Expected output:
This is a happy tESt to check if my reader works!
An happy alligator was AT tHe happy park and a happy a cow blew its nose in a happy scarf. are you an happy Octagon THe
Actual output:
This is a tE happySt to check if my reader works!
a happy
THe Happy
How can I make the following code behave as I expect?
#include <iostream>
#include <string>
#include <cctype>
#include <fstream>
#include <sstream>
#include <locale>
using namespace std;
void
usage(char *progname, string msg){
cerr << "Error: " << msg << endl;
cerr << "Usage is: " << progname << " [filename]" << endl;
cerr << " specifying filename reads from that file; no filename reads standard input" << endl;
}
string capitalization(string word,string adj){
for(int i = 0; i <= word.length(); i++){
if(isupper(word[i])){
for(int j = 0; j <= adj.length(); j++){
adj[j] = toupper(adj[j]);
return adj;
}
}
else if(isupper(word[0])){
for(int j = 0; j <= adj.length(); j++){
adj[j] = tolower(adj[j]);
return adj;
}
}
else{
for(int j = 0; j <= adj.length(); j++){
adj[j] = tolower(adj[j]);
return adj;
}
}
}
}
int main(int argc, char *argv[]){
string adj;
string file;
cin >> adj;
cin >> file;
string line;
string articles[14] = {"a","A","an","aN","An","AN","the","The","tHe","thE","THe","tHE","ThE","THE"};
ifstream rfile;
rfile.open(file.c_str());
if(rfile.fail()){
cerr << "Error while attempting to open the file." << endl;
return 0;
}
string::size_type pos;
string word;
string words[1024];
while(getline(rfile,line)){
istringstream iss(line);
for(int i = 0; i <= line.length(); i++){
iss >> word;
words[i] = word;
for(int j = 0; j <= 14; j++){
if(word == articles[j]){
string article = word;
iss >> word;
pos = line.find(article);
//cout << pos << endl;
string adjec = capitalization(word,adj);
int position = (pos + word.length());
line.insert(position, " " + adjec);
continue;
}
}
}
cout << line << "\n";
}
}
This may not fix any of your problems, but...
The logic in these lines is wrong.
istringstream iss(line);
for(int i = 0; i <= line.length(); i++){
iss >> word;
Let's say your line is
This is a test.
For this line, line.length() is 15 but there aren't 15 words. What you need is
istringstream iss(line);
while ( iss >> word ) {