I am reposting this with all of the code this time. I would appreciate not closing the post for at least a little while. I am obviously no expert and I have never run into anything like this before but I do think it can be useful to other members. I tried the comments and agree the error is with destruction but can't find where. I have included the location of the seg fault in comment towards the bottom. I don't have an IDE and don't know how to use the debugger surely built into xterm so I am at a loss!!
#include <iostream>
#include <fstream>
#include <string>
#include "File.h"
using namespace std;
string promptQuit()
{
string userMode;
cout
<<
"Would you like to [q]uit? Enter any other key to continue."
<<
endl;
cin >> userMode;
cin.clear();
cin.ignore( 1000,'\n' );
return userMode;
}
int main()
{
/**************************************************************************/
/* Variable Declarations and Initializations */
/**************************************************************************/
char fileName[256];
char destinationFile[256];
string userMode;
/**************************************************************************/
/* Begin prompting user for what they want to do, starting with */
/* the filename, then what they want to do with the file. Once the user */
/* finishes with a particular file, they may continue with another file. */
/* Therefore, this loop terminates only when the user asks to quit */
/**************************************************************************/
while( userMode != "q" )
{
cout
<<
"Welcome to the file handling system. Please type the name of the "
"file that you wish to read from, write to, or modify followed by "
"the <return> key:"
<<
endl;
cin.getline( fileName, 256 );
File thisFile( fileName );
cout
<<
"Current File: " << thisFile.getFileName() << "\nWhat would you "
"like to do?\n[r]ead, [w]rite, [m]odify, or [q]uit"
<<
endl;
cin >> userMode;
// Invalid entry handling: Reset the failure bit and skip past the
// invalid input in the stream, then notify and re-prompt the user for
// valid input
while( !( (userMode == "w") | (userMode == "r") | (userMode == "q") |
(userMode == "m" ) ) )
{
cout
<<
"Invalid entry, please try again\nWhat would you like to do?\n"
"[r]ead, [w]rite, [m]odify, or [q]uit"
<<
endl;
cin >> userMode;
cin.clear();
cin.ignore( 1000, '\n' );
}
/*********************************************************************/
/* Write Mode: The user is prompted to enter one number at a time */
/* and this number is written to the chosen file. If the user enters*/
/* an invalid number, such as a letter, the user is notified and */
/* prompted to enter a valid real number */
/*********************************************************************/
if( userMode == "w" )
thisFile.writeTo();
/*********************************************************************/
/* Read Mode: The user reads in the entire contents from the file */
/* they have chosen */
/*********************************************************************/
if( userMode == "r" )
thisFile.readFrom();
/*********************************************************************/
/* Modify Mode: The user may either leave the old file unmodified and*/
/* place the modified contents into a new file or actually modify the*/
/* original file. */
/* The user reads in one line from the file at a time and can either */
/* choose to accept this number, replace it, delete it, or accept it */
/* and insert one or more numbers after it. At any time the user may*/
/* also choose to accept the remainder of the numbers in the file */
/*********************************************************************/
if( userMode == "m" )
{
cout
<<
"Do you want to modify the original file?\n[y]es/[n]o?"
<<
endl;
string modify;
cin >> modify;
while( !( ( modify == "y" ) | ( modify == "n" ) ) )
{
cout
<<
"Invalid entry, please try again\nDo you want to modify "
"the original file?\n[y]es/[n]o?"
<<
endl;
cin >> userMode;
cin.clear();
cin.ignore( 1000, '\n' );
}
if( modify == "y" )
{
File tempFile;
thisFile.modify( &tempFile );
}
if( modify == "n" )
{
cout
<<
"Please type the name of the destination file followed by "
"the <return> key:"
<<
endl;
cin.getline( destinationFile, 256 );
File newFile( destinationFile );
thisFile.modify( &newFile );
/****************************************************************/
/****Seg fault occurs here. Never exits this IF but above*******/
/*function does return.Doesn't get past close curly brace********/
/****************************************************************/
}
}
userMode = promptQuit();
}
return 0;
}
Here is the .cpp file
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <math.h>
#include <iomanip>
#include "File.h"
using namespace std;
// Returns ordinal number from input integer num, e.g., 1st for 1
string ordinalString( const int num )
{
stringstream numeric;
numeric << num;
string ordinalUnit;
string ordinal = numeric.str();
switch( num%10 )
{
case 1: ordinalUnit = "st"; break;
case 2: ordinalUnit = "nd"; break;
case 3: ordinalUnit = "rd"; break;
default: ordinalUnit = "th"; break;
}
switch( num )
{
case 11: ordinalUnit = "th"; break;
case 12: ordinalUnit = "th"; break;
case 13: ordinalUnit = "th"; break;
}
ordinal += ordinalUnit;
return ordinal;
}
float promptRealNumber()
{
float validEntry;
// Invalid entry handling: Reset the failure bit and skip past the
// invalid input in the stream, then notify and re-prompt the user for
// valid input
while ( !(cin >> validEntry) )
{
cout << "Invalid Input: Entry must be a real number. Please try again:";
cin.clear();
cin.ignore( 1000, '\n' );
}
return validEntry;
}
File::File()
{
fileName = "temp.txt";
entries = 0;
}
File::File( const char * file_name )
{
entries = 0;
string currentLine;
fileName = file_name;
ifstream thisFile( file_name );
if ( thisFile.is_open() )
{
while ( !thisFile.eof() )
{
getline ( thisFile, currentLine );
entries++;
}
thisFile.close();
}
else
cout << "Error opening file. File may not exist." << endl;
entries--;
}
File::File( const File * copyFile )
{
fileName = copyFile->fileName;
entries = copyFile->entries;
}
void File::promptNumEntries()
{
cout
<<
"Please enter the number of entries you wish to input into "
<< fileName << " followed by the '<return>' key"
<<
endl;
// Invalid entry handling: Reset the failure bit and skip past the invalid
// input in the stream, then notify and re-prompt the user for valid input
while ( !(cin >> entries) || ( floor( entries ) != entries ) )
{
cout << "Invalid Input: Entry must be an integer. Please try again: ";
cin.clear();
cin.ignore ( 1000, '\n' );
}
}
void File::readFrom()
{
string currentLine;
ifstream inFile( fileName.c_str() );
if ( inFile.is_open() )
{
while ( inFile.good() )
{
getline ( inFile, currentLine );
cout << currentLine << endl;
}
inFile.close();
}
else
cout << "Error opening file. File may not exist." << endl;
}
void File::writeTo()
{
ofstream outFile( fileName.c_str() );
string ending;
promptNumEntries();
for( int entry = 1; entry <= entries; entry++ )
{
// Notify the user which entry they are currently entering so if they lose
// their place, they can easily find which number they should be entering.
cout
<<
"Please enter the " << ordinalString( entry ) << " number followed "
"by the <return> key"
<<
endl;
float entryNum = promptRealNumber();
outFile << fixed << setprecision(1) << entryNum << endl;
}
outFile.close();
}
void File::modify( const File * destination_file )
{
ifstream sourceFile( fileName.c_str() );
ofstream destinationFile( destination_file->fileName.c_str() );
string currentLine;
string entryAction;
string insertMore = "y";
float replacementEntry;
float insertEntry;
int entry = 0;
if ( sourceFile.is_open() )
{
while ( !sourceFile.eof() )
{
getline( sourceFile, currentLine );
cout
<<
currentLine << endl << "Do you want to [k]eep this entry, "
"[r]eplace it, [d]elete it, [i]nsert after it, or accept this "
"and [a]ll remaining entries?"
<<
endl;
cin >> entryAction;
// Keep current entry. Also called when inserting an entry since
// this also requires keeping the current entry
if( ( entryAction == "k" ) | ( entryAction == "i" ) )
destinationFile << currentLine << endl;
// Replace current entry
if( entryAction == "r" )
{
cout
<<
"Please type the new entry followed by the <return> key:"
<<
endl;
replacementEntry = promptRealNumber();
destinationFile
<<
fixed << setprecision(1) << replacementEntry
<<
endl;
}
// Deleting the current entry amounts to simply ignoring it and
// continuing to the next entry, if it exists
if( entryAction == "d" );
// Insert one or more entries after current entry
if( entryAction == "i" )
{
while( insertMore == "y" )
{
cout
<<
"Please type the entry to be inserted followed by the "
"<return> key:"
<<
endl;
insertEntry = promptRealNumber();
destinationFile
<<
fixed << setprecision(1) << insertEntry
<<
endl;
cout << "Insert another number?\n[y]es/[n]o?" << endl;
cin >> insertMore;
while( !( (insertMore == "y") | (insertMore == "n" ) ) )
{
cout
<<
"Invalid entry, please try again\nInsert another "
"number?\n[y]es/[n]o?"
<<
endl;
cin >> insertMore;
cin.clear();
cin.ignore( 1000, '\n' );
}
}
}
// Accept all remaining entries
if( entryAction == "a" )
{
destinationFile << currentLine << endl;
while ( entry < entries )
{
getline ( sourceFile, currentLine );
destinationFile << currentLine << endl;
entry++;
}
}
destinationFile.close();
sourceFile.close();
}
}
else
cout << "Error opening file. File may not exist." << endl;
}
void File::copyFileContents( const File * to, const File * from )
{
ifstream fromFile( to->fileName.c_str() );
ofstream toFile( from->fileName.c_str() );
string currentLine;
while( !fromFile.fail() && !toFile.fail() )
{
for( int line = 0; line < from->entries; line++ )
{
getline( fromFile, currentLine );
toFile << currentLine;
}
}
}
Here is the .h file
#include <string>
using namespace std;
class File
{
public:
File();
File( const char * );
File( const File * copyFile );
~File() { delete this; }
string getFileName() { return fileName; }
float numEntries() { return entries; }
void setNumEntries( const float numEntries ) { entries = numEntries; }
void promptNumEntries();
void readFrom();
void writeTo();
void modify( const File * );
void copyFileContents( const File * , const File * );
private:
string fileName;
float entries;
};
I tried commenting out the sourceFile.close() statement and still nothing. I know its a lot of code but whoever can help would be my hero of the century!!
Remove delete(this) from the destructor!
You aren't constructing a File with new (since you're using a static instance on the stack), but the destructor is called anyway. So the delete statement is invalid and probably causes the segfault.
I've once seen a SEG fault on an if statement and the cause of it (after many hours of misery) turned out to be because I was accessing a private data member of the object, but that I had already started to destroy the object.
My guess is that it might be this line as that looks to me that you are destroying a resource that you are still using.:
sourceFile.close();
Try commenting that out and see how it goes.
Related
I'm new to C++ and I'm running into an issue on one of my assignments. The goal is to load data from a data file that looks like this.
item number date quantity cost per each
1000 6/1/2018 2 2.18
1001 6/2/2018 3 4.44
1002 6/3/2018 1 15.37
1001 6/4/2018 1 4.18
1003 6/5/2018 7 25.2
Basically I need to do calculations the average item number used for each date using arrays and do some other calculations with the cost. I'm getting really hung up with loading the data from the file and manipulating it for equations. This is what I have so far.
#include <cmath> //for math operations
#include <iostream> //for cout
#include <cstdlib> //for compatibility
#include <fstream>
#include <string>
using namespace std;
int main()
{
string date;
int EOQ, rp;
int count;
int itemnum[][];
double quantity[][];
double cost[][];
ifstream myfile;
string filename;
cout << "Data File: " << endl;
cin >> filename; // user enters filename
myfile.open(filename.c_str());
if(myfile.is_open())
{
cout << "file opened" << endl;
string head;
while(getline(myfile, head))
{
break; // so header won't interfere with data
}
while(!myfile.eof())
{ // do this until reaching the end of file
int x,y;
myfile >> itemnum[x][y] >> date >> quantity[x][y] >> cost[x][y];
cout << "The numbers are:" << endl;
for(count = 0; count < y; count++)
{
cout << itemnum[x][y] << endl;
break;
}
//cout << "Item: Reorder Point: EOQ: " << endl;
//cout << itemnum << " " << rp << " " << EOQ << endl;
break;
}
}
else
{
cout << "" << endl; //in case of user error
cerr << "FILE NOT FOUND" << endl;
}
cout << endl;
cout << "---------------------------------------------" << endl;
cout << " End of Assignment A8" << endl;
cout << "---------------------------------------------" << endl;
cout << endl;
system("pause");
return 0;
I haven't started working with the equations yet since I still can't get the file loaded in a simple array!!!
Thank you!
Link for data file : https://drive.google.com/file/d/1QtAC1bu518PEnk4rXyIXFZw3AYD6OBAv/view?usp=sharing
When working on these kinds of problems I like to break these down into the parts related to parsing. I'm using some of the standard libraries to do some of the work for me. I also created a couple of structures to help keep the information of the data organized. As for your date, I could of left that as a single std::string but I chose to break the date down into three individual types themselves and store them into a data structure just to show the capabilities of one of the functions that is involved with parsing.
What I prefer doing is to get either a single line of data from a file and save that to a string, or get the entire contents of a file and save that either to a large buffer or a vector of strings, unless if I'm handling specific type of code where that is not applicable such as parsing a wav file. Then close the file handle as I'm done reading from it! Then after I have all of the information I need, instead of trying to parse the file directly while it is opened I'd rather parse a string as it is easier to parse. Then after parsing the string we can populate our data types that we need.
I had to modify your data file slightly to accommodate for the extra white spaces so I saved your file as a text file with only a single white space between each data type within a single line of text. I also did not include the first line (header) information as I just omitted it completely. However this should still act as a guide of how to design a good work flow for an application that has good readability, reusability, try to keep it portable and as generic as possible. Now, what you have been waiting for; the demonstration of my version of your code:
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <exception>
struct Date {
int month;
int day;
int year;
Date() = default;
Date( int monthIn, int dayIn, int yearIn ) :
month( monthIn ),
day( dayIn ),
year( yearIn )
{}
};
struct DataSheetItem {
int itemNumber;
Date date;
int quantity;
double costPerEach;
DataSheetItem() = default;
DataSheetItem( int itemNumberIn, Date& dateIn, int quantityIn, double costPerEachIn ) :
itemNumber( itemNumberIn ),
date( dateIn ),
quantity( quantityIn ),
costPerEach( costPerEachIn )
{}
};
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
void getDataFromFile( const char* filename, std::vector<std::string>& output ) {
std::ifstream file( filename );
if( !file ) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error( stream.str() );
}
std::string line;
while( std::getline( file, line ) ) {
if ( line.size() > 0 )
output.push_back( line );
}
file.close();
}
DataSheetItem parseDataSheet( std::string& line ) {
std::vector<std::string> tokens = splitString( line, ' ' ); // First parse with delimeter of a " "
int itemNumber = std::stoi( tokens[0] );
std::vector<std::string> dateInfo = splitString( tokens[1], '/' );
int month = std::stoi( dateInfo[0] );
int day = std::stoi( dateInfo[1] );
int year = std::stoi( dateInfo[2] );
Date date( month, day, year );
int quantity = std::stoi( tokens[2] );
double cost = std::stod( tokens[3] );
return DataSheetItem( itemNumber, date, quantity, cost );
}
void generateDataSheets( std::vector<std::string>& lines, std::vector<DataSheetItem>& dataSheets ) {
for( auto& l : lines ) {
dataSheets.push_back( parseDataSheet( l ) );
}
}
int main() {
try {
std::vector<std::string> fileConents;
getDataSheetItemsFromFile( "test.txt", fileContents );
std::vector<DataSheetItem> data;
generateDataSheets( fileConents, data );
// test to see if info is correct
for( auto& d : data ) {
std::cout << "Item #: " << d.itemNumber << " Date: "
<< d.date.month << "/" << d.date.day << "/" << d.date.year
<< " Quantity: " << d.quantity << " Cost: " << d.costPerEach << '\n';
}
} catch( const std::runtime_error& e ) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
NOTE This will not work with how your file currently is; this does not account for the first line of text (header information) and this does not account for any extra white spaces in between the data fields. If you add a single line of text when opening the file and read in a single line and just ignore it, then perform the loop to get all strings to add to vector to return back; your vectors will have the information in it but they will not be at the correct index locations of the vector because of all the extra white spaces. This is something you need to be aware of! Other than that; this is how I would basically design a program or application to parse data. This is by all means not 100% full proof and may not even be 100% bug free, but from a quick glance and running it through my debugger a few times it does appear to be without any noticeable bugs. There could also be some room for improvements for runtime efficiency, etc. but this is just a generalization of basic parsing.
I have an error message for the string class. Based on examples I have found through trying to solve this, I believe I am using the class correctly.
Below is the code :
int main()
{
string allData, gridNum;
ifstream gridData;
gridData.open ("/Users/Neo/Documents/UNi/Year_3/Grid Data Analysis Program/gridData.txt");
if (gridData.is_open())
{
while ( getline (gridData, allData) )
{
size_t gridNum = allData.find("Grid Receiver 34");
string receiverX = allData.substr (gridNum, 40);
cout << receiverX << endl;
}
gridData.close();
}
else cout << "Unable to open file..." << endl;
return 0;
}
error in the console...
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
(lldb)
I am trying to read from a text file into a string variable. I only want to read in 40 characters after the words "Grid receiver 34", then print the contents of the new string.
while ( getline (gridData, allData) )
{
size_t gridNum = allData.find("Grid Receiver 34");
string receiverX = allData.substr (gridNum, 40);
cout << receiverX << endl;
}
Here you read the file line by line, searching for "Grid Receiver 34", however, if that string isn't found then std::string::find will return std::string::npos. Using that as argument for substr gets you in trouble. You should check if it's found before using it:
while ( getline (gridData, allData) )
{
size_t gridNum = allData.find("Grid Receiver 34");
if(gridNum != std::string::npos)
{
string receiverX = allData.substr (gridNum, 40);
cout << receiverX << endl;
}
}
Also, stop using using namespace std;.
You're probably getting an exception on the lines where the search string is not found.
You want to only try to extract the substring on lines where the string is found.
Modify your code as follows:
int main()
{
string allData, gridNum;
ifstream gridData;
gridData.open ("/Users/Neo/Documents/UNi/Year_3/Grid Data Analysis Program/gridData.txt");
if (gridData.is_open())
{
while ( getline (gridData, allData) )
{
size_t gridNum = allData.find("Grid Receiver 34");
if (gridNum != std::string::npos) // add this condition :-)
{
string receiverX = allData.substr (gridNum, 40);
cout << receiverX << endl;
}
}
gridData.close();
}
else cout << "Unable to open file..." << endl;
return 0;
}
Hello im writing my assignment and have it done at all but one little thing is still confusing me. I want to validate float input so if user types char it should display error message. My struggle is that whatever i do my loop either doesn't work or loops forever. Thanks a lot for any advice.
float fuel;
char ch= ???;
if(fuel==ch)
{
do
{cout<<"Input is not valid. Please enter numeric type!";
cin>>fuel;}
while(fuel!=ch);
The way you're trying to do it won't work - since you're comparing a float and char they will most definently just about never be equal.
Try this method instead:
bool notProper = true;
while(notProper) {
std::string input;
std::cin >> input
if( input.find_first_not_of("1234567890.-") != string::npos ) {
cout << "invalid number: " << input << endl;
} else {
float fuel = atof( num1.c_str() );
notProper = false;
}
};
Try code like this.
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
bool CheckFloat( istream & is, float& n ) {
string line;
if ( ! getline( is, line ) ) {
return false;
}
char * ep;
n = strtol( line.c_str(), & ep, 10 );
return * ep == 0;
}
int main() {
float n;
while(1) {
cout << "enter an float: ";
if ( CheckFloat( cin, n ) ) {
cout << "is float" << endl;
}
else {
cout << "is not an float" << endl;
}
}
}
float num;
//Reading the value
cin >> num;
//Input validation
if(!cin || cin.fail())
{
cout << "Invalid";
}
else
{
cout << "valid";
}
You can use above logic to verify input!
Part of my source, please ignore 'serror' usage, it's just basically throws a string error:
//----------------------------------------------------------------------------------------------------
inline double str_to_double(const std::string& str){
char *end = NULL;
double val = strtod(str.c_str(), &end);
if(end == str.c_str() || end - str.c_str() != str.length())
serror::raise("string '%s' does not represent a valid floating point value", str.c_str());
if(val == +HUGE_VAL)
serror::raise("string '%s' represents floating point value which is too big", str.c_str());
if(val == -HUGE_VAL)
serror::raise("string '%s' represents floating point value which is too small", str.c_str());
return val;
}
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 );
}
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.