I am given two strings which contain a floating point number. I need to compare them. Can I directly compare the strings using std::string::compare and will this always give correct results? My current approach is to convert the string to float using std::stof, however I would prefer to avoid C++11 library functions.
simply comparing strings won't help you in cases like
a = "0.43"
b = "0.4300"
if you need to compare first parse them into float and then compare them
std::string s1 = "0.6"
std::wstring s2 = "0.7"
float d1 = std::stof(s1);
float d2 = std::stof(s2);
and then compare them
here is a full program
#include <iostream> // std::cout
#include <string> // std::string, std::stof
int main ()
{
std::string s1 = "0.6"
std::wstring s2 = "0.7"
float d1 = std::stof(s1);
float d2 = std::stof(s2);
if(d1 == d2)
std::cout << "Equals!";
else
std::cout << "Not Equals!";
return 0;
}
click here for more reading on stof
What about writing some ugly codes? It may not be good practice but ...
int compare (const string &str1, const string &str2) {
string *s1 = &str1, *s2 = &str2;
int isReverse = 1;
int len1, len2;
if (str1.length() > str2.length()) {
s1 = &str2;
s2 = &str1;
isReverse = -1;
}
len1 = s1->length();
len2 = s2->length();
if (!len1) {
if (!len2))
return 0;
else if ((*s2)[0] != '-')
return 1*isReverse;
return -1*isReverse;
}
int i = 0;
while(i < len1) {
if ((*s1)[i] > (*s2)[i])
return 1*isReverse;
else if ((*s1)[i] < (*s2)[i])
return -1*isReverse;
i++;
}
while (i < len2) {
if ((*s2)[i] != '0')
return -1*isReverse;
i++;
}
return 0;
}
Related
doing some exrecises for upcoming test. a bit stuck in this one.
"Write a program that asks the user for two strings and checks and prints a message if the second string is contained cyclic in the first string. The cyclic containment means that either the second string appears normally within the first string or the second string appears so that its prefix appears at the end of the first string and the continuation appears at the beginning of the first string".
You can assume that the strings contain only lowercase letters.
String functions are only allowed are : strlen, strcpy, strcmp, strcat
for example:
String A: itisaniceday
String B: sanic
Is a regular occurrence
String A: itisaniceday
String B: dayit
It's a cyclic occurence.
what I did so far:
#include <iostream>
#include <string.h>
using namespace std;
#define Max 128
int isCyclic(char* str1, char* str2);
int main()
{
char* str1 = new char[Max];
char* str2 = new char[Max];
cout << "Please enter two strings:" << endl;
cin >> str1 >> str2;
cout << isCyclic(str1, str2) << endl;
delete[] str1;
delete[] str2;
}
int isCyclic(char* str1, char* str2)
{
int s1 = strlen(str1);
int s2 = strlen(str2);
if (s1!=s2) // if string size is diffrent - they are not contained cyclic
{
return 0;
}
}
You will need two loops, first one over string 1 which is our starting point in string 1 for comparison and second one over string 2 which will be matched to string 1 in a cyclic way. If we reach the end of string 1 and still some characters are left in string 2 then cycle through string 1 starting from index 0.
#include <stdio.h>
#include <iostream>
#include <string.h>
// Should be avoided in general. Use scope resolution instead.
using namespace std;
char* isCyclic(char* s1, char* s2){
int s1_size = strlen(s1);
int s2_size = strlen(s2);
// s1 must contain s2
if(s2_size > s1_size)
return "No Occurence";
for(int i = 0; i < s1_size; i++){
int current = i;
// Boolean to track if we are currently cycling through s1
bool inCycle = false;
int j = 0;
for(; j < s2_size; j++, current++){
// character wise comparision
if(s2[j] != s1[current])
break;
if(! inCycle){
// start from first. Note that we are setting current = -1.
// as we will be incrementing it in the for loop.
if(current == s1_size - 1 && j < s2_size - 1){
current = -1;
inCycle = true;
}
}
}
if(j == s2_size){
if(inCycle)
return "cyclic";
else
return "regular";
}
}
return "No Occurence";
}
int main()
{
printf("Hello World\n");
char* s1 = "itisaniceday";
char* s2 = "dayitis";
cout<<"Occurence Type: "<<isCyclic(s1, s2)<<endl;
return 0;
}
Here is a solution for the second part of the problem (cyclic part). I simply go through all of the characters in the first string and checked if they are the beginning of a cyclic appearance of the second string.
To check that I used % (the modolu operation) if you don't know what it dose then you really need to learn it now.
Also I used bool instead of int because numbers are confusing (and cursed).
#include <iostream>
#include <string.h>
using namespace std;
#define Max 128
bool isCyclic(char* str1, char* str2);
bool isCyclic(char* str1, char* str2,int start);
int main()
{
char* str1 = new char[Max];
char* str2 = new char[Max];
cout << "Please enter two strings:" << endl;
cin >> str1 >> str2;
cout << isCyclic(str1, str2) << endl;
delete[] str1;
delete[] str2;
}
bool isCyclic(char* str1, char* str2) {
for(int i = 0; i < strlen(str1); i++) {
if(str1[i] == str2[0] && isCyclic(str1,str2,i)) {
return true;
}
}
return false;
}
bool isCyclic(char* str1, char* str2,int start)
{
int containingStrLen = strlen(str1);
for(int i = 0; i < strlen(str2); i++) {
if(str1[(start + i)%containingStrLen] != str2[i]) {
return false;
}
}
return true;
}
There are some things missing in this code still:
1) The first part of the problem (it can easily be derived from this code).
2) Some size validation such as making sure that str1 is bigger then str2 before using is cyclic. And that the strings are smaller then Max (I assume).
3) A proper result print.
Good luck in your exam :)
There's a simple trick : if you duplicate the string's prefix at its own end, the problem becomes a straight substring search as a cyclic match would be recomposed at the end. It also handles the corner case where the substring loops back on itself, such as "looploop" inside of "loop".
So here's how you'd do it in broken C-ish dialect:
bool containsCyclic(char const *string, char const *substring) {
std::size_t const stringLen = std::strlen(string);
std::size_t const substringLen = std::strlen(substring);
// Too long a substring wouldn't fit in the string
if(substringLen > 2 * stringLen)
return false;
// Concatenate `string` with its own substring-long prefix
char *const loopedString = new char[stringLen + substringLen + 1];
std::strcpy(loopedString, string);
{ // Partial reimplementation of std::strncpy(loopedString, string, substringLen)
char const *src = string;
char *dest = loopedString + stringLen;
for(std::size_t count = 0; count < substringLen; ++count)
*dest++ = *src++;
*dest = '\0';
}
{ // Partial and naïve reimplementation of std::strstr(loopedString, substring)
for(char const *start = loopedString; start < loopedString + stringLen; ++start) {
// Check if substring is present at this offset
char const *s1 = start;
char const *s2 = substring;
while(*s2 != '\0' && *s1 == *s2)
++s1, ++s2;
if(*s2 == '\0') {
// We found a complete match of substring inside loopedString
delete[] loopedString;
return true;
}
}
}
// No match found
delete[] loopedString;
return false;
}
And just for kicks, here it is in C++:
bool containsCyclicCpp(std::string const &string, std::string const &substring) {
std::string const loopedString = string + string.substr(0, substring.size());
return loopedString.find(substring) != std::string::npos;
}
See it live on Coliru (with tests!)
I have a directory containing files {"good_6", good_7", "good_8"...,"good_660"}, after reading it using readdir and storing in a vector I get {"good_10", "good_100", "good_101", "good_102"...}.
What I want to do is to keep the file names as {"good_6", good_7", "good_8"...,"good_660"} in the vector and then replacing first name with 1, second with 2 and so on... such that good_6 will be 1, good_7 will be 2 and so on. but now good_10 corresponds to 1 and good_100 to 2 and so on.
I tried std::sort on vector but the values are already sorted, just not in a way that I desire (based on integer after _). Even if I just get the last integer and sort on that, it will still be sorted as 1, 100, 101...
Any help would be appreciated. Thanks.
You can use a custom function that compares strings with a special case for digits:
#include <ctype.h>
int natural_string_cmp(const char *sa, const char *sb) {
for (;;) {
int a = (unsigned char)*sa++;
int b = (unsigned char)*sb++;
/* simplistic version with overflow issues */
if (isdigit(a) && isdigit(b)) {
const char *sa1 = sa - 1;
const char *sb1 = sb - 1;
unsigned long na = strtoul(sa1, (char **)&sa, 10);
unsigned long nb = strtoul(sb1, (char **)&sb, 10);
if (na == nb) {
if ((sa - sa1) == (sb - sb1)) {
/* XXX should check for '.' */
continue;
} else {
/* Perform regular strcmp to handle 0 :: 00 */
return strcmp(sa1, sb1);
}
} else {
return (na < nb) ? -1 : +1;
}
} else {
if (a == b) {
if (a != '\0')
continue;
else
return 0;
} else {
return (a < b) ? -1 : 1;
}
}
}
}
Depending on your sorting algorithm, you may need to wrap it with an extra level of indirection:
int natural_string_cmp_ind(const void *p1, const void *p2) {
return natural_string_cmp(*(const char * const *)p1, *(const char * const *)p2);
}
char *array[size];
... // array is initialized with filenames
qsort(array, size, sizeof(*array), natural_string_cmp_ind);
I think you can play around with your data structure. For example instead of vector<string>, you can convert your data to vector< pair<int, string> >. Then {"good_6", "good_7", "good_8"...,"good_660"} should be {(6, "good"), (7, "good"), (7, "good")..., (660, "good")}. In the end, you convert it back and do whatever you want.
Another way is just to define your own comparator to do the exact comparison as what you want.
You can use string::replace to replace string "good_" with empty string, and use stoi to convert the rest of the integral part of the string. Lets say the value obtained is x.
Create std::map and populate it in this way myMap[x] = vec_element.
Then you can traverse from m.begin() till m.end() to find sorted order.
Code:
myMap[ stoi( vec[i].replace(0,5,"") )] = vec[i];
for( MapType::iterator it = myMap.begin(); it != myMap.end(); ++it ) {
sortedVec.push_back( it->second );
If I understand your question, you're just having trouble with the sorting and not how you plan to change the names after you sort.
Something like this might work for you:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <tuple>
#include <string.h>
int main()
{
std::vector<std::string> v;
char buffer[64] = {};
for (size_t i = 1; i < 10; ++i)
{
sprintf(buffer, "good_%d", i * 3);
v.push_back(buffer);
sprintf(buffer, "bad_%d", i * 2);
v.push_back(buffer);
}
std::random_shuffle(v.begin(), v.end());
for (const auto& s : v)
{
std::cout << s << "\n";
}
std::sort(v.begin(), v.end(),
[](const std::string& lhs, const std::string& rhs)
{
//This assumes a lot about the contents of the strings
//and has no error checking just to keep things short.
size_t l_pos = lhs.find('_');
size_t r_pos = rhs.find('_');
std::string l_str = lhs.substr(0, l_pos);
std::string r_str = rhs.substr(0, r_pos);
int l_num = std::stoi(lhs.substr(l_pos + 1));
int r_num = std::stoi(rhs.substr(r_pos + 1));
return std::tie(l_str, l_num) < std::tie(r_str, r_num);
});
std::cout << "-----\n";
for (const auto& s : v)
{
std::cout << s << "\n";
}
return 0;
}
Managed to do it with the following compare function:
bool numericStringComapre(const std::string& s1, const std::string& s2)
{
size_t foundUnderScore = s1.find_last_of("_");
size_t foundDot = s1.find_last_of(".");
string s11 = s1.substr(foundUnderScore+1, foundDot - foundUnderScore - 1);
foundUnderScore = s2.find_last_of("_");
foundDot = s2.find_last_of(".");
string s22 = s2.substr(foundUnderScore+1, foundDot-foundUnderScore - 1);
int i1 = stoi(s11);
int i2 = stoi(s22);
if (i1 < i2) return true;
return false;
}
full file name was good_0.png, hence that find_last_of(".").
I have dynamic string's array:
string *acc = new string[2];
string some_string;
I would like to add two strings to this array, but there is problem with reference ( i think).
I have following example (very ugly, but it shows the problem):
for ( int i = 0; i < 2; i ++ ) {
if ( i == 0 )
some_string = "ab";
else
some_string = "cd";
acc[i] = some_string;
some_string = "";
}
return acc
Of course this code is without any sense, but my code is more complicated and it would hide the problem.
The point is that code is returning cd instead of abcd. In my opinion some_string = ""; make mess here. Am I right?
Is it possible to do it keeping logic of code?
I really don't think you should write code this way. And you don't need temporary some_string - assigning strings is well-defined and do exactly what it should. In thi case, use vector - it is much safer approach:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> ac;
for(int i = 0; i < 2; ++i)
{
if(i == 0)
ac.push_back("ab");
else
ac.push_back("cd");
}
cout<<ac[0]<<ac[1]<<endl;
return 0;
}
Working sample: here.
It is not clear why you expect "abcd" as result.
You assign some_string the value "cd" instead of adding to "ab".
Maybe this is what you are trying:
string* func()
{
string *acc = new string[2];
string some_string;
for ( int i = 0; i < 2; i ++ )
{
if ( i == 0 )
some_string = "ab"; // "ab"
else
some_string = some_string + "cd"; // "ab" + "cd" -> "abcd"
acc[i] = some_string;
//some_string = "";
}
return acc;
}
int main(int argc, char** argv)
{
string* y = func();
cout << y[0] << endl; // First element "ab"
cout << y[1] << endl; // Second element "abcd"
// Other stuff
// ....
// Remember to delete y
return(0);
}
Output:
ab
abcd
But as others had said.... Use vector instead.
You could also use std::array like this:
array<string, 2> func2()
{
array<string, 2> res;
string some_string;
for ( int i = 0; i < 2; i ++ )
{
if ( i == 0 )
some_string = "ab";
else
some_string = some_string + "cd";
res[i] = some_string;
}
// Or just this instead of a for loop
// res[0] = "ab";
// res[1] = "abcd";
return res;
}
int main(int argc, char** argv)
{
array<string, 2> z = func2();
cout << z[0] << endl;
cout << z[1] << endl;
return(0);
}
No, you simply made a bug:
for ( int i = 0; i < 2; i ++ ) {
if ( i == 0 ) // == instead of = !!!!!!!!!!!!!!!!!!!!!!
some_string = "ab";
else
some_string = "cd";
acc[i] = some_string;
some_string = "";
}
return acc;
Think over using a std::vector or std::list instead of pointer too. With a vector, your code looks like:
std::vector<std::string> vs;
...and later...
vs.push_back("ab");
vs.push_back("cd");
I am programming some automated test equipment (ATE) and I'm trying to extract the following values out of an example response from the ATE:
DCRE? 1,
DCRE P, 10.3, (pin1)
DCRE F, 200.1, (pin2)
DCRE P, 20.4, (pin3)
From each line, I only care about the pin and the measured result value. So for the case above, I want to store the following pieces of information in a map<std::string, double> results;
results["pin1"] = 50.3;
results["pin2"] = 30.8;
results["pin3"] = 70.3;
I made the following code to parse the response:
void parseResultData(map<Pin*, double> &pinnametoresult, string &datatoparse) {
char *p = strtok((char*) datatoparse.c_str(), " \n");
string lastread;
string current;
while (p) {
current = p;
if(current.find('(') != string::npos) {
string substring = lastread.substr(1);
const char* last = substring.c_str();
double value = strtod(last, NULL);
unsigned short number = atoi(current.substr(4, current.size()-2).c_str());
pinnametoresult[&pinlookupmap[number]] = value;
}
lastread = p;
p = strtok(NULL, " \n");
}
}
It works, but it's not very efficient. Is there a way to make the function more efficient for this specific case? I don't care about the DCRE or P/F value on each line. I thought about using Boost regex library, but not sure if that would be more efficient.
In order to make this a bit more efficient, try to avoid copying. In particular, calls to substring, assignments etc can cause havoc on the performance. If you look at your code, you will see that the content of datatoparse are repeatedly assigned to lastread and current, each time with one line less at the beginning. So, on average you copy half of the original string times the number of lines, making just that part an O(n^2) algorithm. This isn't relevant if you have three or four line (not even on 100 lines!) but if you have a few more, performance degrades rapidly.
Try this approach instead:
string::size_type p0 = 0;
string::size_type p1 = input.find('\n', p0);
while (p1 != string::npos) {
// extract the line
string line = input.substr(p0, p1 - p0);
// move to the next line
p0 = p1 + 1;
p1 = input.find('\n', p0);
}
Notes:
Note that the algorithm still copies all input once, but each line only once, making it O(n).
Since you have a copy of the line, you can insert '\0' as artificial separator in order to give a substring to e.g. atoi() or strtod().
I'm not 100% sure of the order of parameters for string::find() and too lazy to look it up, but the idea is to start searching at a certain position. Look at the various overloads of find-like functions.
When handling a line, search the indices of the parts you need and then extract and parse them.
If you have line fragments (i.e. a partial line without a newline) at the end, you will have to modify the loop slightly. Create tests!
This is what I did:
#include <cstdlib>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <iostream>
using namespace std;
struct Pin {
string something;
Pin() {}
};
vector<Pin*> pins = { new Pin(), new Pin(), new Pin() };
typedef unordered_map<Pin*, double> CONT_T;
inline bool OfInterest(const string& line) {
return line.find("(") != string::npos;
}
void parseResultData(CONT_T& pinnametoresult, const string& datatoparse)
{
istringstream is(datatoparse);
string line;
while (getline(is, line)) {
if (OfInterest(line)) {
double d = 0.0;
unsigned int pinid;
size_t firstComma = line.find(",")+2; // skip space
size_t secondComma = line.find(",", firstComma);
istringstream is2(line.substr(firstComma, secondComma-firstComma));
is2 >> d;
size_t paren = line.find("(")+4; // skip pin
istringstream is3(line.substr(paren, (line.length()-paren)-1));
is3 >> pinid;
--pinid;
Pin* pin = pins[pinid];
pinnametoresult[pin] = d;
}
}
}
/*
*
*/
int main(int argc, char** argv) {
string datatoparse = "DCRE? 1, \n"
"DCRE P, 10.3, (pin1)\n"
"DCRE F, 200.1, (pin2)\n"
"DCRE P, 20.4, (pin3)\n";
CONT_T results;
parseResultData(results, datatoparse);
return 0;
}
Here's my final result. Does not involve any copying, but it will destroy the string.
void parseResultData3(map<std::string, double> &pinnametoresult, std::string &datatoparse) {
char* str = (char*) datatoparse.c_str();
int length = datatoparse.size();
double lastdouble = 0.0;
char* startmarker = NULL; //beginning of next pin to parse
for(int pos = 0; pos < length; pos++, str++) {
if(str[0] == '(') {
startmarker = str + 1;
//get previous value
bool triggered = false;
for(char* lookback = str - 1; ; lookback--) {
if(!triggered && (isdigit(lookback[0]) || lookback[0] == '.')) {
triggered = true;
*(lookback + 1) = '\0';
}
else if(triggered && (!isdigit(lookback[0]) && lookback[0] != '.')) {
lastdouble = strtod(lookback, NULL);
break;
}
}
}
else if(startmarker != NULL) {
if(str[0] == ')') {
str[0] = '\0';
pinnametoresult[startmarker] = lastdouble;
startmarker = NULL;
}
if(str[0] == ',') {
str[0] = '\0';
pinnametoresult[startmarker] = lastdouble;
startmarker = str + 1;
}
}
}
}
I have this function sentanceParse with a string input which returns a list. The input might be something like "Hello my name is Anton. What's your name?" and then the return value would be a list containing "Hello my name is Anton" and "What's your name?". However, this is not what happens. It seems as if the whitespaces in the sentences are treated like a separator and therefore the return is rather "Hello", "my", "name" etc instead of what I expected.
How would you propose I solve this?
As I am not a 100% sure the problem does not lie within my code, I will add that to the post as well:
Main:
list<string> mylist = sentanceParse(textCipher);
list<string>::iterator it;
for(it = mylist.begin(); it != mylist.end(); it++){
textCipher = *it;
cout << textCipher << endl; //This prints out the words separately instead of the entire sentances.
sentanceParse:
list<string> sentanceParse(string strParse){
list<string> strList;
int len = strParse.length();
int pos = 0;
int count = 0;
for(int i = 0; i < len; i++){
if(strParse.at(i) == '.' || strParse.at(i) == '!' || strParse.at(i) == '?'){
if(i < strParse.length() - 1){
while(i < strParse.length() - 1 && (strParse.at(i+1) == '.' || strParse.at(i+1) == '!' || strParse.at(i+1) == '?')){
if(strParse.at(i+1) == '?'){
strParse.replace(i, 1, "?");
}
strParse.erase(i+1, 1);
len -= 1;
}
}
char strTemp[2000];
int lenTemp = strParse.copy(strTemp, i - pos + 1, pos);
strTemp[lenTemp] = '\0';
std::string strAdd(strTemp);
strList.push_back(strAdd);
pos = i + 1;
count ++;
}
}
if(count == 0){
strList.push_back(strParse);
}
return strList;
}
Your implementation of sentence parse is wrong, here is a simpler correct solution.
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
EDIT:
Here is a full example program using this function. Type some sentences in your console, press enter and it will spit the sentences out with a newline separating them instead of punctuation.
#include <iostream>
#include <string>
#include <list>
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
int main (int argc, const char * argv[])
{
std::string s;
while (std::getline(std::cin,s)) {
std::list<std::string> t= sentence_parse(s);
std::list<std::string>::iterator x=t.begin();
while (x!=t.end()) {
std::cout<<*x<<"\n";
++x;
}
}
return 0;
}
// This function should be easy to adapt to any basic libary
// this is in Windows MFC
// pass in a string, a char and a stringarray
// returns an array of strings using char as the separator
void tokenizeString(CString theString, TCHAR theToken, CStringArray *theParameters)
{
CString temp = "";
int i = 0;
for(i = 0; i < theString.GetLength(); i++ )
{
if (theString.GetAt(i) != theToken)
{
temp += theString.GetAt(i);
}
else
{
theParameters->Add(temp);
temp = "";
}
if(i == theString.GetLength()-1)
theParameters->Add(temp);
}
}