remove element by position in a vector<string> in c++ - c++

I have been trying to remove the value False and 0;0 from a vector<string> plan; containing the following
1003;2021-03-09;False;0;0;1678721F
1005;2021-03-05;False;0;0;1592221D
1005;2021-03-06;False;0;0;1592221D
1003;2021-03-07;False;0;0;1592221D
1003;2021-03-08;False;0;0;1592221D
1004;2021-03-09;False;0;0;1592221D
1004;2021-03-10;False;0;0;1592221D
1001;2021-03-11;False;0;0;1592221D
but the solutions I have found only work with int, and I tried the following
remove(plan.begin(), plan.end(), "False");
also with erase, but it didn't work
what is the mistake that I am making, or how should I do to eliminate the values that I want, which are in the position [2] [3] and [4], thanks for any help.

[Note: With the assumption 1003;2021-03-09;False;0;0;1678721F corresponding to a row inside std::vector<string>]
std::remove : Removes from the vector either a single element (position) or a range of elements ([first, last)).
In case std::vector<string> plan contains value False then it is removed.
std::vector < std::string > plan =
{
"1003","2021-03-09","False","0;0","1678721F"
};
std::remove(plan.begin(),plan.end(),"False");
In your case you need to remove given sub-string from each row of the plan. You need to iterate through all the rows to remove given value using std::string::erase.
std::vector < std::string > plan =
{
"1003;2021-03-09;False;0;0;1678721F",
"1005;2021-03-05;False;0;0;1592221D",
"1005;2021-03-06;False;0;0;1592221D",
"1003;2021-03-07;False;0;0;1592221D",
"1003;2021-03-08;False;0;0;1592221D",
"1004;2021-03-09;False;0;0;1592221D",
"1004;2021-03-10;False;0;0;1592221D",
"1001;2021-03-11;False;0;0;1592221D"};
for (auto & e:plan)
{
//As position of False;0;0; is at a fixed index, i.e: from index:16, 10 characters are removed
e.erase (16, 10);
}
To generalize, You can make use of std::String::find to find a sub-string and erase it.
void removeSubstrs(string& s, string p) {
string::size_type n = p.length();
for (string::size_type i = s.find(p);
i != string::npos;
i = s.find(p))
s.erase(i, n);
}
int
main ()
{
std::vector < std::string > plan =
{
"1003;2021-03-09;False;0;0;1678721F",
"1005;2021-03-05;False;0;0;1592221D",
"1005;2021-03-06;False;0;0;1592221D",
"1003;2021-03-07;False;0;0;1592221D",
"1003;2021-03-08;False;0;0;1592221D",
"1004;2021-03-09;False;0;0;1592221D",
"1004;2021-03-10;False;0;0;1592221D",
"1001;2021-03-11;False;0;0;1592221D"};
for (auto & e:plan)
{
removeSubstrs (e, ";False;0;0");
}
for (auto e:plan)
std::cout << e << std::endl;
return 0;
}

[Note: This answer assumes that each line corresponds to an element in the vector]
With the statement
remove(plan.begin(), plan.end(), "False");
you try to remove all elements from the vector that are equal to "False".
You need to iterate over the vector and erase the sub-string from each and every string in the vector.
For example you can use a range for loop to iterate over all the strings (or rather references to them), and then use the std::string functions find to find the sub-strings you want to remove and replace to replace the sub-strings with empty strings (i.e. nothing).

If you are sure that there is only one occurrence of "First" and "0;0" in your vector, you can use something like this:
std::string EraseFirstSubString(
const std::string & main_str,
const std::string & sub_str)
{
std::string new_main_str = main_str;
size_t pos = new_main_str.find(sub_str);
if (pos != std::string::npos)
{
new_main_str.erase(pos, sub_str.length());
}
return new_main_str;
}
int main()
{
std::vector<std::string> plan = {
"1003;2021-03-09;False;0;0;1678721F",
"1005;2021-03-05;False;0;0;1592221D",
"1005;2021-03-06;False;0;0;1592221D",
"1003;2021-03-07;False;0;0;1592221D",
"1003;2021-03-08;False;0;0;1592221D",
"1004;2021-03-09;False;0;0;1592221D",
"1004;2021-03-10;False;0;0;1592221D",
"1001;2021-03-11;False;0;0;1592221D"
};
for (std::string & str : plan)
{
str = EraseFirstSubString(str, "False");
str = EraseFirstSubString(str, "0;0");
}
};
But, if you think that you may have many occurrences of those sub-strings, you should improve a little bit your sub-string removing mechanism like this:
std::string EaraseSubStrings(
const std::string & main_str,
const std::string & sub_str)
{
std::string new_main_str = main_str;
size_t pos = new_main_str.find(sub_str);
while (pos != std::string::npos)
{
new_main_str.erase(pos, sub_str.length());
pos = new_main_str.find(sub_str);
}
return new_main_str;
}

If you already have a vector of individual std::string objects, you can easily use the operations that the strings library offers.
#include <algorithm>
#include <vector>
#include <string>
// before C++20 change constexpr to inline
constexpr void change(std::vector<std::string>& sv, std::string const& rem) {
for_each(beign(sv),end(sv), [&rem](std::string& s) {
s.erase(std::min(s.size(),s.find(rem)), rem.size());
});
}

Related

c++ Find string when given only part of it inside an array like structure

It's in the form of a word so let's say I'm given the string "foo", and inside my array there are words like "food", "fool", "foo". All three of them should be printed out.
I haven't made a solid attempt at it yet cause I don't know how to wrap my head around it. Any idea?
Assuming you're using std::string, you could use string::find to see if one string is contained in another.
If you have a vector of strings, you might use that along with (for example) std::remove_copy_if to print out all the words from the vector that contain the chosen word:
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <iostream>
int main() {
std::vector<std::string> words{"food", "fool", "foo", "tofoo", "lood", "flood"};
std::string word = "foo";
std::remove_copy_if(words.begin(), words.end(),
std::ostream_iterator<std::string>(std::cout, "\n"),
[&](std::string const &s) {
return s.find(word) == std::string::npos;
});
}
Result:
food
fool
foo
tofoo
You could do something simple, like iterating through each character in the string and checking it against the characters in the string you are trying to match using a separate function. If three characters in a row match the string you are searching for, add it to a vector or something and display them.
// Variables
bool charMatched = false;
vector<string> *stringVec = new vector<string>();
int index = 0;
int counter = 0;
string str = "Whatever you are trying to match";
for (char &c : strings[index]) // For each character in string
{
// Check for match
if (checkChar(c))
{
counter++;
charMatched = true;
if(counter == str.length())
stringVec->push_back(strings[index]);
}
else
{
index++;
counter = 0;
break;
}
}
bool checkChar(char c)
{
// Iterator to go through match string
static string::iterator it = str.begin();
if (c == *it)
{
if (it == str.end())
it = str.begin(); // Reset iterator
else
it++; // Increment iterator
return true;
}
else
{
if (it == str.end())
it = str.begin(); // Reset iterator
else
it++; // Increment iterator
return false;
}
}
You will have to tweak it a little to work with an array the way you want it to but something like this should do what you want. I did not run this through a compiler, I wrote it in Notepad so there may be small syntax errors. I hope this helps!

C++ Finding Anagrams in words

I'm working on a program that looks at whether or not a particular word is an anagram using std:count however, I don't think my function logic is correct and I cannot seem to figure it out.
Assume there are the following words in the file:
Evil
Vile
Veil
Live
My code is as follows:
#include <iostream>
#include <vector>
#include <fstream>
#include <map>
using namespace std;
struct Compare {
std::string str;
Compare(const std::string& str) : str(str) {}
};
bool operator==(const std::pair<int, std::string>&p, const Compare& c) {
return c.str == p.second;
}
bool operator==(const Compare& c, const std::pair<int, std::string>&p) {
return c.str == p.second;
}
std::vector<std::string> readInput(ifstream& file)
{
std::vector<std::string> temp;
string word;
while (file >> word)
{
temp.push_back(word);
}
std::sort(temp.begin(), temp.end());
return temp;
}
int main(int argc, char *argv[]) {
string file = "testing.txt";
ifstream ss(file.c_str());
if(!ss.is_open())
{
cerr << "Cannot open the text file";
}
std::vector<std::string> words = readInput(ss);
std::map<int, std::string> wordsMap;
//std::map<std::string value, int key> values;
for(unsigned i=0; (i < words.size()); i++)
{
wordsMap[i] = words[i];
}
int count = std::count(wordsMap.begin(), wordsMap.end(), Compare("Evil"));
cout << count << endl;
}
I'm pretty sure it's just a case of my logic is wrong in the functions. I hope someone can help :)
The most simple approach would be
To check like following (pseudo code)
bool isAnagram(string s, string t) {return sort(s) == sort(t); }
So, use some think like following, no need of std::map
struct Compare {
std::string str;
Compare(const std::string& x) : str(x) {
std::sort(str.begin(),str.end()); std::transform(str.begin(),
str.end(),str.begin(), ::toupper);}
bool operator ()(const std::string& t)
{
std::string s= t;
std::transform(s.begin(), s.end(),s.begin(), ::toupper);
std::sort(s.begin(),s.end());
return s == str;
}
};
And then
int count = std::count_if(words.begin(), words.end(), Compare("Evil"));
See HERE
This is not the most efficient algorithm, but a quick change to your program that would work could be:
bool operator==(const std::pair<int, std::string>&p, const Compare& c) {
std::string a = c.str;
std::transform(a.begin(), a.end(), a.begin(), ::tolower);
std::sort(a.begin(), a.end());
std::string b = p.second;
std::transform(b.begin(), b.end(), b.begin(), ::tolower);
std::sort(b.begin(), b.end());
return a == b;
}
EDIT: It seems in your present code, you are checking whether the strings are exactly equal to each other (not anagrams).
INSTEAD:
For each word, make an array of 26 elements, each element corresponding to a letter of the alphabet. Parse each word character by character, and increase the count of the particular character in the respective array.
For example for evil, the array would be:
0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0. // It has 1's for letters e,v,i and l
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
You make this array for each word that you have. In your case, all the words will have the same array. You then compare these arrays element-wise and proceed accordingly.
Now you just need to see which words have the same corresponding array.
If you want to compare all the N words pair-wise, you can do so using two nested loops in O(N^2) complexity.
The complexity for comparing one pair is O(1).
Complexity of creating the arrays = O(L) where L is the length of the string.
Consider the following:
map<string, set<string>> anagrams;
for (auto word : words)
anagrams[sort(word)].insert(word);
const set<string>& find_anagrams(const string& word)
{
return anagrams[word];
}
When you have a lot of words that are relatively short (or if you can work with large number libs), then you can use a solution similar to what I wrote here -
Generate same unique hash code for all anagrams
Essentially - map each character to a unique prime number (doesn't have to be big, you can map the entire ABC into primes up to 101), and for each word multiply the primes received from it characters. Since multiplication is commutative, anagrams would give the same result, so you just compare that result, hash it, or do whatever you want
Keep in mind that for long words the values would grow pretty fast, so you might need a big numbers lib

Replace char in string with some string inplace

i want to replace a character in the string with a string. can i do it in-place? As the new string has length greater than original string.Question is that can i do with using additional buffer?
for example
void replaceChar(std::string &input, std::string replacementString, char charToReplace)
{
//some code here. No additional buffer
}
void main(){
std::string input = "I am posting a comment on LinkedIn";
std::string replacementString = "pppp";
char charToReplace = 'o';
replaceChar(input, replacementString, charToReplace);
}
I only want the strategy (algorithm). it would be good if algorithm will be designed keeping some language in mind that will not dynamically increase or decrease the string length once it was initilized like c++
std::string has a replace member, but it works in terms of numerical positions, rather than the previous content of the string. As such, you normally have to combine it with the find member in a loop, something like this:
std::string old("o");
int pos;
while ((pos = x.find(old)) != std::string::npos)
x.replace(pos, old.length(), "pppp");
Personally, I'd rarely get concerned about how often the string gets resized, but if it's a major concern, you can use std::count to find the number of occurrences of the old string, multiply by the difference in size between the old and new strings, and use std::string::reserve() to reserve enough space. Note, however, that reserve was added in C++11 -- older implementations won't have it.
Edit: though it's not a concern with the strings you used, as #ipc pointed out, this doesn't work correctly if the replacement string contains an instance of the value being replaced. If you need to deal with that, you'll need to supply the offset in the string at which to start each search:
int pos = 0;
while ((pos = x.find(old, pos)) != std::string::npos) {
x.replace(pos, old.length(), rep);
pos += rep.length();
}
Or, you might prefer a for loop in this case:
std::string old("o");
std::string rep("pop");
for (int pos=0;
(pos = x.find(old, pos)) != std::string::npos;
pos+=rep.length())
{
x.replace(pos, old.length(), rep);
}
I think you misundertand C++ std::string. It can actually change the string length dynamically. In internally does heap allocations, and will grow the buffer if necessary.
Here is a code that minimises the number of assignments and allocations. It is based on the following answer to a similar question:
https://stackoverflow.com/a/32322122/3903076
The cases where the replacement string has length 0 or 1 are handled separately. Else, the string has to grow.
If there is not enough capacity, then an external buffer will be necessary anyway, so we just do copy-replace and swap.
The interesting case is when the string already has enough capacity, so we can actually do a non-trivial in-place replacement. We do that with a reverse copy-replace, stopping when we do not need to replace anything else.
This can be seen in the last line of the function.
void replaceChar(std::string& input, const std::string& replacementString, char charToReplace)
{
if (replacementString.empty()) {
input.erase(std::remove(input.begin(), input.end(), charToReplace), input.end());
return;
}
if (replacementString.size() == 1) {
std::replace(input.begin(), input.end(), charToReplace, replacementString.front());
return;
}
const auto first_instance = std::find(input.begin(), input.end(), charToReplace);
auto count = std::count(first_instance, input.end(), charToReplace);
const auto extra_size = count * (replacementString.size() - 1);
const auto new_size = input.size() + extra_size;
if (input.capacity() < new_size) {
std::string aux;
aux.reserve(new_size);
replace_with_range_copy(input.cbegin(), input.cend(), std::back_inserter(aux), charToReplace, replacementString.cbegin(), replacementString.cend());
input.swap(aux);
return;
}
input.resize(new_size);
const auto rlast = std::make_reverse_iterator(first_instance);
const auto rfirst = input.rbegin();
const auto old_rfirst = rfirst + extra_size;
replace_with_range_copy(old_rfirst, rlast, rfirst, charToReplace, replacementString.crbegin(), replacementString.crend());
}
Here is an implementation of the replace_with_range_copy algorithm:
template <typename InputIt1, typename OutputIt, typename T, typename InputIt2>
OutputIt replace_with_range_copy(InputIt1 first, InputIt1 last, OutputIt d_first, const T& old_value, InputIt2 new_first, InputIt2 new_last)
{
InputIt1 next;
while (true) {
if (first == last) return d_first;
next = std::find(first, last, old_value);
d_first = std::copy(first, next, d_first);
if (next == last) return d_first;
d_first = std::copy(new_first, new_last, d_first);
first = std::next(next);
}
}
i tried this old-fashioned stuff and i think it works. here it is.
i am not sure that this would work on encodings other than ascii.
#include <string>
#include <cstring>
std::string
replace_char_with_string
(const char *in_p,
char from_ch,
const char *to_p)
{
char output_c_str[strlen(in_p)*2+1], *out_p = output_c_str;
int to_len = strlen(to_p);
while (*in_p)
{
if (*in_p == from_ch)
{
strcpy(out_p, to_p);
out_p += to_len;
}
else
{
*out_p++ = *in_p;
}
++in_p;
}
*out_p = '\0';
std::string output(output_c_str);
return output;
}
// example usage
std::string java_namespace_name = "com.foo.boo";
std::string cpp_namespace_name = replace_char_with_string(java_namespace_name.c_str()

In C++, what's the fastest way to replace all occurrences of a substring within a string with another string?

I'm looking for the most efficient (in terms of "fastest") way to replace all occurrences of a substring within a string with another string. All I've came up with so far is:
std::string StringReplaceAll(const std::string &cstSearch, const std::string &cstReplace, const std::string &cstSubject)
{
if(cstSearch.length() > cstSubject.length() || cstSearch == cstReplace || cstSubject.empty() || cstSearch.empty() || cstSubject.find(cstSearch) == std::string::npos)
{
return cstSubject;
}
std::ostringstream ossReturn;
std::string::const_iterator ci(cstSubject.cbegin());
const std::string::const_iterator::difference_type ciFindSize(std::distance(cstSearch.cbegin(), cstSearch.cend()));
for(std::string::const_iterator ciNow; (ciNow = std::search(ci, cstSubject.cend(), cstSearch.cbegin(), cstSearch.cend())) != cstSubject.cend(); ci = ciNow)
{
std::copy(ci, ciNow, std::ostreambuf_iterator<char> (ossReturn));
std::copy(cstReplace.cbegin(), cstReplace.cend(), std::ostreambuf_iterator<char> (ossReturn));
std::advance(ciNow, ciFindSize);
}
std::copy(ci, cstSubject.cend(), std::ostreambuf_iterator<char> (ossReturn));
return ossReturn.str();
}
... and this one is way(!!!) too slow for my needs :-(
Looking forward to learn from you guys!
First, I'd use std::string, rather than std::ostringstream, to build
up the results; std::ostringstream is for formatting, and there's no
formatting to be done here. Other than that, you've got basically the
correct algorithm; using std::search to find where the next
replacement should be done. I'd use a while loop to make things a bit
more readable, which gives:
std::string
replaceAll( std::string const& original,
std::string const& before,
std::string const& after )
{
std::string retval;
std::string::const_iterator end = original.end();
std::string::const_iterator current = original.begin();
std::string::const_iterator next =
std::search( current, end, before.begin(), before.end() );
while ( next != end ) {
retval.append( current, next );
retval.append( after );
current = next + before.size();
next = std::search( current, end, before.begin(), before.end() );
}
retval.append( current, next );
return retval;
}
(Note that using std::string::append will be faster than using
std::copy; the string knows how many it must add, and can resize the
string accordingly.)
Afterwards, it would be trivial to catch the special case where there is
nothing to replace, and return the initial string immediately; there
might be some improvements to be had using std::string::reserve as
well. (If before and after have the same length,
retval.reserve( original.size() ) is a clear win. Even if they don't,
it could be a win. As for first counting the number of substitutions, then
exactly calculating the final size, I don't know. You'll have to
measure with your actual use cases to find out.)
I asked about this same thing at http://hardforum.com/showthread.php?t=979477 years ago.
I don't remember it all that well, but the following code was in comment #31 and I think it was faster than my other attempts (but not faster than MikeBlas' metered_string example):
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
#include <sstream>
using namespace std;
inline string replaceAll(const string& s, const string& f, const string& r) {
if (s.empty() || f.empty() || f == r || f.size() > s.size() || s.find(f) == string::npos) {
return s;
}
ostringstream build_it;
typedef string::const_iterator iter;
iter i(s.begin());
const iter::difference_type f_size(distance(f.begin(), f.end()));
for (iter pos; (pos = search(i , s.end(), f.begin(), f.end())) != s.end(); ) {
copy(i, pos, ostreambuf_iterator<char>(build_it));
copy(r.begin(), r.end(), ostreambuf_iterator<char>(build_it));
advance(pos, f_size);
i = pos;
}
copy(i, s.end(), ostreambuf_iterator<char>(build_it));
return build_it.str();
}
int main() {
const string source(20971520, 'a');
const string test(replaceAll(source, "a", "4"));
}
See the thread for more examples and lots of discussion.
If I remember correctly, it was really easy to make things faster than boost's replace_all.
Here's a clearer c++0x version:
#include <string>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <ostream>
using namespace std;
string replace_all_copy(const string& s, const string& f, const string& r) {
if (s.empty() || f.empty() || f == r || f.size() > s.size()) {
return s;
}
ostringstream buffer;
auto start = s.cbegin();
while (true) {
const auto end = search(start , s.cend(), f.cbegin(), f.cend());
copy(start, end, ostreambuf_iterator<char>(buffer));
if (end == s.cend()) {
break;
}
copy(r.cbegin(), r.cend(), ostreambuf_iterator<char>(buffer));
start = end + f.size();
}
return buffer.str();
}
int main() {
const string s(20971520, 'a');
const string result = replace_all_copy(s, "a", "4");
}
// g++ -Wall -Wextra replace_all_copy.cc -o replace_all_copy -O3 -s -std=c++0x
I think std::search uses a trivial algorithm, at least the reference states the complexity of a naiive string matching algorithm. If you replace it by an implementation of Boyer-Moore, you should be able to see significant performances increases.
Apart from that you are heavily dependent on good compiler optimizations. By returning the string instead of passing a string* result, you cause unnecessary copying of the result. However, compilers may help you there. But just to be sure you can alos try passing a pointer to the result as argument and appending to that string. However, swapping std::search for an implementation of a non trivial algorithm (boyer-moore as mentioned above or knuth-morris-pratt) should have an impact that is larger by several orders of magnitude compared to fine-tuning the return mechanism.
Try this one.
template<class T> inline void Replace(T& str, const T& str1, const T& str2)
{
const T::size_type str2Size(str2.size());
const T::size_type str1Size(str1.size());
T::size_type n = 0;
while (T::npos != (n = str.find(str1, n))) {
str.replace(n, str1Size, str2);
n += str2Size;
}
}
std::string val(L"abcabcabc");
Replace(val, L"abc", L"d");
I found this one to be faster:
typedef std::string String;
String replaceStringAll(String str, const String& old, const String& new_s) {
if(!old.empty()){
size_t pos = str.find(old);
while ((pos = str.find(old, pos)) != String::npos) {
str=str.replace(pos, old.length(), new_s);
pos += new_s.length();
}
}
return str;
}
A comparison with James kanzes replaceAll function:
replaceAll : 28552
replaceStringAll: 33518
replaceAll : 64541
replaceStringAll: 14158
replaceAll : 20164
replaceStringAll: 13651
replaceAll : 11099
replaceStringAll: 5650
replaceAll : 23775
replaceStringAll: 10821
replaceAll : 10261
replaceStringAll: 5125
replaceAll : 10283
replaceStringAll: 5374
replaceAll : 9993
replaceStringAll: 5664
replaceAll : 10035
replaceStringAll: 5246
replaceAll : 8570
replaceStringAll: 4381
Times are calculated in nanoseconds with std::chrono::high_resolution_clock
I would propose an optimization step: make a preliminary pass to check if there is anything to replace at all. If there is nothing to replace, just return and avoid allocating memory and copying.
i tested 3 ways to replace substring
boost_replace_all
regex_replace_all
basic handmade string replace algorithm
void regex_replace_test(std::string &str) {
std::string res = std::regex_replace(str, std::regex(SUBSTRING), REPLACEABLE);
}
void boost_replace_all_test(std::string &str) {
boost::replace_all(str, SUBSTRING, REPLACEABLE);
}
void handmade_replace_test(std::string &str) {
size_t pos = 0;
while ((pos = str.find(SUBSTRING, pos)) != std::string::npos) {
str.replace(pos, SUBSTRING.length(), REPLACEABLE);
pos += REPLACEABLE.length();
}
}
input data info
test to replace SUBSTRING in BASE string.
BASE string length = 48004
SUBSTRING = fdkfgkd
SUBSTRING length = 7
SUBSTRING's quantity in BASE string= 4364
REPLACEABLE = ddddddd
REPLACEABLE length = 7
i got the following results:
regex_replace_test = 14 ms , 14921715 ns
boost_replace_all_test = 4 ms , 4167104 ns
handmade_replace_test = 0 ms , 372201 ns
fastest way is basic handmade substring replace method.

set<string>: how to list not strings starting with given string and ending with `/`?

for example we have in our set:
bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog
bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog
bin/obj/Debug/vc100.idb
bin/obj/Debug/vc100.pdb
So this is what I tried based on this grate answer:
#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>
using namespace std;
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
};
void directory_listning_without_directories_demo()
{
set<string> output;
set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");
std::transform(demo_set.begin(),
demo_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug/"));
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
int main()
{
directory_listning_without_directories_demo();
cin.get();
return 0;
}
This outputs:
CloudServerPrototype/
file1
file2
folder/
folder/folder/
vc100.idb
vc100.pdb
and we are given with bin/obj/Debug/string. We want to cout:
vc100.idb
vc100.pdb
CloudServerPrototype/
How to do such thing?
Quick example of what you want to do.
String.find(): http://www.cplusplus.com/reference/string/string/find/
String.subStr(): http://www.cplusplus.com/reference/string/string/substr/
string str = "bin/obj/Debug/vc100.pdb";
string checkString ("bin/obj/Debug");
// Check if string starts with the check string
if (str.find(checkString) == 0){
// Check if last letter if a "/"
if(str.substr(str.length()-1,1) == "/"){
// Output strating at the end of the check string and for
// the differnce in the strings.
cout << str.substr(checkString.length(), (str.length() - checkString.length()) ) << endl;
}
}
It's not clear with which part of the problem you are stuck, so here is a starter for you.
To get the parts of the strings between "given string" and the final '/' (where present):
std::string get_pertinent_part(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
To insert these parts into a new set (output) to guarantee uniqueness you can use the following:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part);
You may wish to pass given_string into get_pertinent_part(), in which case you'll need to convert it to a functor:
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
//
// ...same code as before...
//
return s.substr(first, count);
}
};
You can then call it this way:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug"));
To output the new set:
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
Sorting the results is left as an exercise.
The easiest way I can think of, using the standard C functions, would be:
char * string1 = "bin/obj/Debug"
char * string2 = "bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog"
char result[64];
// the above code is just to bring the strings into this example
char * position = strstr(string1, string2);
int substringLength;
if(position != NULL){
position += strlen(string2);
substringLength = strchr(position, '/') - position;
strncpy(result, position, substringLength);
}else{
strcpy(result, string1); // this case is for when your first string is not found
}
cout << result;
The first thing that occurs, is finding the substring, string1, in the string we are analyzing, being string2. Once we found the starting point, and assuming it was there at all, we add the length of that substring to that starting point using pointer arithmatic, and then find the resulting string's length by subtracting the starting position from the ending position, which is found with strchr(position, '/'). Then we simply copy that substring into a buffer and it's there to print with cout.
I am sure there is a fancy way of doing this with std::string, but I'll leave that to anyone who can better explain c++ strings, I never did manage to get comfortable with them, haha