newb question with reading from file

Started by
8 comments, last by scarypajamas 15 years, 9 months ago
I'm trying to read a line from a file that looks like this: sells FRUIT.POPCORN.SODA. I'm using the code: if( "sells" == line.substr(0, line.find(' ')) ) To tell my program when I come upon the 'sells' line in the txt file and it works fine. The problem is when I try to read the fruit.popcorn.soda. part of it. I want to read them all separately and I'm using the period as a delimiter. My code is this: sells[0] = line.substr(6, line.find('.')); //substr(6 because "sells" and a space is 6 up std::cout << sells[0] << std::endl; for( int s=1; s<10; s++ ) { sells = line.substr(sells[s-1].size()+1, line.find('.')); std::cout &lt;&lt; sells &lt;&lt; std::endl; } It dosn't work though. I get an output to my cmd promt of somthing like this: FRUIT.POPCO POPCORN.SOD POPCORN.SOD POPCORN.SOD POPCORN.SOD POPCORN.SOD POPCORN.SOD Why? Why? I thought that line.substr(6, line.find('.')); would start at space 6 in the file and read up to the first period? Why is it outputting weird stuff? Could someone explain.
Advertisement
The documentation totally fails to explain this, but basically, .find() returns an index in the string, while .substr() expects a beginning index and *length*, not ending index.

You would be better served, at least in terms of keeping things clean, to use the free-standing .find() to find characters in the string, and construct substrings by using the iterator-range constructor of std::string. However, your *problem* is probably better solved by actually treating your file input as a stream. Reading the file a line at a time (you are using std::getline(), yes?) is often a good idea for robustness, but then wrap each line in a std::stringstream object:

std::istringstream parser(line);std::string verb; parser >> verb;if (verb == "sells") {  std::string thing_for_sale;  while (std::getline(parser, thing_for_sale, '.') {    std::cout << "It sells " << thing_for_sale << "\n";  }}
Thanks for your help, Zahlman! But now I have a new problem... I figured out how to read my "items" from the file, but the thing is, I'm declaring this function inside a class (Class CEntity). And I'm inhereting this class to another one (Class CCivilian). The variable I'm using to save my "items" (declared as char* sells[20]) isn't saving as it's inhereted.

My class CCivilain looks like this:


CCivilian : CEntity
public:
Civilian() <-- char* sells[20] is getting inhereted from CEntity to here
CCivilian::show() <-- but not to here. there's somesort of memmory loss and it returns werid #'s


Why is that my sells[20] variable only saves to the constructor Civilian and not to the part CCivilian::show()? I thought it's all the same class, so shouldn't it all inherete? I have other variables working on the whole thing?

I think the problem may lie in how I'm parsing the file.


std::string wholething; //get the whole thing
wholething = line.substr(6, line.size());

const char* str; //convert string to char
str=wholething.c_str();

char *temp = NULL; //convert const char* to char
temp = (char *)str;

int s = 0; //declare some locals
char* found;
found = strtok(temp,";");

//parse it!
while (found != NULL)
{
found = strtok (NULL, ";");
sells = found;<br> printf("sells[%s]\n",sells); //I get what I want here, but not when sell its inhereteted… why?<br> s++;<br><br> if( s &gt; 19 ) //to far!<br> break;<br> }<br><br><br>I can't think of any other way to write this and I've been at it for at least six hours now (i try to solve problems my self before posting). I'm kind of a newb still with c++. This is &#111;nly my sixth app.<br><br>I thought constructors were &#111;nly called &#111;nce? Pointing me in the right direction would be most helpful.<br><br>Another newb question, how do you use code tags?
Use a lexical analyzer for parsing stuff like that. There's one built-into LoseThos good for C/C++ syntax. I didn't pretty-this-up for your situation but my lexical analyzer code is here: http://www.losethos.com/frontend.html. It's a little bit overkill.
Using std::string all the way along, instead of converting to char* (in a hacky way at that), would avoid this bug. Do not store what .c_str() returns, it's only meant for temporary use by C-style functions which expect a C-style string array terminated with a '\0'. Your string goes out of scope and your char pointers get invalidated.
Thank you so much for your replies!

I added a null character ( \0 ) to the end of the string but it’s still the same. The reason I'm doing all the hacky conversions from string to char* is because the strtok command wants a char* and not a std::string.

The pseudocode is as follows:

--get the whole line with substr. this returns a string
--convert string to const char*
--convert const char* to char*
--add null character '\0' (suggested by agi_shi)
--use strtok to find delimiter ';' (The reason we convert string to char* is because strtok doesn’t accept std::string)
--keep results in array char* sells[20]

Using std::cout, I can see before I inherit my class that sells[20] does contain the correct results. After I inherit, I can see from the constructor of my new class that sells[20] still contains the correct results. Its only after I try a function of my inherited class does sells[20] somehow get its results lost (it prints out funky stuff).

Is there an alternative to strtok for strings? Maybe another way to parse for a delimiter? The reason there's all this conversion is just for strtok!

I'm pretty sure now my bug is with this conversion and hacky stuff so if anyone could share some suggestions I'd appreciate them!
That is very hacky, undefined behaviour hacky. What is happening (as agi-shi outlined) is that the string goes out of scope. This causes the memory that you were using to be released.

The correct way to do it would be to use std::stringstream, like Zahlman showed.

First thing is to make sells a vector, which makes the rest easier.
#include <vector>#include <string>class Entity{   // ...   std::vector<std::string> sells;};

Here is an example of how to parse the lines. I think its pretty similar to your algorithm description (although you seem to be switching between a full stop and a semicolon as a delimiter):
void foo(){    std::vector<std::string> sells;    // sample data    std::string line = "sells.foo;bar;etc;yeah;whatever";    std::stringstream stream;    stream << line.substr(6, line.size());    std::string temp;    while(std::getline(stream,temp,';'))    {        sells.push_back(temp);        std::cout << "sells: " << sells.back() << '\n';    }}

Running this outputs:
Quote:
sells: foo
sells: bar
sells: etc
sells: yeah
sells: whatever
Thank you for your code snippet rip-off! Yes, I know my way was very hacky...

Thankfully I've got it working now thanks to rip-off's and Zahlman explanations.
Quote:Original post by scarypajamas
Thank you so much for your replies!

I added a null character ( \0 ) to the end of the string but it’s still the same. The reason I'm doing all the hacky conversions from string to char* is because the strtok command wants a char* and not a std::string.

...

Is there an alternative to strtok for strings? Maybe another way to parse for a delimiter? The reason there's all this conversion is just for strtok!


This is jumping through hoops in order to use an old-fashioned and badly designed tool. You were given suggestions in the replies that you're so "thankful" for to keep using strings (which are likely to solve many other problems for you down the road as well) and *stringstreams for parsing*; why resist that?

Where did you even *hear about* strtok()? It isn't mentioned in your original code nor in anything that was suggested to you.

Zahlman wrote, "...*stringstreams for parsing*; why resist that?"


I don't resit it at all! The thing is, I'm still kind of a newb at c++. I taught myself the language for fun and I haven't had any formal training so I don't know what's better than what. I only have my own experience to rely on so I can use all the professional pointers I can get.


Zahlman wrote,"Where did you even *hear about* strtok()?"

Between posting on this topic, I researched ways to parse for delimiters. I found strtok() while googling for ways to achieve results: http://www.cplusplus.com/reference/clibrary/cstring/strtok.html

As for it not being mentioned in my original code, it’s as I said, I found it while searching between posts. I was still trying to solve the problem. When I posted again (about the third post down) I used strtok() in the sample code.

As for the whole "hacky conversion thing" I was trying to pull... I've done conversions before and had no trouble until now, which is why I posted. I don't mean to sound overly grateful, but I have no one in my family or anyone I know who is into programming who can help me. Programming is kind of "my thing" so I do appreciate your help.

This topic is closed to new replies.

Advertisement