I'm trying to implement prefix to infix in c++, that's what i've got so far. The input should be for example something like this:
/7+23
And the ouput:
7/(2+3) or (7/(2+3))
But instead I get:
(/)
That's the code I wrote so far:
void pre_to_in(stack<char> eq) {
if(nowe.empty() != true) {
char test;
test = eq.top();
eq.pop();
if(test == '+' || test == '-' || test == '/' || test == '*') {
cout << "(";
pre_to_in(eq);
cout << test;
pre_to_in(eq);
cout << ")";
} else {
cout << test;
}
}
}
// somewhere in main()
char arr[30];
stack<char> stosik;
int i = 0;
cout << "write formula in prefix notation\n";
cin >> arr;
while(i < strlen(arr)) {
stosik.push(arr[i]);
i++;
}
pre_to_in(stc);
This is a stack. First in, last out. You need reverse input string "32+7/".
You use many stacks. In every enter to pre_to_in() stack is copied. Use reference or pointer, ex: void pre_to_in(stack<char> &eq);
Thats all.
P.S. Unify names (s/nowe/eq/g && s/stc/stosik/g)
cin >> arr;
only reads one "word" of input, not a whole line. Here it's only getting the first slash character.
not sure if you are looking for such solution, anyway for the input you've mentioned it gives the output from you post
it reads tokens from std input
I've built it now under Visual Studio 2005 - to terminate input press Enter, Ctrl+Z, Enter
but on other compilers termination may work in another way
#include <algorithm>
#include <deque>
#include <iostream>
#include <string>
typedef std::deque< std::string > tokens_t;
void pre_to_in( tokens_t* eq )
{
if ( !eq->empty() ) {
const std::string token = eq->front();
eq->pop_front();
if ( ( token == "+" ) || ( token == "-" ) || ( token == "/" ) || ( token == "*" ) ) {
std::cout << "(";
pre_to_in( eq );
std::cout << token;
pre_to_in( eq );
std::cout << ")";
} else {
std::cout << token;
}
}
}
int main()
{
std::cout << "write formula in prefix notation" << std::endl;
tokens_t tokens;
std::copy(
std::istream_iterator< std::string >( std::cin ),
std::istream_iterator< std::string >(),
std::back_inserter( tokens ) );
pre_to_in( &tokens );
}
Related
I would like to make an array of words like: "Tom", "Mike","Tamara","Nik"... I would like to make for user to be possible to enter for instance a number 3, and get a random return of words that have the length of 3 so eather ("Tom" or "Nik"). I think this is done with pointers but I don't know how. Words should be stored in different arrays depending on their length. And with pointers you would point to each array ("Tom","Nik" in same array "Tamara" in different array and "Mike" in different array and so on... because their length is not the same). Can someone please help ?
#include<iostream>
#include <string>
using namespace std;
void IzpisPolja(char **polje,int velikost){
int tab[100];
for (int i=0; i<velikost; i++) {
cout<<polje[i]<<endl;
char *zacasni;
tab[i] = strlen(polje[i]);
// cout<<tab[i]<<endl;
}
}
int main(){
const int size = 4;
char* tabelaOseb[size] = {"Tom", "Mike","Tamara","Nik"};
IzpisPolja(tabelaOseb,size);
return 0;
}
Do you want to do it efficiently ? Storing them in separate arrays will increase search time but also increase insertion, deletion complexity.
Otherwise you can just count number of instances of n length words in an array, then generate random number and return the ith of them.
Also suggest using std::vector
const string* getRandNameOfLength(const string* arr,
const int arrlen,
const int length)
{
int num = 0, j, i;
// Counting number of such names
for (i = 0; i < arrlen; ++i)
{
if (arr[i].size() == length)
num++;
}
// No such name found
if (num == 0)
return NULL;
j = rand() % num;
// Returning random entry of given length
for (i = 0; i < arrlen; ++i)
{
if (arr[i].size() == length && j-- == 0)
return &arr[i];
}
// Function shouldn't get here
return NULL;
}
You can use raw pointers to perform your task, of course, but you can also start using some of the many safer facilities that the language (references, iterators and smart pointers) and the C++ standard library can offer.
I'll show you a complete program that can do what you are asking using conteiners (std::vector, std::map) and algorithms (like std::lower_bound) that can really simplify your work once understood.
Note that as a learning exercise (for both of us), I have used as many "new" features as I could, even when maybe wasn't necessary or handy. Read the comments for better understanding.
The words are stored and managed in a class, while the interaction with the user is performed in main().
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <limits>
#include <algorithm>
#include <random> // for mt19937, uniform_int_distribution
#include <chrono> // for high_resolution_clock
size_t random_index( size_t a, size_t b ) {
// Initialize Random Number Generator Engine as a static variable. - Since c++11, You can use those instead of old srand(time(NULL))
static std::mt19937 eng{static_cast<long unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count())};
// use the RNG to generate random numbers uniformly distributed in a range
return std::uniform_int_distribution<size_t>(a,b)(eng);
}
using svs_t = std::vector< std::string >; // I store the words with equal length in a std::vector of std::string
// like typedef, I'll use svs_t instead of std::vector<std::string>
auto string_less_then = [] (const std::string & a, const std::string & b) -> bool { return a.compare(b) < 0; };
// A lambda function is a mechanism for specifying a function object, its primary use is to specify a simple
// action to be performed by some function. I'll use it to compare two string and return true only if a<b
class word_table {
std::map< size_t, svs_t > words; // std::map store elements formed by a combination of a key value and a mapped value, sorted by key
// I'll use word's length as a key for svs_t values
public:
word_table() {}
word_table( std::initializer_list<std::string> vs ) {
insert_words(vs);
}
void insert_words( svs_t vs ) {
for ( auto && s : vs ) add_word(s); // loop for each value in vs, "auto" let the compiler infer the right type of the variable
}
bool add_word( std::string s ) { // I choose to keep the vector sorted and with unique elements
size_t sl = s.length();
if ( sl > 0 ) {
auto & v = words[sl]; // If sl doesn't match the key of any element in the map, a new element is created
// lower_bound return an iterator that poins to the first element in range (begin,end)
auto it = std::lower_bound(v.begin(), v.end(), s, string_less_then); // which does not compare less than s
// I pass the compare function as a lambda
if ( it != v.end() && it->compare(s) == 0 ) return false; // Already present, duplicates not allowed
v.insert(it, s); // Not the most efficient way, but you seem focused on the random access part
return true;
}
return false;
}
bool remove_word( std::string s) {
size_t sl = s.length();
if ( sl > 0 ) {
auto itvw = words.find(sl); // first find the right element in the map, using the string length as a key, but if word is found
if ( itvw == words.end() ) return false; // an iterator to the element following the last element of the container is returned
auto & v = itvw->second; // In a map the elements are stored in pairs, first is the key, second the value
auto it = std::lower_bound(v.begin(), v.end(), s, string_less_then);
if ( it != v.end() && it->compare(s) == 0 ) {
v.erase(it);
if ( v.empty() ) words.erase(itvw);
return true;
}
}
return false;
}
std::string get_random_word( size_t length ) {
if ( length == 0 ) return "";
auto itvw = words.find(length);
if ( itvw == words.end() || itvw->second.empty() ) return "";
return itvw->second[random_index(0, itvw->second.size() - 1)];
}
void show_all() {
for ( auto && i : words ) {
std::cout << " ";
for (auto && w : i.second ) {
std::cout << w << ' ';
}
std::cout << '\n';
}
}
};
constexpr size_t ss_max = std::numeric_limits<std::streamsize>::max();
namespace opt {
enum options { wrong = -1, exit, show, random, add, remove, menu };
}
class menu {
std::map<int,std::string> opts;
public:
menu( std::initializer_list<std::pair<int,std::string>> il ) {
for ( auto && i : il ) opts.insert(i);
}
void show() {
std::cout << "\nYou can choose among these options:\n\n";
for ( auto && i : opts ) {
std::cout << " " << i.first << ". " << i.second << ".\n";
}
}
};
int main()
{
word_table names({"Tom", "Mike","Tamara","Robert","Lenny","Nick","Alex","Sue","Irina","Beth","Anastacia","Bo"});
int choise = opt::exit;
menu menu_options { {opt::exit, "Exit program"}, {opt::show, "Show all stored names"},
{opt::random, "Show a random name"}, {opt::add, "Add a new name"},
{opt::remove, "Remove a name"} };
menu_options.show();
do {
std::cout << "\nPlease, enter a number (" << opt::menu << " to show again all options): ";
std::cin >> choise;
if ( std::cin.fail() ) { // the user enter something that is not a number
choise = opt::wrong;
std::cin.clear();
std::cin.ignore(ss_max,'\n');
}
if ( std::cin.eof() ) break; // use only if you are redirecting input from file
std::string str;
switch ( choise ) {
case opt::exit:
std::cout << "\nYou choose to quit, goodbye.\n";
break;
case opt::show:
std::cout << "\nAll the stored names, classified by word\'s length:\n\n";
names.show_all();
break;
case opt::random:
size_t l;
std::cout << "Please, enter the length of the name: ";
std::cin >> l;
if ( std::cin.good() ) {
std::string rs = names.get_random_word(l);
if ( rs == "" ) {
std::cout << "\nNo name of length " << l << " has been found.\n";
} else {
std::cout << "\n " << rs << '\n';
}
}
break;
case opt::add:
std::cout << "Please, enter the name You want to add: ";
std::cin >> str; // read a string from cin, you can write more than a word (separeted by spaces)
std::cin.ignore(ss_max,'\n'); // but only the first is stored
if ( names.add_word(str) ) {
std::cout << "\n The name " << str << " has been successfully added.\n";
} else {
std::cout << "\n No name has been added";
if ( str != "" ) std::cout << ", "<< str << " is already present.\n";
else std::cout << ".\n";
}
break;
case opt::remove:
std::cout << "Please, enter the name You want to remove: ";
std::cin >> str;
if ( names.remove_word(str) ) {
std::cout << "\n " << str << " has been succesfully removed.\n";
} else {
std::cout << "\n No name has been removed";
if ( str != "" ) std::cout << ", " << str << " wasn't found.\n";
else std::cout << ".\n";
}
break;
case opt::menu:
menu_options.show();
break;
default:
std::cout << "\n Sorry, that's not an option.\n";
}
} while ( choise != opt::exit );
return 0;
}
I hope it could help.
#include <iostream>
#include <vector>
#include <cstring>
#include <ctime>
#include <cstdlib>
using namespace std;
const char* return_rand_name(const char** names, size_t length)
{
std::vector<size_t> indexes;
for(int i=0; names[i][0] != 0; ++i)
if(strlen(names[i]) == length)
indexes.push_back(i);
if(indexes.size()==0)
return NULL;
return names[indexes[rand()%indexes.size()]];
}
int main()
{
srand(time(NULL));
const char* names[] = {"Alex","Tom","Annie","Steve","Jesus","Leo","Jerry",""};
std::cout << return_rand_name(names, 3) << std::endl;
return 0;
}
And if you want to use functions like strlen etc, include <cstring>, not <string> (which contains class template std::string (which you should use in C++ (instead of char*) ) )
Given a URL (which is a string) such as this:
www.testsite.com/pictures/banners/whatever/
I want to be able to get the characters of the last directory in the URL (in this case it's "whatever", I want to also remove the forward slashes). What would be the most efficient way to do this?
Thanks for any help
#include <iostream>
#include <string>
std::string getlastcomponent(std::string s) {
if (s.size() > 0 && s[s.size()-1] == '/')
s.resize(s.size() - 1);
size_t i = s.find_last_of('/');
return (i != s.npos) ? s.substr(i+1) : s;
}
int main() {
std::string s1 = "www.testsite.com/pictures/banners/whatever/";
std::string s2 = "www.testsite.com/pictures/banners/whatever";
std::string s3 = "whatever/";
std::string s4 = "whatever";
std::cout << getlastcomponent(s1) << '\n';
std::cout << getlastcomponent(s2) << '\n';
std::cout << getlastcomponent(s3) << '\n';
std::cout << getlastcomponent(s4) << '\n';
return 0;
}
Get the length and push every letter from last ( at example pseudo code:
x = string.length()
while(X != 0)
{
CharVector.push(string.at(x));
x--;
if(string.at(x) == "\") break;
}
then you got revetahw instead of whatever.
Then just swap it with this fucntion:
string ReverseString( const string& word )
{
std::string l_bla;
bla.reserve(word.size());
for ( string::size_type x = word.length ( ); x > 0; x-- )
{
l_bla += word.at ( x -1 );
}
return l_bla;
}
so you got whatever
I am doing a maze program and read the maze from a file, but i need to check the file only contain "s","f",“#” or "\n" characters otherwise print error messages? i tried many times , but really confused it ~
Now i'm trying to use STL to solve it, but have new problems!
void fillList(list<char> &myList, const char *mazeFile )
{
ifstream inFile;
string lines;
inFile.open(mazeFile);
while(!inFile.eof())
{
getline(inFile,lines);
for(int i=0;i<lines.length();i++)
myList.push_back(lines[i]);
}
}
bool checkMaze(list<char> &myList)
{
list<char>::iterator itr;
for (itr = myList.begin(); itr != myList.end(); itr++ )
{
if(*itr != 's' || *itr != 'f' || *itr != '#' || *itr != '\n')
return false;
}
return true;
}
myMaze.fillList(myList,argv[1]);
bool valid = myMaze.checkMaze(myList);
if(myMaze.isValid(argv[1]) && valid == true)
myMaze.printMaze();
else
{
cout << "Unable to load maze " << argv[1] << "\n";
return 0;
}
But it still not print ? what problem with that ?
Since you have a std::string, you might consider the various string search member functions.
E.g. string::find_first_not_of
std::string str ("s###X##f");
std::size_t found = str.find_first_not_of("sf#\n");
if (found!=std::string::npos) {
std::cout << "The first non-acceptible character is " << str[found];
std::cout << " at position " << found << '\n';
}
Here's a method that checks if a file contains a specific string:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
bool wordExists(char* file, char* word)
{
string line;
ifstream fileStream;
fileStream.open(file);
//until we can't read any more lines
while( getline(fileStream, line) )
{
if ( line.find(word) != string::npos )
return true;
}
return false;
}
Since you haven't provided your implementation of the problem, I can't determine what your error was - but feel free to let me know if this code doesn't work (I just roughed it out in Notepad++ for this question) or if you have any additional concerns.
I would like to write something in C++ that tokenize a string. For the sake of clarity, consider the following string:
add string "this is a string with spaces!"
This must be split as follows:
add
string
this is a string with spaces!
Is there a quick and standard-library-based approach?
No library is needed. An iteration can do the task ( if it is as simple as you describe).
string str = "add string \"this is a string with space!\"";
for( size_t i=0; i<str.length(); i++){
char c = str[i];
if( c == ' ' ){
cout << endl;
}else if(c == '\"' ){
i++;
while( str[i] != '\"' ){ cout << str[i]; i++; }
}else{
cout << c;
}
}
that outputs
add
string
this is a string with space!
I wonder why this simple and C++ style solution is not presented here.
It's based on fact that if we first split string by \", then each even chunk is "inside" quotes, and each odd chunk should be additionally splitted by whitespaces.
No possibility for out_of_range or anything else.
unsigned counter = 0;
std::string segment;
std::stringstream stream_input(input);
while(std::getline(stream_input, segment, '\"'))
{
++counter;
if (counter % 2 == 0)
{
if (!segment.empty())
std::cout << segment << std::endl;
}
else
{
std::stringstream stream_segment(segment);
while(std::getline(stream_segment, segment, ' '))
if (!segment.empty())
std::cout << segment << std::endl;
}
}
Here is a complete function for it. Modify it according to need, it adds parts of string to a vector strings(qargs).
void split_in_args(std::vector<std::string>& qargs, std::string command){
int len = command.length();
bool qot = false, sqot = false;
int arglen;
for(int i = 0; i < len; i++) {
int start = i;
if(command[i] == '\"') {
qot = true;
}
else if(command[i] == '\'') sqot = true;
if(qot) {
i++;
start++;
while(i<len && command[i] != '\"')
i++;
if(i<len)
qot = false;
arglen = i-start;
i++;
}
else if(sqot) {
i++;
start++;
while(i<len && command[i] != '\'')
i++;
if(i<len)
sqot = false;
arglen = i-start;
i++;
}
else{
while(i<len && command[i]!=' ')
i++;
arglen = i-start;
}
qargs.push_back(command.substr(start, arglen));
}
for(int i=0;i<qargs.size();i++){
std::cout<<qargs[i]<<std::endl;
}
std::cout<<qargs.size();
if(qot || sqot) std::cout<<"One of the quotes is open\n";
}
The Boost library has a tokenizer class that can accept an escaped_list_separator. The combination of these look like they might provide what you are looking for.
Here are links to the boost documentation, current as of this post and almost certainly an old version by the time you read this.
https://www.boost.org/doc/libs/1_73_0/libs/tokenizer/doc/tokenizer.htm
https://www.boost.org/doc/libs/1_73_0/libs/tokenizer/doc/escaped_list_separator.htm
This example is stolen from the boost documentation. Forgive me for not creating my own example.
// simple_example_2.cpp
#include<iostream>
#include<boost/tokenizer.hpp>
#include<string>
int main(){
using namespace std;
using namespace boost;
string s = "Field 1,\"putting quotes around fields, allows commas\",Field 3";
tokenizer<escaped_list_separator<char> > tok(s);
for(tokenizer<escaped_list_separator<char> >::iterator beg=tok.begin(); beg!=tok.end();++beg){
cout << *beg << "\n";
}
}
I would define a class Token to read a single token from a stream.
Then using your code becomes very trivial.
#include <iostream>
#include <string>
int main()
{
// Simply read the tokens from the stream.
Token t;
while(std::cin >> t)
{
std::cout << "Got: " << t << "\n";
}
}
Stream objects like this are very easy to write:
class Token
{
// Just something to store the value in.
std::string value;
// Then define the input and output operators.
friend std::ostream& operator<<(std::ostream& str, Token const& output)
{
return str << output.value;
}
// Input is slightly harder than output.
// but not that difficult to get correct.
friend std::istream& operator>>(std::istream& str, Token& input)
{
std::string tmp;
if (str >> tmp)
{
if (tmp[0] != '"')
{
// We read a word that did not start with
// a quote mark. So we are done. Simply put
// it in the destination.
input.value = std::move(tmp);
}
else if (tmp.front() == '"' && tmp.back() == '"')
{
// we read a word with both open and close
// braces so just nock these off.
input.value = tmp.substr(1, tmp.size() - 2);
}
else
{
// We read a word that has but has a quote at the
// start. So need to get all the characters upt
// closing quote then add this to value.
std::string tail;
if (std::getline(str, tail, '"'))
{
// Everything worked
// update the input
input.value = tmp.substr(1) + tail;
}
}
}
return str;
}
};
I guess there is no straight forward approach with standard library. Indirectly following algo will work:
a) search for '\"' with string::find('\"') . If anything found search for next '\"' using string::find('\'',prevIndex), If found use string::substr(). Discard that part from the original string.
b) Now Serach for ' ' character in the same way.
NOTE: you have to iterate through the whole string.
Here is my solution, it's equivalent to python's shlex, shlex_join() is the inverse of shlex_split():
#include <cctype>
#include <iomanip>
#include <iostream>
#include <string>
#include <sstream>
#include <utility>
#include <vector>
// Splits the given string using POSIX shell-like syntax.
std::vector<std::string> shlex_split(const std::string& s)
{
std::vector<std::string> result;
std::string token;
char quote{};
bool escape{false};
for (char c : s)
{
if (escape)
{
escape = false;
if (quote && c != '\\' && c != quote)
token += '\\';
token += c;
}
else if (c == '\\')
{
escape = true;
}
else if (!quote && (c == '\'' || c == '\"'))
{
quote = c;
}
else if (quote && c == quote)
{
quote = '\0';
if (token.empty())
result.emplace_back();
}
else if (!isspace(c) || quote)
{
token += c;
}
else if (!token.empty())
{
result.push_back(std::move(token));
token.clear();
}
}
if (!token.empty())
{
result.push_back(std::move(token));
token.clear();
}
return result;
}
// Concatenates the given token list into a string. This function is the
// inverse of shlex_split().
std::string shlex_join(const std::vector<std::string>& tokens)
{
auto it = tokens.begin();
if (it == tokens.end())
return {};
std::ostringstream oss;
while (true)
{
if (it->empty() || it->find_first_of(R"( "\)") != std::string::npos)
oss << std::quoted(*it);
else
oss << *it;
if (++it != tokens.end())
oss << ' ';
else
break;
}
return oss.str();
}
void test(const std::string& s, const char* expected = nullptr)
{
if (!expected)
expected = s.c_str();
if (auto r = shlex_join(shlex_split(s)); r != expected)
std::cerr << '[' << s << "] -> [" << r << "], expected [" << expected << "]\n";
}
int main()
{
test("");
test(" ", "");
test("a");
test(" a ", "a");
test("a b", "a b");
test(R"(a \s b)", "a s b");
test(R"("a a" b)");
test(R"('a a' b)", R"("a a" b)");
test(R"(a \" b)", R"(a "\"" b)");
test(R"(a \\ b)", R"(a "\\" b)");
test(R"("a \" a" b)");
test(R"('a \' a' b)", R"("a ' a" b)");
test(R"("a \\ a" b)");
test(R"('a \\ a' b)", R"("a \\ a" b)");
test(R"('a \s a' b)", R"("a \\s a" b)");
test(R"("a \s a" b)", R"("a \\s a" b)");
test(R"('a \" a' b)", R"("a \\\" a" b)");
test(R"("a \' a" b)", R"("a \\' a" b)");
test(R"("" a)");
test(R"('' a)", R"("" a)");
test(R"(a "")");
test(R"(a '')", R"(a "")");
}
There is a standard-library-based approach in C++14 or later. But it is not quick.
#include <iomanip> // quoted
#include <iostream>
#include <sstream> // stringstream
#include <string>
using namespace std;
int main(int argc, char **argv) {
string str = "add string \"this is a string with spaces!\"";
stringstream ss(str);
string word;
while (ss >> quoted(word)) {
cout << word << endl;
}
return 0;
}
I am reading a text file with this format:
grrr,some text,45.4321,54.22134
I just have my double valued stored in a string variable.
Why is it only giving me the first digit of the string?
If I start over with just one while loop and a text file of this new format:
21.34564
it works as it should.
The thing is, sLine has the the same value as the one when I started over. What is different is the three nested for loops that most likely is causing the problem.
Here is the code that gets me what I want:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <sstream>
using namespace std;
int main()
{
string usrFileStr,
fileStr = "test.txt", // declaring an obj string literal
sBuffer,
sLine,
str;
double dValue ;
int lineCount = 1;
int nStart;
istringstream issm;
fstream inFile; // declaring a fstream obj
// cout is the name of the output stream
cout << "Enter a file: ";
cin >> usrFileStr;
inFile.open( usrFileStr.c_str(), ios::in );
// at this point the file is open and we may parse the contents of it
while ( getline ( inFile, sBuffer ) && inFile.eof() )
{
cout << "Original String From File: " << sBuffer << endl;
cout << "Modified Str from File: " << fixed << setprecision(2)
<< dValue << endl;
}
fgetc( stdin );
return 0;
}
So there it works just like it should. But i cant get it to work inside a for loop or when i have multiple feilds in my text file...
With this code, why is it taken off the decimal?
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <sstream>
#include <errno.h>
using namespace std;
int main()
{
string usrFileStr,
myFileStr = "myFile.txt", // declaring an obj string literal
sBuffer,
sLine = "";
istringstream inStream;
int lineCount = 1;
int nStart;
double dValue = 0,
dValue2 = 0;
float fvalue;
fstream inFile; // declaring a fstream obj
// cout is the name of the output stream
cout << "Enter a file: ";
cin >> usrFileStr;
inFile.open( usrFileStr.c_str(), ios::in );
// at this point the file is open and we may parse the contents of it
if ( !inFile )
{
cout << "Not Correct " << endl;
}
while ( getline ( inFile, sBuffer ) )
{
nStart = -1 ;
for ( int x = nStart + 1; x < sBuffer.length(); x++ )
{
if ( sBuffer[ x ] == ',' )
{
nStart = x;
break;
}
cout << sBuffer[ x ];
}
for ( int x = nStart + 1; x < sBuffer.length(); x++ )
{
if ( sBuffer[ x ] == ',' )
{
nStart = x;
break;
}
cout << sBuffer[ x ];
}
for ( int x = nStart + 1; x < sBuffer.length(); x++ )
{
if ( sBuffer[ x ] == ',' )
{
nStart = x;
break;
}
sLine = sBuffer[ x ];
inStream.clear();
inStream.str( sLine );
if ( inStream >> dValue )
cout << setprecision(1) << dValue;
}
for ( int x = nStart + 1; x < sBuffer.length(); x++ )
{
if ( sBuffer[ x ] == ',' )
{
nStart = x;
break;
}
sLine = sBuffer[ x ];
inStream.clear();
inStream.str( sLine );
if ( inStream >> dValue2 )
cout << setprecision(1) << dValue2;
}
cout << ") \n";
lineCount++;
}
cout << "There are a Total of: " << lineCount -1 << " line(s) in the file."
<< endl;
inFile.clear(); // clear the file of any errors
inFile.close(); // at this point we are done with the file and may close it
fgetc( stdin );
return 0;
}
I don't have any other characters to loop over in the first code because im just reading a nice little double value.
In my second code, i have many characters to get to before the one that i want. But regardless, it is still isolated from the other characters and it is still in its own varaible. im to sick to realize what the problem is :/ although i think its the for loops.
I have also tried atof but i get a '0' where the decimal should be.
and strtod is hard because i need im not reading data into a const char *cPtr
Your code is a little tough to read. You probably want to think some point about encapsulation and breaking it up into functions.
Additionally, I would try to avoid reading in single characters and use the various functions and methods for reading data in fields - you can read a whole floating point or integer number using the >> stream extractors.
Finally, a useful skill to learn is how to use a debugger. You can step through the code and inspect the values of variables as you go.
That said, it looks like your problem is here:
if ( sBuffer[ x ] == ',' )
{
nStart = x;
break;
}
**** sLine = sBuffer[ x ];
inStream.clear();
inStream.str( sLine );
if ( inStream >> dValue2 )
cout << setprecision(1) << dValue2;
On the line marked with "****", you place exactly one character into the variable called "sLine". Having done so, you convert that one character into a double precision variable dValue2 and then output it. It should be obvious why this one character is converted into the first digit of the number you want.
Using instream>>dvalue is certainly the right way to do things. But sometimes what's right isn't always easiest or necessarily best.
We could do something like this:
int
main()
{
string s = "grrr,some text,45.4321,54.22134";
double a,b;
ASSERT_IS( 2, sscanf( s.c_str(), "%*[^,],%*[^,],%lf,%lf", & a, & b ) );
cout << setprecision(8);
SHOW(a);
SHOW(b);
}
Or perhaps something like this, while less efficient, might be easier to understand...
int
main()
{
string s = "grrr,some text,45.4321,54.22134";
vector<string> v;
StringSplit( & v, s, "," );
cout << setprecision(8);
SHOW(v);
SHOW(atof( v[2].c_str()));
SHOW(strtod(v[3].c_str(), (char**)NULL));
}
Assuming:
#define SHOW(X) cout << # X " = " << (X) f << endl
/* A quick & easy way to print out vectors... */
template<class TYPE>
inline ostream & operator<< ( ostream & theOstream,
const vector<TYPE> & theVector )
{
theOstream << "Vector [" << theVector.size() << "] {"
<< (void*)(& theVector) << "}:" << endl;
for ( size_t i = 0; i < theVector.size(); i ++ )
theOstream << " [" << i << "]: \"" << theVector[i] << "\"" << endl;
return theOstream;
}
inline void
StringSplit( vector<string> * theStringVector, /* Altered/returned value */
const string & theString,
const string & theDelimiter )
{
UASSERT( theStringVector, !=, (vector<string> *) NULL );
UASSERT( theDelimiter.size(), >, 0 );
size_t start = 0, end = 0;
while ( end != string::npos )
{
end = theString.find( theDelimiter, start );
// If at end, use length=maxLength. Else use length=end-start.
theStringVector -> push_back( theString.substr( start,
(end == string::npos) ? string::npos : end - start ) );
// If at end, use start=maxSize. Else use start=end+delimiter.
start = ( ( end > (string::npos - theDelimiter.size()) )
? string::npos : end + theDelimiter.size() );
}
}
Two points:
You might want to use sBuffer.find(',')
You set sLine to the last character before ",", is this intended to be so? You only parse single digit numbers correctly this way.