How to compare java version strings in c++? - c++

Let's say I have strings (Java version strings) like this:
"1.8.0_101"
"1.8.0_91"
Is there an easy way to determine which version is higher in c/c++?
I have used strcmp() so far. It compares version strings like "1.7.0" with "1.8.0" correctly, but when underscore character is involved it does not work anymore (because underscore has higher value in ASCII table than all numbers)

Disclaimer: this should be just an example to give you an idea of implementation. Your question lacks various points in order to make it a good question, anyway I'm going to try to help and give you a runnable example. The code could be improved and make it more robust.
struct Version {
int m_major;
int m_minor;
int m_release;
int m_minor_release;
Version() : m_major(-1),
m_minor(-1),
m_release(-1),
m_minor_release(-1) { }
// Convert a str to a version object
explicit Version(const std::string& str_version) : Version() {
size_t offset = 0;
size_t finder;
// Cycle until find a character as '.' '-' '_'
while (finder = str_version.find_first_of(".-_", offset),
finder != std::string::npos) {
int* p = nullptr;
if (m_major == -1) {
p = &m_major;
} else if (m_minor == -1) {
p = &m_minor;
} else if (m_release == -1) {
p = &m_release;
} else {
p = &m_minor_release;
}
*p = std::stoi(str_version.substr(offset, finder - offset));
offset = finder + 1;
}
if (offset != str_version.size() && m_minor_release == -1) {
m_minor_release = std::stoi(
str_version.substr(offset,std::string::npos));
}
}
bool operator<(const Version& oth) const {
// compare this version with another one
}
};
I hope you can find this little example useful for your problem implementation.

Related

C++ Console : Parsing METAR data

I am working on my first web app (weather visualization) that requires some light c++ on the back end. I am using wget to download the raw text, and c++ console to parse the data and it then writes HTML. This works great so far.
METAR is basically raw weather data from a station. (Time, Date, Conditions, Temp etc). The one I am using currently is :
2018/08/10 08:09
KBAZ 100809Z AUTO 00000KT 10SM BKN012 26/23 A3002 RMK AO2 T02610233
I have been able to store each set of data into different variables. The set I am looking at with the issue is the "26/23" above, which is the temperature and dew point in Celsius.
So far I have a string called tempAndDewpoint with "26/23" stored in it... I am using substr(0,2) to return the just temperature in a new string called temperature. (since the first number is temperature). This works great.
My question is, what happens if the temperature is below 10, like 9? I could no longer use substring(0,2) because that would then return "9/" as the current temperature.
I hope to find some guidance with this that is not too complicated for me to duplicate. I wasn't even sure what to name this question as I am not sure what this issue is called. Surely it must be common?
Beware: Negative temperatures in METAR are prefixed with M. So these are valid temp groups: 5/M2 or M8/M12 (negative dew points are in fact icing points). So I would not use a custom parser here:
struct TTD {
short int t;
short int td;
bool parse(const char *tempAndDewpoint) {
const char *next;
t = parse_partial(tempAndDewpoint, &next);
if (*next != '/') return false;
td = parse_partial(next + 1, &next);
return (*next == '\0');
}
private:
static short int parse_partial(const char *beg, const char **next) {
bool neg = false;
short int val = 0;
if (*beg == 'M') {
neg = true;
beg += 1;
}
while (*beg >= '0' && *beg <= '9') {
val = val * 10 + (*beg - '0');
beg += 1;
}
*next = beg;
if (neg) val = -val;
return val;
}
};
The simple solution is to not store as a string at all. Split the string into two independent numbers. As stated in the other answer you do need to take care of "M" being a prefix for negative numbers but there is no read to parse the numbers by hand:
int parseNum(const std::string& str)
{
size_t pos;
int num;
if (!str.empty() && str.front() == 'M')
{
num = -std::stoi(str.substr(1), &pos);
if (pos != str.size() - 1)
{
throw std::invalid_argument("invalid input");
}
}
else
{
num = std::stoi(str, &pos);
if (pos != str.size())
{
throw std::invalid_argument("invalid input");
}
}
return num;
}
size_t slash = tempAndDewpoint.find("/");
if (slash == std::string::npos)
{
throw std::invalid_argument("invalid input");
}
int temp = parseNum(tempAndDewpoint.substr(0, slash));
int dew = parseNum(tempAndDewpoint.substr(slash + 1));

Retrieve each token from a file according to specific criteria

I'm trying to create a lexer for a functional language, one of the methods of which should allow, on each call, to return the next token of a file.
For example :
func main() {
var MyVar : integer = 3+2;
}
So I would like every time the next method is called, the next token in that sequence is returned; in that case, it would look like this :
func
main
(
)
{
var
MyVar
:
integer
=
3
+
2
;
}
Except that the result I get is not what I expected:
func
main(
)
{
var
MyVar
:
integer
=
3+
2
}
Here is my method:
token_t Lexer::next() {
token_t ret;
std::string token_tmp;
bool IsSimpleQuote = false; // check string --> "..."
bool IsDoubleQuote = false; // check char --> '...'
bool IsComment = false; // check comments --> `...`
bool IterWhile = true;
while (IterWhile) {
bool IsInStc = (IsDoubleQuote || IsSimpleQuote || IsComment);
std::ifstream file_tmp(this->CurrentFilename);
if (this->eof) break;
char chr = this->File.get();
char next = file_tmp.seekg(this->CurrentCharIndex + 1).get();
++this->CurrentCharInCurrentLineIndex;
++this->CurrentCharIndex;
{
if (!IsInStc && !IsComment && chr == '`') IsComment = true; else if (!IsInStc && IsComment && chr == '`') { IsComment = false; continue; }
if (IsComment) continue;
if (!IsInStc && chr == '"') IsDoubleQuote = true;
else if (!IsInStc && chr == '\'') IsSimpleQuote = true;
else if (IsDoubleQuote && chr == '"') IsDoubleQuote = false;
else if (IsSimpleQuote && chr == '\'') IsSimpleQuote = false;
}
if (chr == '\n') {
++this->CurrentLineIndex;
this->CurrentCharInCurrentLineIndex = -1;
}
token_tmp += chr;
if (!IsInStc && IsLangDelim(chr)) IterWhile = false;
}
if (token_tmp.size() > 1 && System::Text::EndsWith(token_tmp, ";") || System::Text::EndsWith(token_tmp, " ")) token_tmp.pop_back();
++this->NbrOfTokens;
location_t pos;
pos.char_pos = this->CurrentCharInCurrentLineIndex;
pos.filename = this->CurrentFilename;
pos.line = this->CurrentLineIndex;
SetToken_t(&ret, token_tmp, TokenList::ToToken(token_tmp), pos);
return ret;
}
Here is the function IsLangDelim :
bool IsLangDelim(char chr) {
return (chr == ' ' || chr == '\t' || TokenList::IsSymbol(CharToString(chr)));
}
TokenList is a namespace that contains the list of tokens, as well as some functions (like IsSymbol in this case).
I have already tried other versions of this method, but the result is almost always the same.
Do you have any idea how to improve this method?
The solution for your problem is using a std::regex. Understanding the syntax is, in the beginning, a little bit difficult, but after you understand it, you will always use it.
And, it is designed to find tokens.
The specific critera can be expressed in the regex string.
For your case I will use: std::regex re(R"#((\w+|\d+|[;:\(\)\{\}\+\-\*\/\%\=]))#");
This means:
Look for one or more characters (That is a word)
Look for one or more digits (That is a integer number)
Or look for all kind of meaningful operators (Like '+', '-', '{' and so on)
You can extend the regex for all the other stuff that you are searching. You can also regex a regex result.
Please see example below. That will create your shown output from your provided input.
And, your described task is only one statement in main.
#include <iostream>
#include <string>
#include <algorithm>
#include <regex>
// Our test data (raw string) .
std::string testData(
R"#(func main() {
var MyVar : integer = 3+2;
}
)#");
std::regex re(R"#((\w+|\d+|[;:\(\)\{\}\+\-\*\/\%\=]))#");
int main(void)
{
std::copy(
std::sregex_token_iterator(testData.begin(), testData.end(), re, 1),
std::sregex_token_iterator(),
std::ostream_iterator<std::string>(std::cout, "\n")
);
return 0;
}
You try to parse using single loop, which makes the code very complicated. Instead i suggest something like this:
struct token { ... };
struct lexer {
vector<token> tokens;
string source;
unsigned int pos;
bool parse_ident() {
if (!is_alpha(source[pos])) return false;
auto start = pos;
while(pos < source.size() && is_alnum(source[pos])) ++pos;
tokens.push_back({ token_type::ident, source.substr(start, pos - start) });
return true;
}
bool parse_num() { ... }
bool parse_comment() { ... }
...
bool parse_whitespace() { ... }
void parse() {
while(pos < source.size()) {
if (!parse_comment() && !parse_ident() && !parse_num() && ... && !parse_comment()) {
throw error{ "unexpected character at position " + std::to_string(pos) };
}
}
}
This is standard structure i use, when lexing my files in any scripting language i've written. Lexing is usually greedy, so you don't need to bother with regex (which is effective, but slower, unless some crazy template based implementation). Just define your parse_* functions, make sure they return false, if they didn't parsed a token and make sure they are called in correct order.
Order itself doesn't matter usually, but:
operators needs to be checked from longest to shortest
number in style .123 might be incorrectly recognized as . operator (so you need to make sure, that after . there is no digit.
numbers and identifiers are very lookalike, except that identifiers starts with non-number.

Is there an alternative to using str.substr( ) to extract a substring at a given position?

I am trying to compare two std::strings, and decide if string A is the same as string B, but with the insertion or deletion of a single character.
Otherwise it returns false.
For example: "start" and "strt" or "ad" and "add"
Currently:
if(((sizeA - sizeB) != 1)
&& ((sizeB - sizeA) != 1))
{
return false;
}
if(sizeA < sizeB)
{
for(int i = 0; i < sizeA; ++i)
{
if(stringA[i] != stringB[i])
{
if(stringA.substr(i)
== stringB.substr(i + 1))
{
return true;
}
else return false;
}
}
} //with another loop that runs only if stringA is larger than stringB
This works flawlessly, but gprof tells me that this function is being bogged down.
I tried converting the for loop to use iterators to access the chars, but this doubled my run time.
Ive narrowed it down to my use of std::string.substr( ) because it is constructing new strings each time stringA and stringB differ in size by 1.
When the first character differs, I need a more efficient way to check if I were to delete that character, would the two strings then be equal?
It seems, once it is known whether there is a one character difference the comparison can be done more effective with a single pass over the string: find the location of the difference, skip the character, and see if the tail is the same. To that end it is obviously necessary to know which one is the smaller string but that's trivial to determine:
bool oneCharDiff(std::string const& shorter, std::string const& longer) {
if (shorter.size() + 1u != longer.size() {
return false;
}
typedef std::string::const_iterator const_iterator;
std::pair<const_iterator, const_iterator> p
= std::mismatch(shorter.begin(), shorter.end(), longer.begin());
return std::equal(p.first, shorter.end(), p.second + 1);
}
bool atMostOneCharDiff(std::string const& s0, std::string const& s1) {
if (s0.size() < s1.size()) {
return oneCharDiff(s0, s1);
else if (s1.size() < s0.size()) {
return oneCharDiff(s1, s0);
}
else {
return s0 == s1;
}
}
Try:
if (stringA.compare(i, stringA.npos, stringB, i+1, stringB.npos) == 0) {
/* the strings are equal */
}
In this write-up, that's version (3) of std::basic_string::compare.
If your compiler supports it it may be worth checking out the new ISO/IEC TS 19568:xxxx Technical Specification string_view class.
It provides an immutable view of a string through references without copying the string itself so it promises to be much more efficient when dealing with substrings.
#include <experimental/string_view>
using std::experimental::string_view;
bool func(string_view svA, string_view svB)
{
// ... stuff
if(svA.size() < svB.size())
{
for(int i = 0; i < svA.size(); ++i)
{
if(svA[i] != svB[i])
{
if(svA.substr(i)
== svB.substr(i + 1))
{
return true;
}
else return false;
}
}
}
// ... stuff
return false;
}
As you can see it works pretty much like a drop-in replacement for std::string (or const char* etc...). Simply pass your normal std::string objects as arguments to the function and the string_view parameters will initialize from the passed in strings.

Getting a substring between two tags C/C++

Hello I am creating a parser of sorts in C/C++ it is rather simple i just want to be able to get a string from to tags "(" and ")" using C/C++ i know that the logic is like find first tag and increment a number every single char that is found until the next tag is found. But i suck a logic so if someone could at least give me a function that could help.
Edit:I see that C/C++ string functions are nothing alike so just C++ will do.
You seem unsure of the differences between string handling in C and in C++. Your description seems to imply wanting to do it in a C-style way.
void GetTag(const char *str, char *buffer)
{
buffer[0] = '\0';
char *y = &buffer[0];
const char *x = &str[0];
bool copy = false;
while (x != NULL)
{
if (*x == '(')
copy = true;
else if (*x == ')' && copy)
{
*y = '\0';
break;
}
else if (copy)
{
*y = *x;
y++;
}
++x;
}
}
Alternatively, the C++ way is to use the std::string which is safer because it's not fiddling with pointers, and arguably easier to read and understand.
std::string GetTag(const std::string &str)
{
std::string::size_type start = str.find('(');
if (start != str.npos)
{
std::string::size_type end = str.find(')', start + 1);
if (end != str.npos)
{
++start;
std::string::size_type count = end - start;
return str.substr(start, count);
}
}
return "";
}

Nested for-loop solution

I made this program just out of interest and wanted to make it better. My problem is that I want to make a nested for-loop to carry out the iterations but I can't get my head around it, I have tried many times but my head is melting. Any help would be greatly appreciated. Also for some reason on windows and openSuse (from what I have seen) the program prints out some random characters after the expected output, a solution to this would be a great bonus. Thanks !
Sorry I didn't make it clearer, the point of the code is to be able to theoretically generate every combination of letters from AAAAAAAA to ZZZZZZZZ.
1) No it's not homework
#include <iostream>
using namespace std;
int main()
{
char pass [] = {'A','A','A','A','A','A','A','A'};
while(pass[0] != '[')
{
pass[7]++;
if(pass[7]=='[')
{
pass[6]++;
pass[7] = 'A';
}
if(pass[6] == '[')
{
pass[6] = 'A';
pass[5]++;
}
if(pass[5] == '[')
{
pass[5] = 'A';
pass[4]++;
}
if(pass[4] == '[')
{
pass[4] = 'A';
pass[3]++;
}
if(pass[3] == '[')
{
pass[3] = 'A';
pass[2]++;
}
if(pass[2] == '[')
{
pass[2] = 'A';
pass[1]++;
}
if(pass[1] == '[')
{
pass[1] = 'A';
pass[0]++;
}
cout << pass << endl;
}
return 0;
}
Maybe like this:
const char char_first = 'A';
const char char_last = '[';
const unsigned int passlen = 8;
while (pass[0] != char_last)
{
++pass[passlen - 1];
for (unsigned int i = passlen - 1; i != 0; --i)
{
if (pass[i] == char_last)
{
++pass[i - 1]; // OK, i is always > 0
pass[i] = char_first;
}
}
}
For printing, include <string> and say:
std::cout << std::string(pass, passlen) << std::endl;
I took the liberty of making a few of the magic numbers into constants. If you're ever going to refactor this into a separate function, you'll see the merit of this.
Since (to output it) you use pass as a C string, it should be null terminated. Since it is not, garbage is printed. So you could define it as:
char pass [] = {'A','A','A','A','A','A','A','A','\0'};
or simpler
char pass[] = "AAAAAAAAA";
I'd forget about carrying on my own and just convert to/from numbers. What you're doing here is basically printing a numbers whose digits range from 'A' to ']', mappable to 0-28 via the magic of ASCII (why no ^ in passwords?)
Printing the number of anything then really boils down to
#include <iostream>
#include <cmath>
using namespace std;
std::string format(long num, int ndigits) {
if(ndigits == 0) {
return "";
} else {
char digit = 'A' + num % 28;
return format(num / 28, ndigits - 1) + digit;
}
}
int main()
{
for(int i = 0 ; i < powl(28,8) ; ++i) {
cout << format(i, 8) << endl;
}
}
You may still want to work in a char array instead of producing a billion temporary strings if you're serious about the loop, but the principle stays the same.
First try to find the common parts in the expressions looking like
if(pass[7]=='[')
{
pass[6]++;
pass[7] = 'A';
}
You should think along a line like "There's always the same number here, and a one-lower number there". Then, you replace that notion of a number with a variable and find out which range the variable has. KerrekSB gave you a solution, try to arrive at similar code from your own reasoning.
You just have to play a bit with your while and make it fit a for-loop.
while(pass[0] != '[') becomes for (i=0; pass[0] != '['; i++)
then you can replace all ifs with only one:
if(pass[i+1] == '[')
{
pass[i+1] = 'A';
pass[i]++;
}
How did we come to that conclusion? Well if you check all your if-statements all that changes between them is the indices. You can see clearly that pattern so you just replace the indices with a variable.
For starters, this is definitely not a case for a nested loop. In fact,
your entire code boils down to:
pass = initialPattern();
while ( isValidPattern( pass ) ) {
nextPattern( pass );
std::cout << pass << std::endl;
}
(But I wonder if you don't really mean to do the output before the
increment.)
Now all you have to do is define the type of pass and relevant
functions; you might even consider
putting everything in a class, since all of the functions operate on the
same data instance.
Judging from your code, pass should be an std::string with 8
characters; the initialization could be written:
std::string pass( 8, 'A' );
isValidPattern apparently only looks at the first character. (I'm not
sure that's correct, but that's what your code does.) Something like:
bool
isValidPattern( std::string const& pattern )
{
return pattern[0] != '[';
}
according to your code, but something like:
struct NotIsUpper
{
bool operator()( char ch ) const
{
return ! ::isupper( static_cast<unsigned char>( ch ) );
}
};
bool
isValidPattern( std::string const& pattern )
{
return pattern.size() == 8
&& std::find_if( pattern.begin(), pattern.end(), NotIsUpper() )
== pattern.end();
}
would seem more appropriate. (Of course, if you're doing any sort of
coding with text, you'd already have NotIsUpper and its siblings in
your tool kit.)
Finally, nextPattern seems to be nothing more than a multi-digit
increment, where the data is stored in big-endian order. So the
following (classical) algorithm would seem appropriate:
void
nextPattern( std::string& pattern )
{
static char const firstDigit = 'A';
static char const lastDigit = 'Z';
static std::string const invalidPattern( 1, '[' );
std::string::reverse_iterator current = pattern.rbegin();
std::string::reverse_iterator end = pattern.rend();
while ( current != end && *current == lastDigit ) {
*current = firstDigit;
++ current;
}
if ( current != end ) {
++ *current;
} else {
pattern = invalidPattern;
}
}
Formally, there is no guarantee in the standard that the letters will
be encoded in sequential ascending order, so for maximum portability,
you probably should in fact use an std::vector<int> with values in the
range [0, 26), and map those to letters just befor output. This
would be trivial if you put all of these operations in a class, since
the internal representation wouldn't be visible to the client code.
Something like:
class PatternGenerator
{
std::vector<int> myData;
public:
explicit PatternGenerator()
: myData( 8, 0 )
{
}
void next()
{
static int const lastDigit = 26;
std::vector<int>::reverse_iterator current = pattern.rbegin();
std::vector<int>::reverse_iterator end = pattern.rend();
while ( current != end && *current == lastDigit - 1 ) {
*current = 0;
++ current;
}
if ( current != end ) {
++ *current;
} else {
myData.front() = lastDigit;
}
}
bool isValid() const
{
return myData.front() < lastDigit;
}
friend std::ostream& operator<<(
std::ostream& dest, PatternGenerator const& obj )
{
static char const characterMap[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ( std::vector<int>::iterator current = obj.myData.current();
current != obj.myData.end():
++ current ) {
dest << characterMap[*current];
}
return dest;
}
};
(Note that things like isValid become simpler, because they can depend on the class invariants.)
Given this, all you have to write is:
int
main()
{
PatternGenerator pass;
while ( pass.isValid() ) {
std::cout << pass << std::endl;
pass.next();
}
return 0;
}
To do nested loops, you need to turn it inside-out.
You've written the code thinking as follows: go through all the possibilities for the last symbol, then change the second-last once and go back, etc. That's like counting up from 1, getting to 10 and putting a 1 in the tens column, etc.
Nested loops work the other way: go through the possibilities for the first symbol, allowing the inner loops to take care of possibilities for the other symbols each time. i.e., "list all those numbers, in order, that start with 0 in the millions place, then the ones that start with 1 etc.". In the outermost loop, you just set that value for the first digit, and the nested loops take care of the rest.