Cannot properly eliminate string one from another character by character - c++

Here the function (sub) takes two string as input, traversing two string I try to find out if there is any matches in string1 compared to string2. If any that character of string1 is replaced by NULL character. Now this works properly for non repeated character. But if string1 has more than one character that matches once it all replaced by NULL character where i needed only one replacement. For example if string1 and string2 are 122 and 2, after elimination i need 1 2 where i gets now a single 1.
void sub (string str1, string str2){
int i,j,k;
for(i=0; i<=str2.size() ; i++){
for(j=0; j<=str1.size() ; j++ ){
if( str2[i] == str1[j] )
str1[j] = NULL;
}
}
cout<<str1;
expected result is 1 2 instead of 1, if str1=122 and str2=2

You are making things more difficult on yourself than they need to be. The string library provides two functions that can do exactly what you need in a single call.
The member function std::basic_string::find_first_of will locate the first occurrence of a character from string2 in string1 returning the position where it is found.
The std::basic_string::erase function can remove all characters from string1 beginning at that position.
Your sub function will then reduce to:
void sub (std::string& s1, const std::string& s2)
{
s1.erase (s1.find_first_of (s2));
}
A short example using your given strings would be:
#include <iostream>
#include <string>
void sub (std::string& s1, const std::string& s2)
{
s1.erase (s1.find_first_of (s2));
}
int main (void) {
std::string s1 ("122"), s2 ("2");
sub (s1, s2);
std::cout << "s1: " << s1 << "\ns2: " << s2 << '\n';
}
Example Use/Output
$ ./bin/sub1at2
s1: 1
s2: 2
Look things over and let me know if you have further questions.

You can't remove a character from a string by setting it to NULL. The length of the string will remain the same. But one way to simulate the removal of the duplicates is to return a new string that matches the return conditions.
First iterate over the second string and use a hash table to map each character in s2 to true. Then iterate over s1 and add the current character to a new string only if the character in the hash table maps to false. Remapping the character to false after this condition ensures that all but one of the number of characters is written to the result string.
string remove_first_duplicates(string s1, string s2) {
unordered_map<char, bool> m;
string result;
for (char i : s2) m[i] = true;
for (char i : s1) {
if (!m[i]) result += i;
m[i] = false;
}
return result;
}

NULL is not a character-constant, even if \0 is the null character. It's a macro for a null pointer constant, and for historical reasons is often defined as 0, though it might be nullptr or any other null pointer constant.
Zeroing out characters does not stop them being part of the string. for that, you must move the remaining ones and adjust the length.
If you only want to do it once, on the first match, leve the function with return afterwards.
Consider separating it into two functions: One for finding a match, and one calling that and using the result for removing the first match.

As far as I understood your question, you want to remove one char from str1 corresponding to a match in str2.
void sub(string str1, string str2)
{
int i = 0, j = 0;
while (j < str2.size())
{
if (str1[i] == str2[j])
{
str1[i] = NULL; // could use str1.erase(i,1)
i = 0;
j += 1;
continue;
}
else
i += 1;
if (i == str1.size() - 1)
{
i = 0;
j += 1;
}
}
cout<<str1<<endl;
}
This will yield the output you desire. But this will produce NULL char in str1, better option is to use erase functionality from std::string.

Related

Recognize string formatting Debug Assertion

I have a runtime problem with code below.
The purpose is to "recognize" the formats (%s %d etc) within the input string.
To do this, it returns an integer that matches the data type.
Then the extracted types are manipulated/handled in other functions.
I want to clarify that my purpose isn't to write formatted types in a string (snprintf etc.) but only to recognize/extract them.
The problem is the crash of my application with error:
Debug Assertion Failed!
Program:
...ers\Alex\source\repos\TestProgram\Debug\test.exe
File: minkernel\crts\ucrt\appcrt\convert\isctype.cpp
Line: 36
Expression: c >= -1 && c <= 255
My code:
#include <iostream>
#include <cstring>
enum Formats
{
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING,
TYPE_NUM
};
typedef struct Format
{
Formats Type;
char Name[5 + 1];
} SFormat;
SFormat FormatsInfo[TYPE_NUM] =
{
{TYPE_INT, "d"},
{TYPE_FLOAT, "f"},
{TYPE_STRING, "s"},
};
int GetFormatType(const char* formatName)
{
for (const auto& format : FormatsInfo)
{
if (strcmp(format.Name, formatName) == 0)
return format.Type;
}
return -1;
}
bool isValidFormat(const char* formatName)
{
for (const auto& format : FormatsInfo)
{
if (strcmp(format.Name, formatName) == 0)
return true;
}
return false;
}
bool isFindFormat(const char* strBufFormat, size_t stringSize, int& typeFormat)
{
bool foundFormat = false;
std::string stringFormat = "";
for (size_t pos = 0; pos < stringSize; pos++)
{
if (!isalpha(strBufFormat[pos]))
continue;
if (!isdigit(strBufFormat[pos]))
{
stringFormat += strBufFormat[pos];
if (isValidFormat(stringFormat.c_str()))
{
typeFormat = GetFormatType(stringFormat.c_str());
foundFormat = true;
}
}
}
return foundFormat;
}
int main()
{
std::string testString = "some test string with %d arguments"; // crash application
// std::string testString = "%d some test string with arguments"; // not crash application
size_t stringSize = testString.size();
char buf[1024 + 1];
memcpy(buf, testString.c_str(), stringSize);
buf[stringSize] = '\0';
for (size_t pos = 0; pos < stringSize; pos++)
{
if (buf[pos] == '%')
{
if (buf[pos + 1] == '%')
{
pos++;
continue;
}
else
{
char bufFormat[1024 + 1];
memcpy(bufFormat, buf + pos, stringSize);
bufFormat[stringSize] = '\0';
int typeFormat;
if (isFindFormat(bufFormat, stringSize, typeFormat))
{
std::cout << "type = " << typeFormat << "\n";
// ...
}
}
}
}
}
As I commented in the code, with the first string everything works. While with the second, the application crashes.
I also wanted to ask you is there a better/more performing way to recognize types "%d %s etc" within a string? (even not necessarily returning an int to recognize it).
Thanks.
Let's take a look at this else clause:
char bufFormat[1024 + 1];
memcpy(bufFormat, buf + pos, stringSize);
bufFormat[stringSize] = '\0';
The variable stringSize was initialized with the size of the original format string. Let's say it's 30 in this case.
Let's say you found the %d code at offset 20. You're going to copy 30 characters, starting at offset 20, into bufFormat. That means you're copying 20 characters past the end of the original string. You could possibly read off the end of the original buf, but that doesn't happen here because buf is large. The third line sets a NUL into the buffer at position 30, again past the end of the data, but your memcpy copied the NUL from buf into bufFormat, so that's where the string in bufFormat will end.
Now bufFormat contains the string "%d arguments." Inside isFindFormat you search for the first isalpha character. Possibly you meant isalnum here? Because we can only get to the isdigit line if the isalpha check passes, and if it's isalpha, it's not isdigit.
In any case, after isalpha passes, isdigit will definitely return false so we enter that if block. Your code will find the right type here. But, the loop doesn't terminate. Instead, it continues scanning up to stringSize characters, which is the stringSize from main, that is, the size of the original format string. But the string you're passing to isFindFormat only contains the part starting at '%'. So you're going to scan past the end of the string and read whatever's in the buffer, which will probably trigger the assertion error you're seeing.
Theres a lot more going on here. You're mixing and matching std::string and C strings; see if you can use std::string::substr instead of copying. You can use std::string::find to find characters in a string. If you have to use C strings, use strcpy instead of memcpy followed by the addition of a NUL.
You could just demand it to a regexp engine which bourned to search through strings
Since C++11 there's direct support, what you have to do is
#include <regex>
then you can match against strings using various methods, for instance regex_match which gives you the possibility, together with an smatch to find out your target with just few lines of codes using standard library
std::smatch sm;
std::regex_match ( testString.cbegin(), testString.cend(), sm, str_expr);
where str_exp is your regex to find what you want specifically
in the sm you have now every matched string against your regexp, which you can print in this way
for (int i = 0; i < sm.size(); ++i)
{
std::cout << "Match:" << sm[i] << std::endl;
}
EDIT:
to better express the result you would achieve i'll include a simple sample below
// target string to be searched against
string target_string = "simple example no.%d is: %s";
// pattern to look for
regex str_exp("(%[sd])");
// match object
smatch sm;
// iteratively search your pattern on the string, excluding parts of the string already matched
cout << "My format strings extracted:" << endl;
while (regex_search(target_string, sm, str_exp))
{
std::cout << sm[0] << std::endl;
target_string = sm.suffix();
}
you can easily add any format string you want modifying the str_exp regex expression.

How to find see if one string is located inside of another and replace it?

I need help with the following program. I don't know how to check if occurrences of string2 are inside string1, and then replace them with string3.
Write a function named replaceSubstring. The function should accept three string object arguments. Let’s call them string1, string2, and string3. It should search string1 for all occurrences of string2. When it finds an occurrence of string2, it should replace it with string3. Demonstrate and test the function with a complete program.
For example, suppose the three arguments have the following values:
string1: "the dog jumped over the fence"
string2: "the"
string3: "that"
With these three arguments, the function would return a string object with the value "that dog jumped over that fence". Demonstrate the function in a complete program.
int main()
{
string string1 = "xyzxyzxyz";
string string2 = "xyz";
string string3 = "a";
replaceSubstring(string1, string2, string3);
return 0;
}
void replaceSubstring(string string1, string string2, string string3)
{
string result;
for (int i = 0; i < string1.length(); i++){
if (string1.find(string2, i)){
result = string1.replace(i, string3.length()+i, string3);
}
}
cout << result;
}
A quick way to do this is with the Boost String Algorithms Library as stated here
#include <boost/algorithm/string/replace.hpp>
{ // 1.
string test = "abc def abc def";
boost::replace_all(test, "abc", "hij");
boost::replace_all(test, "def", "klm");
}
{ // 2.
string test = boost::replace_all_copy
( boost::replace_all_copy<string>("abc def abc def", "abc", "hij")
, "def"
, "klm"
);
}
Because, as stated here:
There is no one built-in function in C++ to do this. If you'd like to replace all instances of one substring with another, you can do so by intermixing calls to string::find and string::replace. For example:
size_t index = 0;
while (true) {
/* Locate the substring to replace. */
index = str.find("abc", index);
if (index == std::string::npos) break;
/* Make the replacement. */
str.replace(index, 3, "def");
/* Advance index forward so the next iteration doesn't pick it up as well. */
index += 3;
}
If the objective is to implement your own code for replacing matched strings, I would recommend that you read up on Z and KMP algorithms. For a quick and dirty solution, see below Geeks For Geeks:
// C program for Naive Pattern Searching algorithm
#include<stdio.h>
#include<string.h>
void search(char *pat, char *txt)
{
int M = strlen(pat);
int N = strlen(txt);
/* A loop to slide pat[] one by one */
for (int i = 0; i <= N - M; i++)
{
int j;
/* For current index i, check for pattern match */
for (j = 0; j < M; j++)
if (txt[i+j] != pat[j])
break;
if (j == M) // if pat[0...M-1] = txt[i, i+1, ...i+M-1]
printf("Pattern found at index %d \n", i);
}
}
/* Driver program to test above function */
int main()
{
char txt[] = "AABAACAADAABAAABAA";
char pat[] = "AABA";
search(pat, txt);
return 0;
}
After the positions have been found, write a method to build a new string and replace, add, delete characters, one by one starting at the positions.
string.find returns the index of the first character of the found string. Here's a small example.
int main()
{
string myString = "ThisMessageIsPointLess";
string strToFind = "Is";
size_t idx = myString.find(strToFind.c_str());
// idx = 11 because 'Is' starts at the 11th position in the string
idx = myString.find("Foo");
// idx == std::string::npos because it hasn't found "Foo" in myString
In all cases, you should check that the substring has been found in the string. You just have to compare the index that's being returned with the default failure value from the library:
if (idx == std::string::npos)
{
// Failed to find the substring
return false;
}
Now, there are quite a few overloads of std::string::replace and they can do different things. I suggest you use the one that takes two iterators and the 'replacement' string.
string replacement = "Seems";
myString.replace(myString.begin() + idx, myString.begin() + idx + strToFind.size(), replacement.c_str());
// myString = "ThisMessageSeemsPointless"
return true;
The overload we've used takes the iterator of the first character to replace, the iterator of the last character to replace, and the replacement string. We know that the string starts at idx from the beginning. By simple logic, we know it should end at idx + strToFind.size() from the beginning.
PS: it might be relevant to name your variables so other people understand them more easily. Don't put "string" in the name; its type is string so that's redundant. Consider replacing string2 by token and string3 by newToken or replacement... anything that's more meaningful than numbers.
Last thing, you might want to pass your variables by reference (for string1) and by const-reference (for string2 and string3). This will avoid creating local copies of the strings and should improve overall performance.
bool replaceSubstring(string& string1, string const& string2, string const& string3)
Your function has several problems in it:
you did not follow the instructions! "the function would return a string object...". your function does not return anything. It just outputs the result to the console screen instead. That should be main()'s responsibility instead.
you have the right idea to use std::string::find() and std::replace(), but you are using them both completely wrong.
find() returns the index of the substring, or -1 (well, technically std::string::npos) if not found. Any value other than 0 will evaluate as true in an if expression. Your code is assuming that the substring is found at the same index that you are passing into find() as an input parameter, and that is simply not guaranteed.
the version of replace() you are calling expects a starting index and the number of characters to replace. You are not passing it the index returned by find(), and you are passing it the wrong character count, too.
You are also looping through the input string one character at a time. Replacing substrings within the same input string that you are looping through is going to reek havoc with your looping logic.
With that said, try this instead:
string replaceSubstring(const string &string1, const string &string2, const string &string3)
{
string result = string1;
string::size_type pos = 0;
do
{
pos = result.find(string2, pos);
if (pos == string::npos) break;
result.replace(pos, string2.length(), string3);
pos += string3.length();
}
while (true);
return result;
}
int main()
{
string string1 = "the dog jumped over the fence";
string string2 = "the";
string string3 = "that";
string result = replaceSubstring(string1, string2, string3);
cout << result;
return 0;
}
Without any additional library like boost, you need to write a custom replacement algorithm using the std library. This one computes the resulting string as a returned value.
string replaceSubstring(const string& string1, const string& string2, const string& string3)
{
string result;
size_t posStart = 0, posFound;
for(; (posFound = string1.find(string2, posStart)) != string1.npos;
posStart = posFound + string2.size())
{
copy(string1.begin()+posStart, string1.begin()+posFound, back_inserter(result));
result.append(string3);
}
copy(string1.begin()+posStart, string1.end(), back_inserter(result));
return result;
}
Of course you can easily change it into void by changing the fisrt parameter from const string& string1 to string& string1 and assigning it from the result before returning.
If this code is to run on large strings and if it turns out that the allocations needed by the back-insertion are not correctly optimized by the implementation (a concern raised by #RemyLebeau in the comments), you can make some reservation prior to the copy statements:
...
result.reserve(result.size() + posFound + string3.size() + 2 - posStart);
copy(string1.begin()+posStart, string1.begin() + posFound, back_inserter(result));
...
result.reserve(result.size() + string1.size() + 2 - posStart);
copy(string1.begin()+posStart, string1.end(), back_inserter(result));

std::string returning inappropriate value

I wrote a program which perform string compression using counts of repeated characters. The program in C++ is :
#include<iostream>
#include<cstring>
std::string compressBad(std::string str)
{
std::string mystr = "";
int count = 1;
char last = str[0];
for (int i = 0; i < str.length();++i)
{
if(str[i] == last)
count++;
else
{
std::string lastS = last+"";
std::string countS = std::to_string(count);
mystr.append(lastS);
mystr.append(countS);
//mystr = mystr + last + count;
count = 1;
last = str[i];
}
}
std::string lastS = last+"";
std::string countS = std::to_string(count);
mystr.append(lastS);
mystr.append(countS);
return mystr;
//return mystr+last+count;
}
int main()
{
std::string str;
std::getline(std::cin, str);
std::string str2 = compressBad(str);
std::cout<<str2;
/*if (str.length() < str2.length())
std::cout<<str;
else
std::cout<<str2;*/
std::cout<<std::endl;
return 0;
}
Few example on running this are :
Input : sssaaddddd
Output : ùÿÿ*425
Output it should print : s3a2d5
Second example:
Input : sssaaddd
Output: ùÿÿ*423
Output it should print : s3a2d3
I also implemented the same concept in Java and there it is working fine. The java implementation is here
Why is this problem happening with above code.
There may be other issues in your code, but I think that this line might be to blame:
std::string lastS = last+"";
Here, you're trying to convert the character last to a string by concatenating the empty string to the end. Unfortunately, in C++ this is interpreted to mean "take the numeric value of the character last, then add that to a pointer that points to the empty string, producing a new pointer to a character." This pointer points into random memory, hence the garbage you're seeing. (Notice that this is quite different from how Java works!)
Try changing this line to read
std::string lastS(1, last);
This will initialize lastS to be a string consisting of just the character stored in last.
Another option would be to use an ostringstream:
std::ostringstream myStr;
myStr << last << count;
// ...
return myStr.str();
This eliminates all the calls to .append() and std::to_string and is probably a lot easier to read.
last + "" doesn't do what you think.
just do
mystr.append(1, last);

Concatenate one character from string 1 and second character from string 2

I want to concatenate two strings but it like odd position characters from strin1 and even position characters from stri2.
This is what i have done:
string mainvalue(string og2,string eg2)
{
string odd=og2;
string even=eg2;
string s3;
reverse(odd.begin(),odd.end());
cout<<"Main ordered text is :"<<odd<<endl;
int length1=odd.length();
int length2=even.length();
for(int i=0;i<length1;i++)
{
for(int j=0;j<length2;j++)
{
s3 += odd[j]+even[j];
}
}
cout<<"Complete text is :"<<s3<<endl;
//return s3;
}
Output is look like : :▌π═▌π═▌π═
The reason for your strange output is that you're adding two characters, and adding two alphabetical characters rarely gives a printable result.
But your code would be incorrect even if you could add characters to make strings, as your result would basically be length1 copies of the desired result.
Assuming that the input strings are of equal length, one simple solution is to alternately take one character from each string.
This assumes that the first element is "odd" - if it's "even", just swap the two lines in the loop:
string mainvalue(string odds, string evens)
{
string merged;
for (string::size_type i = 0; i < odds.size(); ++i)
{
merged += odds[i];
merged += evens[i];
}
return merged;
}
You could also do something like this:
string mainvalue(string odds, string evens)
{
auto merged = string {odds.size() + evens.size(), ' '};
auto odd = odds.begin();
auto even = evens.begin();
auto out = merged.begin();
while (odd != odds.end())
{
*out++ = *odd++;
*out++ = *even++;
}
return merged;
}

String reversal in C++

I am trying to reverse the order of words in a sentence by maintaining the spaces as below.
[this is my test string] ==> [string test my is this]
I did in a step by step manner as,
[this is my test string] - input string
[gnirts tset ym si siht] - reverse the whole string - in-place
[string test my is this] - reverse the words of the string - in-place
[string test my is this] - string-2 with spaces rearranged
Is there any other method to do this ? Is it also possible to do the last step in-place ?
Your approach is fine. But alternatively you can also do:
Keep scanning the input for words and
spaces
If you find a word push it onto stack
S
If you find space(s) enqueue the
number of spaces into a queue Q
After this is done there will be N words on the stack and N-1 numbers in the queue.
While stack not empty do
print S.pop
if stack is empty break
print Q.deque number of spaces
end-while
Here's an approach.
In short, build two lists of tokens you find: one for words, and another for spaces. Then piece together a new string, with the words in reverse order and the spaces in forward order.
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
string test_string = "this is my test string";
int main()
{
// Create 2 vectors of strings. One for words, another for spaces.
typedef vector<string> strings;
strings words, spaces;
// Walk through the input string, and find individual tokens.
// A token is either a word or a contigious string of spaces.
for( string::size_type pos = 0; pos != string::npos; )
{
// is this a word token or a space token?
bool is_char = test_string[pos] != ' ';
string::size_type pos_end_token = string::npos;
// find the one-past-the-end index for the end of this token
if( is_char )
pos_end_token = test_string.find(' ', pos);
else
pos_end_token = test_string.find_first_not_of(' ', pos);
// pull out this token
string token = test_string.substr(pos, pos_end_token == string::npos ? string::npos : pos_end_token-pos);
// if the token is a word, save it to the list of words.
// if it's a space, save it to the list of spaces
if( is_char )
words.push_back(token);
else
spaces.push_back(token);
// move on to the next token
pos = pos_end_token;
}
// construct the new string using stringstream
stringstream ss;
// walk through both the list of spaces and the list of words,
// keeping in mind that there may be more words than spaces, or vice versa
// construct the new string by first copying the word, then the spaces
strings::const_reverse_iterator it_w = words.rbegin();
strings::const_iterator it_s = spaces.begin();
while( it_w != words.rend() || it_s != spaces.end() )
{
if( it_w != words.rend() )
ss << *it_w++;
if( it_s != spaces.end() )
ss << *it_s++;
}
// pull a `string` out of the results & dump it
string reversed = ss.str();
cout << "Input: '" << test_string << "'" << endl << "Output: '" << reversed << "'" << endl;
}
I would rephrase the problem this way:
Non-space tokens are reversed, but preserves their original order
The 5 non-space tokens ‘this’, ‘is’, ‘my’, ‘test’, ‘string’ gets reversed to ‘string’, ‘test’, ‘my’, ‘is’, ‘this’.
Space tokens remain in the original order
The space tokens ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘ remains in original order between the new order of non-space tokens.
Following is a O(N) solution [N being the length of char array]. Unfortunately, it is not in place as OP wanted, but it does not use additional stack or queue either -- it uses a separate character array as a working space.
Here is a C-ish pseudo code.
work_array = char array with size of input_array
dst = &work_array[ 0 ]
for( i = 1; ; i++) {
detect i’th non-space token in input_array starting from the back side
if no such token {
break;
}
copy the token starting at dst
advance dst by token_size
detect i’th space-token in input_array starting from the front side
copy the token starting at dst
advance dst by token_size
}
// at this point work_array contains the desired output,
// it can be copied back to input_array and destroyed
For words from first to central words switch word n with word length - n
First use a split function and then do the switching
This pseudocode assumes you don't end the initial string with a blank space, though can be suitably modified for that too.
1. Get string length; allocate equivalent space for final string; set getText=1
2. While pointer doesn't reach position 0 of string,
i.start from end of string, read character by character...
a.if getText=1
...until blank space encountered
b.if getText=0
...until not blank space encountered
ii.back up pointer to previously pointed character
iii.output to final string in reverse
iv.toggle getText
3. Stop
All strtok-solutions work not for your example, see above.
Try this:
char *wordrev(char *s)
{
char *y=calloc(1,strlen(s)+1);
char *p=s+strlen(s);
while( p--!=s )
if( *p==32 )
strcat(y,p+1),strcat(y," "),*p=0;
strcpy(s,y);
free(y);
return s;
}
Too bad stl string doesn't implement push_front. Then you could do this with transform().
#include <string>
#include <iostream>
#include <algorithm>
class push_front
{
public:
push_front( std::string& s ) : _s(s) {};
bool operator()(char c) { _s.insert( _s.begin(), c ); return true; };
std::string& _s;
};
int main( int argc, char** argv )
{
std::string s1;
std::string s( "Now is the time for all good men");
for_each( s.begin(), s.end(), push_front(s1) );
std::cout << s << "\n";
std::cout << s1 << "\n";
}
Now is the time for all good men
nem doog lla rof emit eht si woN
Copy each string in the array and print it in reverse order(i--)
int main()
{
int j=0;
string str;
string copy[80];
int start=0;
int end=0;
cout<<"Enter the String :: ";
getline(cin,str);
cout<<"Entered String is : "<<str<<endl;
for(int i=0;str[i]!='\0';i++)
{
end=s.find(" ",start);
if(end==-1)
{
copy[j]=str.substr(start,(str.length()-start));
break;
}
else
{
copy[j]=str.substr(start,(end-start));
start=end+1;
j++;
i=end;
}
}
for(int s1=j;s1>=0;s1--)
cout<<" "<<copy[s1];
}
I think I'd just tokenize (strtok or CString::Tokanize) the string using the space character. Shove the strings into a vector, than pull them back out in reverse order and concatenate them with a space in between.