2

I have a string as "1.0.0" and I want to extract the "1", "0", and "0". If the last zero is not present, the string must store 0 by default:

verstr.substr(0,verstr.find(".");

The above statement can find the first digit that is "1", however, I am not able to think of a solution for extracting the remainder of the string.

After this i convert it to a long as:

 va = atol(verstr.substr(0,verstr.find(".")).c_str());

so i want the "1" in va , 0 in "vb" and so on

Thanks.

asked Aug 1, 2012 at 13:08
4
  • 2
    Create the substring that contains everything after the first .. Then find the first . in that substring. Commented Aug 1, 2012 at 13:10
  • you mean verstr.substr(.,verstr.find(".")); Commented Aug 1, 2012 at 13:13
  • 1
    Do you know how to create the substring that contains everything after the first dot? If not, you should really just learn how to use substr and find, starting by reading their documentation. Commented Aug 1, 2012 at 13:17
  • i've used bad algorithm and erase substring, that i already find. but it is bad practise Commented Aug 1, 2012 at 13:18

7 Answers 7

4

C++11 solution:

#include <iostream>
#include <string>
#include <regex>
using namespace std;
int main(int, char **) {
 string version("1.2.3");
 match_results<string::const_iterator> m;
 regex re("([0-9]+)\\.([0-9]+)(\\.([0-9]+))?");
 if (regex_match(version, m, re)) {
 int major = stoi(m[1].str()),
 minor = stoi(m[2].str()),
 rev = stoi(m[4].str().length() == 0 ? 0 : m[4].str());
 cout << "major: " << major << endl;
 cout << "minor: " << minor << endl;
 cout << "rev: " << rev << endl;
 } else {
 cout << "no match\n";
 }
}

The regular expression used is ([0-9]+)\.([0-9]+)(\.([0-9]+))? and breaks down as follows:

[0-9]+ matches one or more digits

\. matches a literal dot.

? following the last expression indicates that it is optional

Expressions wrapped in ( and ) are capture groups. There are five capture groups in this expression:

0 - always matches the entire string - we don't use this.

1 - matches the major version number.

2 - matches the minor version number.

3 - matches a dot followed by the revision number - we don't use this but it is necessary because we use the parentheses followed by a ? to make this whole group optional.

4 - matches the revision number.

answered Aug 1, 2012 at 13:40
Sign up to request clarification or add additional context in comments.

2 Comments

Pastebin style answer. Better teach to fish, not bestow fish.
does work very well, but one issue could u please explain "regex re("([0-9]+)\\.([0-9]+)(\\.([0-9]+))?"); "
3

Not sure if I understand what you need, if you want to retrieve the digits as strings, with a minimum of x digits, you can do something like this.

vector<string> GetVersion(const string &strInput, int iMinSize)
{
 vector<string> vRetValue;
 std::stringstream ss(strInput); 
 string strItem;
 while(std::getline(ss, strItem, '.')) 
 vRetValue.push_back(strItem);
 while(vRetValue.size() < iMinSize)
 vRetValue.push_back("0");
 return vRetValue;
}
int _tmain(int argc, _TCHAR* argv[])
{ 
 vector<string> vRetValue = GetVersion("1.0", 3); 
 return 0;
}
answered Aug 1, 2012 at 13:27

5 Comments

Another pastebin-style answer. People, I beg you, pls explain your stuff.
@phresnel: Do you really think that this function requires instructions ?
Consider that if the questioner had your level of expertise, he wouldn't be here in the first place. E.g., "What is _tmain? Is it better than main()?", "What is getline for, I only have one line?".
Btw, the tone of your last question lets me sense someone fallen to elitism. Sorry if that is a wrong assumption.
@phresnel: I also use stackoverflow, so your first consideration doesn't apply. Secondly the questioner uses std::string so I assume he knows about containers (ex. std::vector) and finnaly about std::getline do you explain std::cout ? I agree with you that people should explain the code, but in this case I really don't think it's necessary.
3

A possibility would to use std::sscanf(). It is simple to use and provides a level of error checking with relatively few lines of code:

#include <iostream>
#include <string>
#include <cstdio>
int main()
{
 std::string input[] = { "1.0.7", "1.0.", "1.0", "1.", "1" };
 for (size_t i = 0; i < sizeof(input)/sizeof(input[0]); i++)
 {
 std::cout << input[i] << ": ";
 // Init to zero.
 int parts[3] = { 0 };
 // sscanf() returns number of assignments made.
 if (std::sscanf(input[i].c_str(),
 "%d.%d.%d",
 &parts[0],
 &parts[1],
 &parts[2]) >= 2)
 {
 // OK, the string contained at least two digits.
 std::cout << parts[0]
 << ","
 << parts[1]
 << ","
 << parts[2]
 << "\n";
 }
 else
 {
 std::cout << "bad format\n";
 }
 }
 return 0;
}

Output:

1.0.7: 1,0,7
1.0.: 1,0,0
1.0: 1,0,0
1.: bad format
1: bad format

See online demo: http://ideone.com/0Ox9b .

answered Aug 1, 2012 at 13:29

2 Comments

This is really good usage of scanf (in comparisons to others solutions). BTW - I added one case to your example (ideone.com/L0ZhD) "111...1111.11111...1111." and it produced unexpected output. Do you think it can be fixed? (anyway +1)
@PiotrNycz, thank you. The numbers provided are resulting in integer overflow. I can't think of a way to fix that. You could use unsigned long instead of int to increase the range but we are dealing with version numbers so the input is fairly unlikely.
2

find and substr are two really nice family of function overloads that are pretty well suited to many simple parsing problems, especially when your syntax checking only needs to be loose.

To extract multiple scalars out of your version vector, store the found index somewhere:

const auto a = verstr.find('.');
const std::string major = verstr.substr(0, a);

Then re-use it with one of the overloads of string::find, saying start searching at one after a:

const auto b = verstr.find ('.', a+1);
const std::string minor = verstr.substr(a+1, b);

And so forth.

If you need a syntax check, compare the returned indices against string::npos:

const auto a = verstr.find('.');
if (std::string::npos == a)
 .... bad syntax ....

Pastebin style version of this answer:

#include <string>
#include <stdexcept>
#include <iostream>
struct Version
{
 std::string Major, Minor, Patch;
 Version(std::string const &Major)
 : Major(Major), Minor("0"), Patch("0")
 {}
 Version(std::string const &Major, std::string const &Minor)
 : Major(Major), Minor(Minor), Patch("0")
 {}
 Version(std::string const &Major, std::string const &Minor, std::string const &Patch)
 : Major(Major), Minor(Minor), Patch(Patch)
 {}
};
std::ostream& operator<< (std::ostream &os, Version const &v)
{
 return os << v.Major << '.' << v.Minor << '.' << v.Patch;
}
Version parse (std::string const &verstr) {
 if (verstr.empty()) throw std::invalid_argument("bad syntax");
 const auto first_dot = verstr.find('.');
 if (first_dot == std::string::npos)
 return Version(verstr);
 const auto second_dot = verstr.find('.', first_dot+1);
 if (second_dot == std::string::npos)
 return Version(verstr.substr(0, first_dot),
 verstr.substr(first_dot+1, second_dot));
 return Version(verstr.substr(0, first_dot),
 verstr.substr(first_dot+1, second_dot),
 verstr.substr(second_dot+1));
}

and then

int main () {
 std::cout << parse("1.0") << '\n'
 << parse("1.0.4+Patches(55,322)") << '\n'
 << parse("1") << '\n';
 parse(""); // expected to throw
}
answered Aug 1, 2012 at 13:53

Comments

0

try something like this instead of solution below the line

string s = "1.0.0";
string delimiters = ".";
size_t current;
size_t next = -1;
do
{
 current = next + 1;
 next = s.find_first_of( delimiters, current );
 string current_substring = s.substr( current, next - current ); // here you have the substring
}
while (next != string::npos);

Ok, please don't use this solution below, if you really don't know what you're doing, according to discussion below this answer with @DavidSchwartz

Take a look at function strtok http://www.cplusplus.com/reference/clibrary/cstring/strtok/

 char str[] = "1.0.0";
 char * pch;
 pch = strtok (str,".");
 while (pch != NULL)
 {
 printf ("%s\n",pch);
 pch = strtok (NULL, ".");
 }
answered Aug 1, 2012 at 13:12

8 Comments

I didn't down-vote, but..seriously? The question is about C++
@KirilKirov and that means, you should use basic C functions? I think this solution is quite elegant, even if it is not C++
I didn't down-vote, but strtok is an awful solution and makes no sense to use with C++ strings. (strtok comes with so many caveats, the only sensible thing is just to not use it.)
@DavidSchwartz ok then, I'm deleting my answer in a while, if you recommend to use something else
I recommend using substr and find. Once you understand those two functions, the implementation is trivial. (find the first dot. substr everything after the first dot. find the first dot in the substr. substr everything before that first dot. substr everything after that first dot.) Though there are even easier ways, this is simple and straightforward.
|
0

Take a look at Boost libraries, specifically String Algo.

Standard library support for string manipulation is somewhat limited in C++. And reinventing the wheel is just plain bad.

Update:

I was asked in comments why I consider all find/substr based solutions bad style. I'll try my best.

As questions does not states otherwise, performance is not a question here. Maintainability and readability are much more important. All solutions proposed here tightly tie split algorithm semantics with a specific version parsing algorithm semantics. This hurts both.

This hurts maintainability, because when you will need to change version format, it will involve changing the very same block of code that implements splitting, making it more error-prone. Same applies to unit-tests.

This hurts readability, because due to mixed semantics I can't at once guess an intent behind this block of code. For example, when I am looking up parse algorithm to check how missing 3d version argument is handled, I'd better not waste my time digging through split implementation details.

If parsing pattern would have been slightly more difficult, I'd have advised regular expressions. But in this case splitting string by a delimiter is an action generic and often used enough to justify having it as a separate function.

answered Aug 1, 2012 at 13:23

7 Comments

but in this case, the standard library has enough machinery.
I have not yet seen one good std-only solution in this thread. I strongly believe that it is a bad approach to code as typical and often used algorithm as split using substr/find and similar. Both from error-prone point of view and readability.
You must refresh this page (F5 or strg+R) so you see several std-only solutions, posted 4 minutes before my comment. find and substr work just fine.
I have seen it and consider it a bad one.
Maybe take the time and explain why. find and substr are often good enough or superior in terms of performance. Just like you don't use std::map<> in every situation on the world, but prefer std::vector or std::deque depending on the circumstances, you should not use just split in every situation. Please expose yourself, your answer does not even state which "String Algo" to use.
|
-1

if it's only simple char comparison in a small string...

char[] should not be so bad... and c functions should work... (EDIT: for some, its a blasphemy... a lot of C++ method use char* whether it's const or not).

why use an object if it's to has the same functionality with more memory to be used, and more time for the process to spend?

EDIT: I saw that some answer suppose to create a lot of string object... i don't khnow if it's really the best way... a little 2 line recursive C like function can do that without gasping a lot. In c++ code I probably would do that with string object, as it's negligible gasp... but just to say it so.

In string object i would use the length property to get the last char first (with [] operator, or appropriate method). then just need to get the two elements (in a loop, or with 2 back reference in an object accepting regex (which is less efficient))

answered Aug 1, 2012 at 13:32

1 Comment

atol is not so good, even if it's in c, in this case stringstream is a easy way to convert.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.