Skip to main content
gamedev.net
🔒 Locked

Problem with sorting out strings

Started by qwertyuiop23 Jan 19, 2008 at 8:08 PM 16 replies 1.9k views
Original Post
qwertyuiop23
qwertyuiop23
Hi, I am trying to create a function where a string is passed to the function then the function splits that string into variables uses then variables then gets the results from doing stuff to the variables then put them back into a string the return them. eg. I pass the string 1.2.3.4 to the function then it splits the string up at the position of the "." and thens sets it to one long variable containg only numbers but also the array of it contains each separate number then this is where i am getting stuck i then try to convert the string array containg one number eaci to a real value using istringstream but it does not work. This is the code i have so far. All of it works except the bit with istringstream. Ignore system("Pause") in there so it does close straight away. #include #include #include using namespace std; int main(int argc, char** argv[]) { std::string str; std::string integers; cout<<"Enter the string"<>str; for(int i=0; i)) { integers.push_back(str); } } int argument[strlen(integers.data())]; for(int j=0; j>argument[j]; } int result; result=argument[2]*argument[3]; cout<[Edited by - qwertyuiop23 on January 19, 2008 9:23:40 PM]
Zakwayda
Zakwayda
1. Is this homework?

2. This probably belongs in the For Beginners forum.

3. Be sure to use [source] tags when posting code.

4. I'm not sure, but I don't think string::data() is part of the standard; if that is the case, I'd recommend avoiding its use (in the interest of portability).

5. C++ string objects know their own length, which can be queried via the size() or length() functions. There's no need to use strlen() when working with string objects (generally speaking).
qwertyuiop23
qwertyuiop23
1. Nope it isn't. Why?

2. Probably not. That forum is for Game Development beginners in Game Development should go there. This has nothing to do witha game . :)

3. Fixed. :)

4. What do you mean?

5. Ohk will do that. Could that fix my problem though?
Sc4Freak
Sc4Freak
Quote:
Original post by jyk
1. Is this homework?

2. This probably belongs in the For Beginners forum.

3. Be sure to use source tags when posting code.

4. I'm not sure, but I don't think string::data() is part of the standard; if that is the case, I'd recommend avoiding its use (in the interest of portability).

5. C++ string objects know their own length, which can be queried via the size() or length() functions. There's no need to use strlen() when working with string objects (generally speaking).

data() is part of the string class. It returns a const pointer to the raw char data. But it does not guarantee a null-terminated, c-style string. That means that using strlen() on the data() is not guaranteed to work, since std::string isn't required to internally contain a c-style string. The string::c_str() will convert the string to use a c-style string, then return a const pointer to it.

Although, yes, you should just use string::length(). Also, try to use some more sentences :P.

Anyway, I'm not seeing how this would even compile. You can't create a static array with a size that is specified at runtime. For a dynamically sized array, you need to use dynamic allocation (or, better yet, a std::vector).
qwertyuiop23
qwertyuiop23
Um i know the code i posted does not compile because of an erorr the compiler throws about this part here:

std::istringstream strin(integers[j]);

this code however does compile BUT it does not store the string values into real values.
#include
#include
#include

using namespace std;

int main(int argc, char** argv[])
{
std::string str;
std::string integers;

cout<<"Enter the string"< cin>>str;

for(int i=0; i {
if(isdigit(str))
{
integers.push_back(str);
}
}

for(int k=0; k {
cout<<"Argument["< }


system("PAUSE");
return 0;
}

Thanks
Qwertyuiop23

PS: Took out source tags. It made the code go all funny.

Gage64
Gage64
First, this is the general usage for std::istringstream:

std::istringstream is("3 4.5");int i;double d;is >> i >> d;    // Properly ignores white-space


In your first code example, you are passing a char as the constructor parameter, but you should pass an std::string.

Second, your method of extracting the numbers only works with single digits. What if the string looks like this: "12.3617"?

If you want to do the parsing yourself, you should learn more about std::string's capabilities. In particular, have a look at the substr() and find_first_of() member functions. Also, like others have stated, if you want to get the string's length, don't do this:

strlen(str.data());


Do this:

str.length();or str.size();
Sc4Freak
Sc4Freak
Quote:
Original post by qwertyuiop23
Um i know the code i posted does not compile because of an erorr the compiler throws about this part here:

std::istringstream strin(integers[j]);

Then next time, how about you tell us what the problem is? "It does not work" is incredibly vague. In the future, you need to tell us what's wrong, so that we can help you.

So, let's try again. You code doesn't compile, correct? What exactly is the compiler error?
Zakwayda
Zakwayda
Quote:
Original post by Sc4Freak
data() is part of the string class.
Sorry, you're right. I must have been thinking of something else.
qwertyuiop23
qwertyuiop23
Ok i took on board what all you guys hvae helpfully posted AND rewriten my code. It has done away with all the things that i shouldn't do and put in somethings that guys have suggested. The code:
[source="cpp"]#include <string>#include <sstream>#include <iostream>#include <stdexcept> using namespace std;double real(const std::string& s){   std::istringstream i(s);   double x;   i >> x;   return x;}double string_count(std::string arg0, std::string arg1){       int count , k;       std::string str;       str=arg1;       count=0;       for(int xx=0;xx<str.length();xx+=1)       {        if ((str.at(xx))=arg0)        {         count+=1;//Line 26        }       }       return count+1;}int main(int argc, char** argv[]){    std::string str;    std::string sstring;    std::string integers;    int arr,len,ind,pos;    std:string sep;    std::string dat;    char fname[1024];    sep = "|";    dat = str + sep;    len = sep.length();    ind = 0;    for(int yy=0;yy<(string_count(sep,dat));yy+=1)     {    pos = dat.find(sep);    dat.copy(fname,pos,0);    fname[pos]=0;    arr[ind]=real(fname);    dat = dat.erase(1,pos+len);    ind += 1;//Line 53    }    system("PAUSE");    return 0;}



However it throws two compiler erorrs and in doing so will not compile:

Errors:
Line 26; In function double string_count(std::string, std::string)': cannot convert std::string' to char' in assignment
Line 53; In function
int main(int, char***)': invalid types `int[int]' for array subscript

Cheers
~Qwertyuiop23
Zahlman
Zahlman
Quote:
Original post by qwertyuiop23
Errors:
Line 26; In function double string_count(std::string, std::string)': cannot convert std::string' to char' in assignment
Line 53; In function
int main(int, char***)': invalid types `int[int]' for array subscript

Cheers
~Qwertyuiop23


I'll give you a hint: for whatever reason, lines 26 and 53 are actually each 2 lines further up in the code than you have indicated.
Zakwayda
Zakwayda
A few more tips:

1. The = and == operators do different things (generally speaking), so be careful not to mistakenly substitute one for the other (a common error).

2. When comparing objects, keep the types of the two objects in mind. Can they be compared? Is operator == defined for them? Don't expect that you can compare objects of different types just because it makes sense to you to do so.

3. In earlier versions of the C standard it was required that all variables be declared at the beginning of the function (IINM). In C++ (and in later versions of the C standard), this is neither required nor recommended. Declare the variables as near first use as possible, and declare and initialize them at the same time where possible.

4. If you find yourself using a raw character array, consider whether you could use a std::string object instead.

5. Use meaningful variable names, and spell them out fully.

6. You can't 'index into' an int with an int (I doubt this is actually what you intended).

Thank you for indicating on which lines the errors occurred! That makes it much easier for us :) (Although I think your numbers may be off a couple of lines...)

There are some other issues in your code, but the above should at least move you toward getting it to compile correctly.
qwertyuiop23
qwertyuiop23
1.What is the difference between = and == operators?

2. So maybe you can't compare char and a string?

3. Ok i see the logic in that. Thanks

4. In some parts i am sure that i have to use an array to set the values to after i have converted them to double. What other places are you talking about?

5. Ok lol yeah probably should do that since it is easier for guys to help. Will do that

6. I am pretty sure that is what the debugger told me. What does it mean?

Yeah i checked the lines and they were right for me. I don't know what is up with it. Will change the variable names now and change the places they are declared in.

Cheers
~Qwertyuiop23

ok code cleaned up:
[source=cpp]#include <string>#include <sstream>#include <iostream>#include <stdexcept> using namespace std;double real(const std::string& s){   std::istringstream i(s);   double x;   i >> x;   return x;}double string_count(std::string data_substring, std::string data_string){       int count;       number_occurences=0;       for(int xx=0;xx<data_string.length()+1;xx+=1)       {        if ((data_string.at(xx))=data_substring)        {         number_occurences+=1;        }       }       return number_occurences;}int main(int argc, char** argv[]){    std::string string_data("3.41|2.87|5.23");    int data_array,string_length,array_length,position;    std:string separator;    std::string data_string;    char number_as_string[1024];    separator = "|";    data_string = string_data + separator;    string_length = separator.length();    array_length = 0;    for(int yy=0;yy<(string_count(separator,data_string));yy+=1)     {    position=data_string.find(sep);    data_string.copy(number_as_string,position,0);    number_as_string[position]=0;    data_array[ind]=real(number_as_string);    data_string = data_string.erase(1,pos+len);    array_length += 1;    }    system("PAUSE");    return 0;}


[Edited by - qwertyuiop23 on January 22, 2008 6:05:56 PM]
Driv3MeFar
Driv3MeFar
Quote:
Original post by qwertyuiop23
1.What is the difference between = and == operators?


operator= is for assignment, operator== is for comparing values.

Quote:
Original post by qwertyuiop23
2. So maybe you can't compare char and a string?


Correct. A char is not a string, and a string is not a char.
Quote:
Original post by qwertyuiop23
6. I am pretty sure that is what the debugger told me. What does it mean?


data_array is of type int. That is, it's just an integer. You can't index into an integer. You want an array of integers, or even better a std::vector.
qwertyuiop23
qwertyuiop23
Can you explain in a bit more detail what you mean by array of integers and index into an integer. I though i was doing it correctly??

i though i could do this?:
int a=2;int b[a]={1,2};


Cheers
~Qwertyuiop23
Sc4Freak
Sc4Freak
Quote:
Original post by qwertyuiop23
Can you explain in a bit more detail what you mean by array of integers and index into an integer. I though i was doing it correctly??

i though i could do this?:
*** Source Snippet Removed ***

Cheers
~Qwertyuiop23

No, that's not valid. A static array must have its size known at compile time, which means a non-const int like that is not valid. Make the variable 'a' a const int, and it should work.

In any case, that's not the problem with your code. You create an integer, but try to access it like an array.

Here:
int data_array,string_length,array_length,position;

Then here:
data_array[ind]=real(number_as_string);


You declare 'data_array' as an integer, but try to index it with the variable 'ind' (which, by the way, is not declared).
qwertyuiop23
qwertyuiop23
Haha yes i realised when you pointecd it out that ind is not initialised. It used to be until i changed all the variable names. It should be array_length. So how what i do that?

Cheers
~Qwertyuiop23
Zahlman
Zahlman
Let's look at the string_count function, for an example.

Step 1: line things up neatly (and space them out neatly), and fix the compiler errors

- We want a comparison, so we should be using ==.
- We'll still have an error, because we're trying to compare a character of 'str' to the entire string 'arg0'. What we want to do is count instances of a particular character within the string (I assume), so we should be passing a character to look for, rather than a string to look for.

double string_count(char arg0, std::string arg1) {  int count, k;  std::string str;  str = arg1;  count = 0;  for (int xx = 0; xx < str.length(); xx += 1) {    if ((str.at(xx)) == arg0) {      count += 1;    }  }  return count + 1;}


Step 2: When we declare variables, we should initialize them whenever possible. What you are doing right now is not what I mean by initialization; it is only assignment. Instead, you can set the initial value (hence initialization) directly in the declaration. (Part of the reason you're allowed to hold off on declarations is so that you can wait until you have enough information to initialize with.)

Also, 'k' is not used, so let's just get rid of that.

double string_count(char arg0, std::string arg1) {  int count = 0;  std::string str = arg1;  for (int xx = 0; xx < str.length(); xx += 1) {    if ((str.at(xx)) == arg0) {      count += 1;    }  }  return count + 1;}


Step 3: There's no reason to copy the string, since we're not going to change it. We could use 'arg1' directly. Similarly, there's no reason to implicitly copy the string, by passing it by value. Instead, we'll pass a const reference (just like the real() function does). In general, we prefer passing by const reference for object types, and by value for primitive types.

While we're at it, let's give more meaningful names to our variables. There's no reason you should type something cryptic and challenge yourself to remember stuff. Notice that descriptive names still don't have to be exceptionally long...

double number_of_instances(char of, const std::string& in) {  int count = 0;  for (int xx = 0; xx < in.length(); xx += 1) {    if ((in.at(xx)) == of) {      count += 1;    }  }  return count + 1;}


Step 4: Fixing the semantics. Adding 1 to our count at the end doesn't make any sense for the current name; reading further in your code, I see how the function is intended to be used, which affects the name again. (Hey, these things happen). We can still clean that up, though: just initialize to 1 instead of 0. But anyway, there is no reason we would want to return a double: the number naturally represents an integral quantity, so use int. You can't have three and a half substrings of a string. Our internal calculation uses an int, and the result will be compared to an int, so just use int. :)

int number_of_substrings(char separator, const std::string& data) {  int result = 1;  for (int xx = 0; xx < data.length(); xx += 1) {    if ((data.at(xx)) == separator) {      result += 1;    }  }  return result;}


Step 5: Don't reinvent the wheel. The standard library already knows how to "count elements of a container that match a prototype". Note that strings are a kind of container: they store the individual characters, in a specific order.

We will use the algorithm 'std::count' from the header ''. I'm going to do this somewhat dumbly first, so that you can see what gets replaced by the algorithm.

#include <algorithm>int number_of_substrings(char separator, const std::string& data) {  int result = 1;  result += std::count(data.begin(), data.end(), separator);  return result;}


Step 6: Just simplify that expression...

#include <algorithm>int number_of_substrings(char separator, const std::string& data) {  return std::count(data.begin(), data.end(), separator) + 1;}





Step 7: Make the rest of the code compile, using the new function.

Steps 8...N: Apply similar cleanups to the rest of the code. Sometimes you'll have to go back and redo things you thought you'd fixed properly. This is the nature of the craft of programming. In particular, notice that there's no reason to use a char buffer to copy substrings into: std::string supports a lot of ways to create substrings that are actual std::string instances.

Steps N+1...?: Find out that you don't actually have to count how many substrings there will be before you start splitting. Use a standard library container such as std::vector; they will resize as needed if you just .push_back() each new substring in turn, and keep track of the element count by themselves. You know, just like how std::strings can grow to whatever length is needed, and know how long they are.
qwertyuiop23
qwertyuiop23
Ok i have read what you have posted AND understood it (i think).

I did what you said and made the first argument in the real function a char and the second a const string. However separator is set as a string in the first place so it has to be converted to a char. Which as you know cannot work. It is a string because other things that NEED to be a string are partialy built from it. Like the variable data_string. And it also uses the string function string.length() to get the length of the separator so for these to work the separator needs to be a string.

Is what I am saying correct?

Cheers
~Qwertyuiop23

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.