As part of my assignment I need to open a file and then read the information into 3 arrays. These information are separated into 3 different columns first one is, Country Code name(is string), second is the population(is int) and the third is the full name of the country. Here is the example of few lines of the file:
AU 20090437 Australia
BR 186112794 Brazil
BU 7262675 Bulgaria
CA 32805041 Canada
CN 1306313812 China
DO 8950034 Dominican Republic
So far I have:
void readCntrData(string [], int [], string[], int &size, const int maxSize);
int main()
{
const int COUNTRIES = 200;
// maximum size of arrays
int size = 0;
string cntrCodes[COUNTRIES];
int cntrPopulation[COUNTRIES];
string cntrNames[COUNTRIES];
string inputFileName = "countries.txt";
ifstream inputFile;
inputFile.open(inputFileName.c_str());
if (inputFile.fail())
{
cout << "\n\tPlease check the name of the input file and \n\ttry again later!\n";
exit(EXIT_FAILURE);
}
int index = 0;
while (index < COUNTRIES && inputFile >> cntrCodes[index] >> cntrPopulation[index] >> cntrNames[index] ) {
index++;
}
size = index;
if (size == COUNTRIES && !inputFile.eof()){
cout << "\n\tThe input file \"" << inputFileName <<
"\"is too big: \n\tit has more than " << COUNTRIES << " items!\n";
exit(EXIT_FAILURE);
}
inputFile.close();
}
The issue here few countries have two part names, and my code breaks where the name of country has two parts. I don't know how to ignore the space there and read the whole name.
I appreciate any feedback.
Just cleaning the code up a bit...
the main problem you ask about in your question has already been addressed by user4581301 in comments: summarily, using getline to read the country code will cope with having one or more whitespace-separated words
since C++11, we've been able to use the ifstream constructor to open a filename passed in a std::string, and it's often convenient to construct the ifstream inside an if/else construct that then handles the successful-open and failed-attempt-to-open cases
there's no need to explicitly close ifstreams that are going out of scope anyway
it's good to send error messages to std::cerr, so if someone running your program does something like "theprogram > theprogram.output" they'll still see the error messages in their terminal
no real need to have separate index and size variables, and something like num_countries is a better, more meaningful name
checking for EOF is a bit of a dark art - I wouldn't recommend using inputFile.eof() the way you did because if the last country read was followed by an empty line in the file, you wouldn't have hit end-of-file even though it's "logically" empty thereafter; it's too easy for someone not to notice empty lines at the end of a file when they're working in a editor to create/update the file; checking if there's another non-whitespace character is a more robust approach
the common size for int these days is 32 bits, which can handle numbers up to a couple billion (when signed, or a bit over 4 billion for unsigned int); China's population's a bit too close to that for comfort - if you want to write code in a way that should still work 20 or 50 years hence, it's good to think about whether the types will still be large enough to store the values; int64_t from <cstdint> is a better choice.
Revised code:
int main()
{
const int COUNTRIES = 200;
string cntrCodes[COUNTRIES];
int64_t cntrPopulation[COUNTRIES];
string cntrNames[COUNTRIES];
string inputFileName = "countries.txt";
if (std::ifstream inputFile{inputFileName})
{
int num_countries = 0;
while (num_countries < COUNTRIES &&
inputFile >> cntrCodes[num_countries] >> cntrPopulation[num_countries] &&
getline(std::cin, cntrNames[num_countries]))
++num_countries;
// will see if we can read another character (remember >> skips
// whitespace by default), rather than check inputFile.eof()
// which may not be true if there're any extra empty lines after
// the data...
char character;
if (num_countries == COUNTRIES && inputFile >> character)
{
std::cerr << "\n\tThe input file \"" << inputFileName <<
"\"is too big: \n\tit has more than " << COUNTRIES << " items!\n";
exit(EXIT_FAILURE);
}
// any extra code to actually use the country data goes here...
}
else
{
std::cerr << "\n\tPlease check the name of the input file and \n\ttry again later!\n";
exit(EXIT_FAILURE);
}
}
For the last input of each line, you can use getline which reads everything until it meets an Enter key.
You can try this:
while (index < COUNTRIES && inputFile >> cntrCodes[index] >> cntrPopulation[index] ) {
getline (inputFile, cntrNames[index]);
index++;
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I'm still new to c++(and to stackoverflow).
I'm creating program to analyze some data for me(open file, save data into vectors, then I search vectors for name, and then I work with objects functions). Anyways, previous "version" of the program works fine, but file that I open only contains 1 string per line in the beginning(followed by other data), so it's easy to work with.
What I'm trying to do, is to open up the files that contain 1-3 strings(i.e. line 1 - West Ham United, line 2 - Everton etc.). After the name, other data follows on the same line.
I thought about separating the name and other data into 2 lines, in example Line1 - name, Line2 - other data, and so on. But how do I save the whole "name" line into vector(vector is of the object type). Other option would be to open up the file and saving all of the chars into an array. Afterwards I would output that array in Outfile, deleting the spaces only between letters(i.e. - WestHamUnited), and then I would work with the output file.
Question is, how do I delete the spaces only between letters?
Maybe you guys could recommend some other option that I'm not aware of.
Let's suppose there is text line stored in string object (s can be read from file/stream by getline)
std::string s = "Manchester City 17 26 2.16";
and we need to read all text before the first number to other string (join some words without spaces)
std::string name = ""; // empty string to store name
We do not know how many non-numbers (parts of name) will be until the first number is in the string, but we expect that number is.
If your task is something like that, consider the following example:
std::istringstream ss(s); // creation of stream object to read data from text line
double d = 0; // will be the first number
std::string str; // buffer string for parts of name
bool notNumber;
// loop that works to update name trying to find a number
do{
ss >> d; // try to read number
notNumber = ss.fail(); // save the state (if number was read value is false)
if (notNumber) // is value is true (not a number is the first in the stream)
{
ss.clear(); // reset the state
ss >> str; // read a word
name.append(str); // add this word to the name
}
} while (notNumber && !ss.eof()); // while non-numbers are read and stream is not empty
// after loop we have name and, perhaps, number
std::cout << "Name is: " << name << std::endl;
std::cout << "The first number is: " << d << std::endl;
UPDATE:
Actually we can use notNumber to know that number was really present in the initial string s
if (notNumber)
std::cout << "There is no number after name!" << std::endl;
else
std::cout << "The first number is: " << d << std::endl;
UPDATE 2:
For file test.txt comprising
West Ham United 3.14 22
Everton 17 26 2.16
Manchester City 9.32 17 26
the following program
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
using namespace std;
int main(void)
{
// data structure to store data from file
map<string, vector<double>> dataTable;
// open the file
ifstream inFile("test.txt");
if (!inFile.is_open())
{
cout << "File error!" << endl;
return 1;
}
// reading data from file
while (!inFile.eof() && inFile.good())
{
string s;
string name;
getline(inFile, s);
istringstream ss(s);
double d = 0;
string str;
bool notNumber;
do{
ss >> d;
notNumber = ss.fail();
if (notNumber)
{
ss.clear();
ss >> str;
name.append(str);
}
} while (notNumber && !ss.eof());
if (name.length() == 0)
continue;
// saving numbers to table
do{
dataTable[name].push_back(d);
ss >> d;
} while (!ss.fail());
}
inFile.close();
// print data stored in table
for (map<string, vector<double>>::iterator i = dataTable.begin(); i != dataTable.end(); i++)
{
cout << i->first << " : ";
for (vector<double >::iterator j = i->second.begin(); j != i->second.end(); j++)
{
cout << *j << " ";
}
cout << endl;
}
return 0;
}
provides the following output:
"Question is, how do I delete the spaces only between letters?"
You are aware of std::string, aren't you? Then you should be able to locate a method that deletes a character or character range from the string.
Good. Than the only problem would be how to detect if a character is a space. How hard could it be, I wonder.
I short: RTFM, Luke, use it with full confidence.
If you can control input file format, I would recommend you to introduce some delimiter mark for the first column.
Otherwise, if the number of columns is fixed, you can read line in reverse. E.g. the latest 3 words are numbers and all the rest is team's name.
Note, that it is wrong just read words until the first digit, because there are exist teams with numbers in name (e.g. FC Schalke 04).
Here is an example:
struct FileLine
{
std::string col1;
double col2 = 0., col3 = 0., col4 = 0.;
// `words` is a vector of words from a line in file
bool load(const std::vector<std::string>& words)
{
col1.clear();
col2 = col3 = col4 = 0.;
std::vector<std::string>::size_type n = words.size();
if (n >= 4) {
col4 = std::stod(words.at(n-1));
col3 = std::stod(words.at(n-2));
col2 = std::stod(words.at(n-3));
col1 = std::accumulate(words.begin(), words.end()-3, col1);
}
return n >= 4;
}
};
Another way is to use regular expressions (link). It's very powerful tool for a text parsing.
I am trying to read a line of string characters with numbers (e.g "30 40 50 20") and put them into a vector. I also need to avoid empty space and newlines. But when I read the input, it doesn't see the string "30", it sees the characters "3" and "4".
void Input() {
getline(cin,line, '\n');
for (int i = 0; i < line.length(); i++) {
if (! (isspace(line[i]))) {
cout << line[i] << ", ";
scores.push_back(line[i]);//(atoi(input));
}
}
cout << scores.size() << "! ";
}
A line like "30 40 50" won't give a vector size of 3, it will give a size of 6.
What are the optimal ways to get around this issue?
EDIT: I should have clarified in the original message that this is for a challenge, in which I am unable to include the string stream library in the original case.
I think you're doing the right thing grabbing the whole line before parsing, otherwise you get into a bit of a pickle. But you do actually have to do some parsing. Right now you're just pulling out individual characters.
The following isn't optimal but it'll get you started — continue using formatted stream extraction, but isolated to this line from the file.
So:
void Input()
{
getline(cin, line, '\n');
istringstream ss(line);
int val;
while (ss >> val)
scores.push_back(val);
cout << scores.size() << "! ";
}
Read the line and put into a std::istringstream, then read as "normally" using the >> operator from the string stream.
Putting the line into a std::istringstream and extracting the numbers from that is the best way.
Here's an alternative to a manual loop using the standard library:
std::istringstream numbers(line);
std::copy(std::istream_iterator<int>(numbers),
std::istream_iterator<int>(),
std::back_inserter(scores));
It is probably best to take advantage of an input stringsteam, example: http://www.cplusplus.com/reference/sstream/stringstream/stringstream/.
The extraction operator allows you to parse data from the stream to some variable of datatype T. Another advantage of input stringstreams is the ability to query whether the pass was successful, and in your case ignore whitespace characters by setting the skipws format flag.
Example:
int main () {
std::istringstream ss("30 40 50");
float val = 0.0f;
while( ss >> std::skipws >> val )
{
std::cout << val << "\n";
}
return 0;
}
Out: 30 40 50
This may be a very simplistic question, but I have not found any examples to guide me. I am trying to write class in C++ that can read a text file where columns of data (float, char, int, etc...) are separated by spaces. I would like the class to be able to ignore some columns and read in specified columns. For now I am experimenting with one and two column formats and progressing from there. A brief example of a test input file is listed below.
103.816
43.984
2214.5
321.5
615.8
8.186
37.6
My first attempt at writing a code to read in one column of data is trivial and looks like this.
void Read_Columnar_File::Read_File(const std::string& file_name)
{
int i;
std::ifstream inp(file_name,std::ios::in | std::ios::binary);
if(inp.is_open()) {
std::istream_iterator<float> start((inp)), end;
std::vector<float> values(start,end);
for(i=0; i < 7; i++) std::cout << values[i] << std::endl;
}
else std::cout << "Cannot Open " << file_name << std::endl;
inp.close();
}
In my next attempt I am trying to read in only one column of a two column format like the input shown below. The numbers are just made up for this example
103.816 34.18
43.984 21.564
2214.5 18.5
321.5 1.00
615.8 4.28
8.186 1.69
37.6 35.48
I modified the code format slightly to look like the example below. I am using a brief but of pseudocode after the inp >> statement to illustrate that I am trying to get the code to skip to the next line after reading in the first column. my question is "How do I get the code to just read the first column and then skip to the next line where again it just reads the first column of data and make it keep doing this until the end of file?" And thank you in advance for any advice that you can give.
void Read_Columnar_File::Read_File(const std::string& file_name)
{
int i;
float input;
std::vector<float> values;
std::ifstream inp(file_name,std::ios::in | std::ios::binary);
if(inp.is_open()) {
for(i=0; i < 7; i++) {
inp >> input >> \\ - At this point I want the code to skip to the next
\\ line of the input file to only read the first column
\\ of data
values.push_back(input);
}
for(i=0; i < 7; i++) std::cout << values[i] << std::endl;
}
else std::cout << "Cannot Open " << file_name << std::endl;
inp.close();
}
You can use the member function ignore() to discard all the characters until the next line. I would also fix up your code to use a for() loop predicated on the success of the extraction so your code will work for any number of columns, not just 7:
for (float input; inp >> input; values.push_back(input))
{
inp.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
When you want to read only part of a line, and skip the rest of that line, one easy starting point is to:
read the entire line into a string
put the whole string into an istringstream
Parse out the parts you care about
Repeat
As a rule, I generally find this easier to generalize than ones that alternate between reading and ignoring data as it's being read from the file.
So I'm trying to make a program that will read a txt file where time, voltage and ampere are stored, the text file looks like this http://pastebin.com/a0ZvYGmj, only ten times bigger (it's obnoxiously huge, so large that it didn't fit into a single pastebin).
So what I have to do is write a program that will read through the entire voltage column and detect the highest value (second column), then display it and the time it occurs (first column). I can also do it with the ampere, but that's optional.
Problem is I literally have no idea what to do, this is what I have managed to do so far
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
FILE * pFile;
char mystring [100];
pFile = fopen ("voltage.txt" , "r");
if (pFile == NULL) perror ("Error opening file");
else {
if ( fgets (mystring , 100 , pFile) != NULL )
puts (mystring);
fclose (pFile);
}
system ("PAUSE");
return 0;
}
But all that does is read the first entry of each of the 3 columns ("Time Volt Ampere"), so I probably have to start over.
Can anyone send me in the right direction? How do I read an entire column, them make the program detect the biggest number in said column?
I'm completely lost so any help would be appreciated.
If you have the option of using the file as an input, then you could use simple cin in order to get your values for Time,Volt and Amp. ( the only thing you need to keep in mind is to ignore first row because they are just names)
If you know that all three values in each row will be present, then you can create while loop that runs until EOF. Then you read 3 values (Time, Volt, Amp) and compare with the previous Volt.
If previous volt is larger - ignore this row and move on.
Else - keep current Time, Volt and Amp.
On the next iteration, compare new 3 values to the max value of Volt so far.
Here is my "quick and dirty" solution:
#include <iostream>
#include <string>
using namespace std;
int main()
{
// cin>>temp goes through names and ignores it since we don't need it
string temp;
cin >> temp;
cin >> temp;
cin >> temp;
float timeval = 0;
float volt = 0;
float amp = 0;
float timevalTemp = 0;
float voltTemp = 0;
float ampTemp = 0;
while(cin >> timevalTemp >> voltTemp >> ampTemp)
{
if(voltTemp > volt)
{
timeval = timevalTemp;
volt = voltTemp;
amp = ampTemp;
}
}
cout << "Max Time: " << timeval << endl;
cout << "Max Volt: " << volt << endl;
cout << "Max Amp: "<< amp << endl;
return 0;
}
Let me know if something is not clear. I ran it on your text file and it produces:
Max Time: 0.00035
Max Volt: 9.78167
Max Amp: 0.1473
You can try using fscanf. Please have a look at this How to use fscanf to read a file
After you read you will have to compare voltage values of each row to get max voltage. I hope you know how to do this.
First, you must use a llop construction so you read the whole set of lines. So try putting your fgetsinside of a while.
Next, you need to split each line by spaces. Use strtokfor that.
Next I'll recommend you keep a max_voltage variable and as you travel through the file, you assign it the current max value. Also, you need to keep track of that max_voltage variable's timecompanion so use another variable for that one (Google for "find max value in an array" if you have doubts about how to do it).
After you try that, post your work if you have some question/need clarifications.
Bye.
fgets reads file untill it encounter newline character, or end of file.
If fgets encounters eof before any character it'll return null pointer. So it should be okay if you change file reading code to:
while ( fgets (mystring , 100 , pFile) != NULL )
puts (mystring);
I would define what a line looks like:
struct Line {
double time, volt, ampere;
int linenumber;
};
istream& operator>>(istream& in, Line& line) {
++linenumber;
return in >> line.time >> line.volt >> line.ampere;
}
ostream& operator<<(ostream& out, const Line& line) {
return out << "Line: " << line.linenumber
<< " Time: " << line.time
<< " Volt: " << line.volt
<< " Ampere: " << line.ampere;
}
and use it with std::cin.
int main()
{
string dummy;
std::istream& in = cin;
in >> dummy >> dummy >> dummy;
Line max;
Line line;
while(in.good()) {
in >> line;
if (line.volt > max.volt) max = line;
}
cout << "Max line: " << max;
return 0;
}
(or you can replace cin with a file input stream:
#include <ifstream>
...
std::ifstream in(argv[1]);
...
)
You can use sort for that, it's a highly specialized merge-sort implementation that surely included in your cygwin distro. You can tell it which columns to sort on (the -k switch), and it both works with stdandard input and filename.
Then just take e.g. the first 4 lines of output to account for 'special' lines. Use cat -n to prepend line numbers.
$ cat -n input.txt | sort -r -k 2 | head -n 10
1 Time Volt Ampere
4796 0.2398 9.76417 0.147683
4795 0.23975 9.75667 0.147833
4794 0.2397 9.77333 0.147867
4793 0.23965 9.7575 0.14765
4792 0.2396 9.75167 0.147533
4791 0.23955 9.75833 0.147542
4790 0.2395 9.75583 0.147542
4789 0.23945 9.77 0.1476
4788 0.2394 9.75583 0.147317
I have the following block of code that i am using to read a text file of the following format:
firstname lastname id mark
firstname lastname id mark
Following is the block of code.
void DBManager::ReadFile(void){
fstream myfile; /*fstream object that will be used for file input and output operations*/
char* fn; /*pointer to the storage which will hold firstname*/
char* ln; /*pointer to the storage which will hold lastname*/
int id; /*integer var to hold the id*/
float mark; /*float var to hold the mark*/
/*read in the filename*/
g_FileName = new char[1024]; /*allocate memory on the heap to store filename*/
cout << "Please enter the filename:";
cin >> g_FileName;
/*open file*/
myfile.open(g_FileName, ios::in | ios::out);
if(myfile.is_open()){ /*check if the file opening is successful*/
cout << "File reading successful !\n";
/*read information from the file into temporary variables before passing them onto the heap*/
while (!myfile.eof()) {
fn=(char*) new char[1024];
ln=(char*) new char[1024];
myfile >> fn >> ln >> id >> mark;
cout << fn << " " << ln << " " << id << " " << mark << " " << endl;
}
myfile.close();
}
else{ /*else print error and return*/
perror("");
return;
}
}
The above block of code works ! :)
But I am surprised as to how myfile knows it is supposed to hold one line at a time and how its being smart enough about setting the four variables.
I am new to C++ , and hence this might be covered in some sort of documentation. But i would be happy to have some insight from you'll or a link to somewhere i can understand fstream objects better.
In C++, std::fstream is a type of stream which works specifically for files. When reading from a file, the interface for std::fstream is almost identical to std::cin. Input streams are programmed to read the next word or number when asked with the >> operator. They know where words and numbers are because they are separated by white space. In the default locale, spaces, tabs and newlines are considered to be white space. You can change the locale to include other characters, like commas, and have those be skipped while reading from a file. Basically, when reading with input streams, newlines and spaces are treated the same.
Some nice explanation for learning about streams is here: http://www.cprogramming.com/tutorial/c++-iostreams.html
I'm not sure what the question is. However, the code has several problems:
You should always check input after having tried to read.
Testing for eof() to determine if there is more to read doesn't work.
You have a memory leak, allocating memory in every iterator.
Reading without a constraint into a char array is unsafe, i.e., it is prone to buffer overrides (one of the major attack vectors).
You want to use a loop looking something like this:
std::string fn, ln;
while (myfile >> fn >> ln >> id >> mark) {
...
}