So I am trying to read string data from a file into a dynamically allocated array, but I cannot seem to get the correct code for doing so. I have below a code using an array of a preset size but that would not be efficient, hence why I want to use dynamic memory allocation. I know I have to use pointers but I am fairly new to the concept so any help would be greatly appreciated.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <string>
#define SIZE 100
using namespace std;
void loadData();
int main()
{
loadData();
return 0;
{
string fileName;
std::string wordArray[SIZE];
cout << "Please enter the name of the text file you want to process followed by '.txt': " << endl;
cin >> fileName;
ifstream dataFile(fileName);
if (dataFile.fail()) {
cerr << fileName << " could not be opened." << endl; //error message if file opening fails
exit(-1);
}
while (!dataFile.eof()) {
for (int i = 0; i < SIZE; i++) {
dataFile >> wordArray[I];
for (std::string& s : wordArray) //this for loop transforms all the words in the text file into lowercase
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return std::tolower(c); });
}
}
dataFile.close();
}
}
The below program show how you can store the strings read from the input.txt file and store them in lowercase in a std::vector.
Version 1: Stores word by word(in lowercase) into the vector
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <sstream>
int main()
{
std::ifstream inputFile("input.txt");
//create vector that will contain the words in the file in lowercase
std::vector<std::string> wordVec;
std::string line, individualWord;
if(inputFile)
{
while(std::getline(inputFile, line, '\n'))
{
std::istringstream ss(line);
while(ss >> individualWord)//word by word
{
std::transform(individualWord.begin(), individualWord.end(), individualWord.begin(),
[](unsigned char c)
{ return std::tolower(c);
});
wordVec.push_back(individualWord);
}
}
}
else
{
std::cout<<"file could not be opened"<<std::endl;
}
inputFile.close();
//lets print out the elements of the vector to check if elements are correctly stored in lowercase
for(const std::string &elem: wordVec)
{
std::cout<<elem<<std::endl;
}
return 0;
}
The output of version 1 can be seen here.
Version 2: Stores a complete single line(in lowercase) into the vector
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
int main()
{
std::ifstream inputFile("input.txt");
//create vector that will contain the words in the file in lowercase
std::vector<std::string> wordVec;
std::string line;
if(inputFile)
{
while(std::getline(inputFile, line, '\n'))
{
std::transform(line.begin(), line.end(), line.begin(),
[](unsigned char c)
{ return std::tolower(c);
});
wordVec.push_back(line);
}
}
else
{
std::cout<<"file could not be opened"<<std::endl;
}
inputFile.close();
//lets print out the elements of the vector to check if elements are correctly stored in lowercase
for(const std::string &elem: wordVec)
{
std::cout<<elem<<std::endl;
}
return 0;
}
The output of the above(version 2) program can be seen here.
The difference between version 1 and 2 is that version 1 reads a complete line and then read word by word and store those words(in lowercase) into the std::vector while version 2 reads a complete line(which ends with '\n') and store that line(in lowercase) into the std::vector.
Sometime life could be easy. By using modern C++ elements, the implementation will be really simple in the end.
I am not so sure what I should explain for 3 lines of code. It is basically visible with the comments in the code.
Please see the first solution:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <cctype>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Define a vector and read all words from the file
std::vector words(std::istream_iterator<std::string>(inputStream), {});
// Show result to the user
std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
}
Then next solution converts the word into lower case. So, I had to write 4 statements. Please see the below second solution.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <cctype>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
std::vector<std::string> words{};
// Read all words from the file and convert to lower case
std::transform(std::istream_iterator<std::string>(inputStream), {}, std::back_inserter(words),
[](std::string w) { for (char& c : w) c = std::tolower(c); return w; });
// Show result to the user
std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
}
If data does not need to be stored, we can come up with a 2-statement version.
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Read all words from the file and convert to lower case and output it
std::transform(std::istream_iterator<std::string>(inputStream), {}, std::ostream_iterator<std::string>(std::cout, "\n"),
[](std::string w) { for (char& c : w) c = std::tolower(c); return w; });
}
}
Sometime teachers want the students to learn dynamic memory management using pointers.
But the usage of pointers for owned memory is strongly discouraged. The std::vector has been invented for that reason over a decade ago.
Anyway, I will also show a solution using new. It works, but you should not use it.
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Do some initial allocation of memory
std::string* words = new std::string[1]{};
unsigned int numberOfAvaliableSlotsInDynamicArray{1};
// Now we want to read words. We want also to count the words,so that we can allocate appropriate memory
std::string word{};
unsigned int wordCounter{};
// Read all words in a loop
while (inputStream >> word) {
// Check, if we still have enough space in our dynamic array
if (wordCounter >= numberOfAvaliableSlotsInDynamicArray) {
// Oh, we are running out of space. Get more memory
numberOfAvaliableSlotsInDynamicArray *= 2;
std::string* temp = new std::string[numberOfAvaliableSlotsInDynamicArray]{};
// Copy all existing data into new array
for (unsigned int i{}; i < wordCounter; ++i)
temp[i] = words[i];
// Delete old memory
delete[] words;
// And assign new storage to words
words = temp;
}
// STore the recently read word at the end of the array
words[wordCounter] = word;
// Count words. Now we have one word more
++wordCounter;
}
// Now we have read all words from the file. Show output
for (unsigned int i{}; i < wordCounter; ++i)
std::cout << words[i] << '\n';
// Release memory
delete[] words;
}
}
And even smart pointers, which shall be used as pointers if at all, are not nice.
Also the following should not be used.
#include <iostream>
#include <fstream>
#include <string>
#include <memory>
int main() {
// Open the input file and check, if it could be opened
if (std::ifstream inputStream{ "r:\\input.txt" }; inputStream) {
// Do some initial allocation of memory
std::unique_ptr<std::string[]> words = std::unique_ptr<std::string[]>(new std::string[1]);
unsigned int numberOfAvaliableSlotsInDynamicArray{ 1 };
// Now we want to read words. We want also to count the words,so that we can allocate appropriate memory
std::string word{};
unsigned int wordCounter{};
// Read all words in a loop
while (inputStream >> word) {
// Check, if we still have enough space in our dynamic array
if (wordCounter >= numberOfAvaliableSlotsInDynamicArray) {
// Oh, we are running out of space. Get more memory
numberOfAvaliableSlotsInDynamicArray *= 2;
std::unique_ptr<std::string[]> temp = std::unique_ptr<std::string[]>(new std::string[numberOfAvaliableSlotsInDynamicArray]);
// Copy all existing data into new array
for (unsigned int i{}; i < wordCounter; ++i)
temp[i] = std::move(words[i]);
// And assign new storage to words
words = std::move(temp);
}
// STore the recently read word at the end of the array
words[wordCounter] = word;
// Count words. Now we have one word more
++wordCounter;
}
// Now we have read all words from the file. Show output
for (unsigned int i{}; i < wordCounter; ++i)
std::cout << words[i] << '\n';
}
}
Conclusion: Use a std::vector.
Related
#include<bits/stdc++.h>
using namespace std;
int main()
{
int i=0;
char a[100][100];
do {
cin>>a[i];
i++;
}while( strcmp(a[i],"\n") !=0 );
for(int j=0;j<i;i++)
{
cout<<a[i]<<endl;
}
return 0;
}
Here , i want to exit the do while loop as the users hits enter .But, the code doesn't come out of the loop..
The following reads one line and splits it on white-space. This code is not something one would normally expect a beginner to write from scratch. However, searching on Duckduckgo or Stackoverflow will reveal lots of variations on this theme. When progamming, know that you are probably not the first to need the functionality you seek. The engineering way is to find the best and learn from it. Study the code below. From one tiny example, you will learn about getline, string-streams, iterators, copy, back_inserter, and more. What a bargain!
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <vector>
int main() {
using namespace std;
vector<string> tokens;
{
string line;
getline(cin, line);
istringstream stream(line);
copy(istream_iterator<string>(stream),
istream_iterator<string>(),
back_inserter(tokens));
}
for (auto s : tokens) {
cout << s << '\n';
}
return 0;
}
First of all, we need to read the line until the '\n' character, which we can do with getline(). The extraction operator >> won't work here, since it will also stop reading input upon reaching a space. Once we get the whole line, we can put it into a stringstream and use cin >> str or getline(cin, str, ' ') to read the individual strings.
Another approach might be to take advantage of the fact that the extraction operator will leave the delimiter in the stream. We can then check if it's a '\n' with cin.peek().
Here's the code for the first approach:
#include <iostream> //include the standard library files individually
#include <vector> //#include <bits/stdc++.h> is terrible practice.
#include <sstream>
int main()
{
std::vector<std::string> words; //vector to store the strings
std::string line;
std::getline(std::cin, line); //get the whole line
std::stringstream ss(line); //create stringstream containing the line
std::string str;
while(std::getline(ss, str, ' ')) //loops until the input fails (when ss is empty)
{
words.push_back(str);
}
for(std::string &s : words)
{
std::cout << s << '\n';
}
}
And for the second approach:
#include <iostream> //include the standard library files individually
#include <vector> //#include <bits/stdc++.h> is terrible practice.
int main()
{
std::vector<std::string> words; //vector to store the strings
while(std::cin.peek() != '\n') //loop until next character to be read is '\n'
{
std::string str; //read a word
std::cin >> str;
words.push_back(str);
}
for(std::string &s : words)
{
std::cout << s << '\n';
}
}
You canuse getline to read ENTER, run on windows:
//#include<bits/stdc++.h>
#include <iostream>
#include <string> // for getline()
using namespace std;
int main()
{
int i = 0;
char a[100][100];
string temp;
do {
getline(std::cin, temp);
if (temp.empty())
break;
strcpy_s(a[i], temp.substr(0, 100).c_str());
} while (++i < 100);
for (int j = 0; j<i; j++)
{
cout << a[j] << endl;
}
return 0;
}
While each getline will got a whole line, like "hello world" will be read once, you can split it, just see this post.
I have this .txt file that has a lot of words ( one each line ).
I tried
ifstream myReadFile;
myReadFile.open("restrict_words.txt");
char output[100];
if (myReadFile.is_open()) {
while (!myReadFile.eof()) {
printf("mamao");
myReadFile >> output;
cout<<output;
}
}
But i dont know how to make it work like... where should i pass it path and stuff
I would like to do
while(reading){
stringArray.add(file.line);
}
How can i do that?
First, this: (!myReadFile.eof()) is wrong. See the link for why. Second. If all you want is to load a file of strings into an array, this will do it:
#include <iostream>
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
int main()
{
std::ifstream inp("restrict_words.txt");
std::istream_iterator<std::string> inp_it(inp), inp_eof;
std::vector<std::string> words(inp_it, inp_eof);
// words now has ever whitespace separated string
// from the input file as a vector entry
for (auto s : words)
std::cout << s << '\n';
}
Suggested reading:
std::vector<>
std::string
std::istream_iterator<>
C++11 Range-based for loop
Do you mean this?
//untested
#include <vector>
#include <fstream>
#include <string>
#include <iostream> //edited
int main()
{
std::ifstream ist("restrict_words.txt");
std::string word;
std::vector<std::string> readWords;
while(ist >> word)
readWords.push_back(word);
//test
for(unsigned i = 0; i != readWords.size(); ++i)
std::cout << readWords.at(i) << '\n'; // or readWords[i] (not range checked)
}
EDIT:
For each individual line you would do:
std::string line;
std::vector<std::string> readLines;
while(std::getline(ist, line))
{
readLines.push_back(line);
}
I'm having a problem with this code. After compiling with g++, I run a.out and I get a segmentation fault and no "here" displayed. The code is pretty short:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
bool inWords(vector<string> words, string str);
int main()
{
cout << "here";
vector<string> words;
string str;
istringstream iss;
ifstream file("data.txt", ifstream::in);
// read in words
for(int i = 0; file >> str; /*no i++*/)
{
if(str[str.length() - 1] == '.')
str.erase( str.length()-1);
// if word has a period at the end, erase it
if(!inWords(words, str))
{
// if word is not in vector words, add it
words.push_back(str);
i++;
}
}
// output each word
for (vector<string>::size_type i = 0; i < words.size(); i++)
cout << words[i];
// return to beginning of file
file.clear();
file.seekg(0, ios::beg);
// read in sentences
// to be implemented
file.close();
return 0;
}
bool inWords(vector<string> words, string str)
{
for(int i = 0; !words[i].empty(); i++)
if(words[i] == str) { return true; }
return false;
}
As far as I know, nothing should be a problem. data.txt is definitely in the same directory as the file and I receive no arguments from the command line. Can anyone help?
It won't be before main. Try using a debugger to see where it happens (eg GDB) which are incredibly handy tool. The reason you don't see "here" is because the buffer isn't flushed. Put a << std::endl after it so that it forces output at that point.
A technicality: You can segfault before main but that will happen in a constructor. I see you have no custom objects defined/instantiated in global scope.
The technique for iterating the vector in inWords is wrong and interacts with elements past the end of the vector causing the segmentation fault.
This program immediately accesses words[0] in inWords when the words vector is empty and words[0] (the first element of the vector) does not exist yet because the size of the vector is still zero but the loop does not do anything to avoid this condition.
I think inWords could be better implemented with std::find, perhaps like this:
bool inWords(const vector<string>& words, const string& str)
{
return std::find(words.begin(), words.end(), str) != words.end();
}
Remember to #include <algorithm> to make use of std::find. I also changed the parameters to pass by const reference, so you'll need to change the forward declaration of that function. I also added an endl to the output to make it readable.
Full text of repair:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
bool inWords(const vector<string>& words, const string& str);
int main()
{
vector<string> words;
string str;
istringstream iss;
ifstream file("data.txt", ifstream::in);
// read in words
for(int i = 0; file >> str; /*no i++*/)
{
if(str[str.length() - 1] == '.')
str.erase( str.length()-1);
// if word has a period at the end, erase it
if(!inWords(words, str))
{
// if word is not in vector words, add it
words.push_back(str);
i++;
}
}
// output each word
for (vector<string>::size_type i = 0; i < words.size(); i++)
cout << words[i] << std::endl;
// return to beginning of file
file.clear();
file.seekg(0, ios::beg);
// read in sentences
// to be implemented
file.close();
return 0;
}
bool inWords(const vector<string>& words, const string& str)
{
return std::find(words.begin(), words.end(), str) != words.end();
}
I have a class called StringList consisting of a constructor and a de-structor. What I am shooting for is for the ability of my program to retain its strings in the array even after it is not running. The way i want to do this is to have my constructor function read strings from a file and store them into my string array (str[]). My de-structor will save my current strings into my file. I am having trouble reading and storing from the file when memory is created. I want each word to be one element in the array.
For example, in the file that is being read from, the strings are stored as such:
HELLO
MOM
DAD
FOUR
YELLOW
I want each word to be a slot. In other words. str[0] = HELLO, str[1]= MOM, str[2]=DAD and such.
Here is my constructor function:
StringList::StringList()
{
numberOfStrings=0;
str = new string[1000000];
ifstream myfile ("Read.txt");
if (myfile.is_open())
{
for (int i = 0; i < 1000000; i++)
{
getline(myfile,str[i]);
numberOfString++;
}
myfile.close();
}
}
Problem here is the for (int i=0; i<100000;i++) line
What this did is continue to fill each blank space into the element until it reached 100000.
Same if i put i<20, it would read all the contents and add blanks to fill to 20. Is there anyway to fill up to the amount of actual strings in the txt. file?
NumberOfStrings++ is outside of your for loop when you read (i.e. it only gets incremented once). Also please consider using std::vector<std::string> instead of a dynamic array.
Here's a version of your code using std::vector instead of an array:
#include <vector>
#include <fstream>
#include <iostream>
#include <string>
class StringList
{
public:
StringList(): str(1000000), numberOfStrings(0)
{
std::ifstream myfile ("Read.txt");
if (myfile.is_open())
{
for (int i = 0; i < str.size(); i++)
{
getline(myfile, str[i]);
numberOfStrings++;
}
myfile.close();
}
}
StringList::~StringList()
{
std::ofstream os("Read.txt");
for (int i = 0; i <numberOfStrings; i++)
{
os << str[i] << std::endl;
}
}
private:
std::vector<std::string> str;
int numberOfStrings;
};
As you can see the changes are rather minimal.
The numberOfStrings variable is only updated once after the for loop has finished. You can also simplify this without the need to specify a large number of lines to read by checking the return value of getline for failure. If you try to read past the end of file is getline will return false.
numberOfStrings = 0;
str = new std::string[1000000];
std::ifstream myfile("Read.txt");
if (myfile.is_open())
{
std::string line;
while(getline(myfile, str[numberOfStrings]))
numberOfStrings++;
myfile.close();
}
You can simplify this even further by using std::vector. To expand on the example provided in your answer StringList might look something like below.
StringList.h
#include <vector>
#include <string>
class StringList
{
public:
StringList();
void PrintWords();
private:
size_t numberOfLines;
std::vector<std::string> str;
};
StringList.cpp to read in single line into each string
#include "StringList.h"
#include <fstream>
StringList::StringList()
{
std::ifstream myfile("Read.txt");
if (myfile.is_open())
{
std::string line;
while(getline(myfile, line))
{
lines_.push_back(line);
}
myfile.close();
}
numberOfLines = str.size();
}
StringList.cpp to read in single word into each string using std::istream_itertor and std::copy
#include "StringList.h"
#include <fstream>
#include <istream>
#include <algorithm> // std::copy
#include <iterator> // istream_iterator
StringList::StringList()
{
std::ifstream myfile("Read.txt");
if (myfile.is_open())
{
std::copy(
istream_iterator<string>(myfile),
istream_iterator<string>(),
back_inserter(str));
}
numberOfLines = str.size();
}
Some other function to print the words
StringList::PrintWords()
{
for(size_t i = 0; i < numberOfLines; ++i)
{
std::cout << str[i] << std::endl;
}
}
I also recommend avoiding using using namespace std in your code. It pulls everything from std into the current scope (typically the global namespace) and can cause conflicts with identifiers.
This would be my approach to reading the data (this doesn't quite work the same as the other answers, but as long as your wordlist doesn't contain words with whitespace, it should work fine).
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
int main()
{
std::fstream myFile("read.txt");
if (myFile.is_open())
{
std::istream_iterator<std::string> iter(myFile), end;
std::vector<std::string> str(iter, end);
// print contents
for (int i = 0; i < str.size(); i++)
std::cout << i << ": " << str[i] << std::endl;
}
}
References:
istream_iterator
vector
You can continue to averse std::vector all you want, but for a scenario like this, it is the best tool for the job.
I would like to read an input file in C++, for which the structure (or lack of) would be something like a series of lines with text = number, such as
input1 = 10
input2 = 4
set1 = 1.2
set2 = 1.e3
I want to get the number out of the line, and throw the rest away. Numbers can be either integers or doubles, but I know when they are one or other.
I also would like to read it such as
input1 = 10
input2=4
set1 =1.2
set2= 1.e3
so as to be more robust to the user. I think this means that it shouldn't be red in a formatted fashion.
Anyway, is there a smart way to do that?
I have already tried the following, but with minimal knowledge of what I've been doing, so the result was as expected... no success.
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <boost/lexical_cast.hpp>
#include <string>
using namespace std;
using namespace boost;
int main(){
string tmp;
char temp[100];
int i,j,k;
ifstream InFile("input.dat");
//strtol
InFile.getline(temp,100);
k=strtol(temp,0,10);
cout << k << endl;
//lexical_cast
InFile.getline(temp,100);
j = lexical_cast<int>(temp);
cout << j << endl;
//Direct read
InFile >> tmp >> i;
cout << i << endl;
return 0;
}
Simply read one line at a time.
Then split each line on the '=' sign. Use the stream functionality do the rest.
#include <sstream>
#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream data("input.dat");
std::string line;
while(std::getline(data,line))
{
std::stringstream str(line);
std::string text;
std::getline(str,text,'=');
double value;
str >> value;
}
}
With error checking:
#include <sstream>
#include <fstream>
#include <iostream>
#include <string>
int main()
{
std::ifstream data("input.dat");
std::string line;
while(std::getline(data,line))
{
std::stringstream str(line);
std::string text;
double value;
if ((std::getline(str,text,'=')) && (str >> value))
{
// Happy Days..
// Do processing.
continue; // To start next iteration of loop.
}
// If we get here. An error occurred.
// By doing nothing the line will be ignored.
// Maybe just log an error.
}
}
There are already some fine solutions here. However, just to throw it out there, some comments implied that Boost Spirit is an inappropriate solution for this problem. I'm not sure I completely disagree. However, the following solution is very terse, readable (if you know EBNF) and error-tolerant. I'd consider using it.
#include <fstream>
#include <string>
#include <boost/spirit.hpp>
using namespace std;
using namespace boost::spirit;
int main()
{
ifstream data("input.dat");
string line;
vector<double> numbers;
while(getline(data,line))
{
parse(line.c_str(),
*(+~ch_p('=') >> ch_p('=') >> real_p[push_back_a(numbers)]),
space_p);
}
}
Off the top of my head:
vector<double> vals(istream &in) {
vector<double> r;
string line;
while (getline(f, line)) {
const size_t eq = line.find('=');
if (eq != string::npos) {
istringstream ss(line.substr(eq + 1));
double d = 0;
ss >> d;
if (ss) r.push_back(d);
else throw "Line contains no value";
}
else {
throw "Line contains no =";
}
}
return r;
}
int main(int argc, char *argv[]) {
vector<double> vs = vals(ifstream(argv[1]));
}
C FTW (modified to handle doubles)
#include <stdio.h>
int
main ()
{
double num;
while (!feof (stdin))
if (1 == fscanf (stdin, "%*[^=] = %lf", &num))
printf ("%g\n", num);
return 0;
}
now that you are already using boost with lexical_cast, just parse each line with boost::split() and boost::is_any_of() into 1 2-element vector, with token_compress turned on.
the following code illustrates the parse, but skips the numeric conversion, which could be solved easily with boost lexical_cast.
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <vector>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/foreach.hpp>
using std::string;
using std::cout;
using std::ifstream;
using std::stringstream;
using std::vector;
std::string file_to_string()
{
ifstream data("data.txt");
stringstream s;
s << data.rdbuf();
return s.str();
}
void print_parameter(vector<string>& v)
{
cout << v_para[0];
cout << "=";
cout << v_para[1];
cout << std::endl;
}
vector<string> string_to_lines(const string& s)
{
return v_lines;
}
int main()
{
vector<string> v_lines;
boost::split(v_lines, file_to_string(), boost::is_any_of("\n"), boost::token_compress_on);
vector<string> v_para;
BOOST_FOREACH(string& line, v_lines)
{
if(line.empty()) continue;
boost::split(v_para, line, boost::is_any_of(" ="), boost::token_compress_on);
// test it
print_parameter(v_para);
}
}
If you are devising this format, I would suggest adopting the INI file format.
The lightweight syntaxed INI format includes sections (allows you to have a little more structure in the format) which may or may not be desirable in your case:
I.e.
[section_1]
variable_1=value1
variable_2=999
[sectionA]
variable_A=value A
variable_B=111
The external links on this wikipedia page list a number of libraries that can be used for working with these types of files that extend/replace the basic GetPrivateProfileString functions from the Windows API and support other platforms.
Most of these would handle the space padded = sign (or at least before the = since a space after the = may be intentional/significant.
Some of these libraries might also have an option to omit [sections] if you don't want that (my own C++ class for handling INI like format files has this option).
The advantage to these libraries and/or using the Windows API GetPrivateProfileXXX functions is that your program can access specific variables
(I.e. get or set the value for variable_A from sectionA) without your program having to
write/scan/rewrite the entire file.
Here's my quickest STL solution:
#include <fstream>
#include <list>
#include <locale>
void foo()
{
std::fstream f("c:\\temp\\foo.txt", std::ios_base::in);
std::list<double> numbers;
while (!f.eof())
{
int c = f.get();
if (std::isdigit(c, std::locale::classic()) ||
c == '+' ||
c == '-' ||
c == '.')
{
f.putback(c);
double val;
f >> val;
if (f.fail()) {
f.clear(f.eof() ? std::ios_base::eofbit : std::ios_base::goodbit);
continue;
}
else
{
numbers.push_back(val);
}
}
}
}
Just tested this... it works, and doesn't require anything outside of the C++ standard library.
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>
#include <sstream>
using namespace std; // just because this is an example...
static void print(const pair<string, double> &p)
{
cout << p.first << " = " << p.second << "\n";
}
static double to_double(const string &s)
{
double value = 0;
istringstream is(s);
is >> value;
return value;
}
static string trim(const string &s)
{
size_t b = 0;
size_t e = s.size();
while (b < e && isspace(s[b])) ++b;
while (e > b && isspace(s[e-1])) --e;
return s.substr(b, e - b);
}
static void readINI(istream &is, map<string, double> &values)
{
string key;
string value;
while (getline(is, key, '='))
{
getline(is, value, '\n');
values.insert(make_pair(trim(key), to_double(value)));
}
}
int main()
{
map<string, double> values;
readINI(cin, values);
for_each(values.begin(), values.end(), print);
return 0;
}
EDIT: I just read the original question and noticed I'm not producing an exact answer. If you don't care about the key names, juts discard them. Also, why do you need to identify the difference between integer values and floating-point values? Is 1000 an integer or a float? What about 1e3 or 1000.0? It's easy enough to check if a given floating-point value is integral, but there is a clas of numbers that are both valid integers and valid floating-point values, and you need to get into your own parsing routines if you want to deal with that correctly.