I am trying to check if char exists in vector of chars, and if so, to get it`s number. I have done the first part (check if char exists):
char letter(a);
string word;
vector<char>vWord(word.begin(), word.end());
if(find(vWord.begin(), vWord.end(), letter) != vWord.end()){}
But I have no idea how to get the position. Any help is appreciated.
Save the iterator and do some math on it:
vector<char>::iterator itr = find(vWord.begin(), vWord.end(), letter);
if(itr != vWord.end())
{
int index = itr - vWord.begin();
}
However do note that std::string already has a find method.
You're almost there. You already have an iterator pointing to that character (returned by find), so you can use std::distance to find the distance:
char letter(a);
string word;
vector<char>vWord(word.begin(), word.end());
auto it = find(vWord.begin(), vWord.end(), letter);
if (it != vWord.end())
{
size_t index = std::distance(vWord.begin(), it);
}
For random-access iterators (such as those used by std::vector), std::distance(a, b) is a constant-time operation and is implemented by doing b - a.
Side note: you can do std::find and iterator operations on std::string directly; it's a perfectly fine container in its own right.
Related
I am trying to validate a single-line input string in C++11 to see if it contains any leading / trailing whitespaces. My code now looks like this:
bool is_valid(const std::string& s) {
auto start = s.begin();
auto end = s.end();
if (std::isspace(*start) || std::isspace(*end)) {
return false;
}
return true;
}
int main() {
std::string name{};
std::getline(std::cin, name);
if (!is_valid(name)) {
std::cout << "Invalid!";
}
return 0;
}
But now the program can only detect leading whitespaces. For example, for John it would print Invalid! but for Mary it would classify it as valid input, which is not. Does anyone know what's wrong with my program?
A simple test for std::string::front() and std::string::back() could have been done after testing for the empty string:
bool is_valid(const std::string& s)
{
return s.empty() ||
(!std::isspace(static_cast<unsigned char>(s.front())) &&
!std::isspace(static_cast<unsigned char>(s.back())));
}
The end iterator does not point to an element in the container. It points one past the last element. You may not dereference the end iterator. For a std::string you can use it's operator[]:
char last_char = s[s.size()-1];
advance the begin iterator:
auto it = s.begin() + s.size()-1;
char last_char = *it;
or decrement the end iterator:
auto it = s.end() -1;
char last_char = *it;
Other alternatives are back() or using the reverse iterator rbegin().
Note that they all require s.size() != 0. For an empty string s.begin() == s.end(). You should check that first in the function and return true for that case.
s.end() is one pass the end of the string just like any other containers in C++, so accessing it invokes undefined behavior. You need to use std::prev(s.end()) instead (which is valid only the string contains at least 1 character though, so you need to check the string length first)
.end is used to get an iterator to past the last element. You can use std::string::rbegin to get the last element.
auto end = s.rbegin();
NB: std::string::starts_with and std::string::ends_with are available from C++20.
Yes, .end() is to the past-the-end element. Then why not using .back() instead?
bool is_valid(std::string const& str) {
return str.empty() || !(std::isspace(str.front()) || std::isspace(str.back()));
}
I am trying to store some specific characters of a string in a vector. When I want to push back the characters though, there is a problem with the value and I do not know why. Going over the cpp reference page didn't help me unfortunately. Could someone please help?
Here is the code:
int main()
{
std::string str1 = "xxxGGxx$xxxGxTxGx";
std::vector<std::string> vec;
std::vector<std::string>::iterator it;
for(auto &ch:str1)
{
if(ch == 'G' || ch == '$' || ch == 'T')
{
vec.push_back(ch); //Problem: ch not accepted
}
}
for(it = vec.begin(); it!=vec.end(); it++)
{
std::cout << *it;
}
}
Vector needs to be of type char, not string.
The reason vec.push_back(ch); does not work is because vec is a vector of strings and not a vector of char. push_back() only accepts the type stored inside the vector, in this case string. A vector<int> cannot push_back a string because a string can't be implicitly converted to an int. It has to somehow be converted to an int first then pushed back. Since a char can't be implicitly converted into a string the compiler gets confused and thinks its impossible.
There are two simple alternatives:
Instead of using vector<string> use vector<char> that way push_back() will append characters.
vector<char> vec;
char ch = 'a';
vec.push_back(ch); // both these work
vec.push_back('b'); // both these work
Or, Convert your char into a string and then call push_back onto your string.
string str(1, ch); // Creates a string containing 1 character equal to ch
vec.push_back(str); // Push back our string
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I was looking for replacing a character in a string with a character in another string comparing to another string.
Well, hard to describe this, a code would be better :
std::string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
std::string keyboard = "AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn";
std::string change(std::string str)
{
for (short i = 0; i < alphabet.length(); i++)
{
std::cout << str[i] << std::endl;
}
std::cout << str << std::endl;
system("PAUSE");
return str;
}
Well, I don't think it is understandable, so what I try to do is :
Find (maybe Find() ?) the letter in str and get the i-variable in alphabet.
With the i-variable want to get the pos and then get the letter in the keyboard string.
Hope you understand what I mean, and thank you.
I though about replace(), find() and swap(), but didn't work, I maybe did something wrong.
Thank you in advance !
EDIT : Well, I was sure you won't understand anything, imagine str = "Hello", I want to replace H per Y (alphabet = H -> keyboard = I)? (alphabet get the position of the H in the alphabet string, we memorise this position in i, and we keyboard[i]), this is what I want to do.
I propose
template <typename InputIterator,
typename BidirIterator,
typename ForwardIterator>
InputIterator substitute( InputIterator first, InputIterator last,
BidirIterator key_first, BidirIterator key_last,
ForwardIterator val_first )
{
for (; first != last; ++first)
{
auto&& ref = *first;
auto i = std::lower_bound( key_first, key_last, ref );
if (i == key_last || *i != ref)
return first;
ref = *std::next(val_first, std::distance(key_first, i));
}
return last;
}
This assumes that [key_first, key_last) is sorted and that val_first refers to a range that is at least as long as [key_first, key_last).
Demo.
The idea is that we iterate through all elements of a range using iterators whose type is to be determined - i.e. we can use both char const[]s and std::vector<char>s. We pick the current element and use a binary search to find that element within the key set. Then we use the offset to access the substitution counterpart in the value set, and assign that to our original element.
We use lower_bound to do the binary search. It doesn't itself constitute one, so we have to apply some checks to the return value to ensure the iterator refers to the correct value. If we couldn't find the key then the iterator to the first non-working element is returned. (That can be the past-the-end iterator.)
The avove is a general method that works for arbitrary key/value sets: For alphabetic shuffling there are (platform dependent) methods using lookup tables. Rustically this could look like this:
template <typename InputIterator>
void substitute( InputIterator first, InputIterator last,
char const* values )
{
char lookup[UCHAR_MAX+1];
for (int c = 0; c != sizeof lookup; ++c)
if (std::isupper(c))
lookup[c] = values[c - 'A'];
else if (std::islower(c))
lookup[c] = values[c - 'a' + 26];
else
lookup[c] = c;
// Further adjustments to the table can be made here
std::transform( first, last, first,
[&] (unsigned char c) {return lookup[c];} );
}
Demo. It depends on stuff that normal character sets should easily suffice. The table can also be created elsewhere and passed to transform via the unwrapped one-liner. This is especially efficient for large string transformations as no branching is necessary in the implicit loop in transform.
So, you want to replace every char in your string with a char from keyboard string using original char's position in alphabet?
I would consider using a map alphabet char to keyboard char. Then iterate through string and replace its characters.
#include <string>
#include <algorithm>
#include <map>
int main(int argc, char* argv[])
{
std::map<char, char> alphabetToKeyboard;
alphabetToKeyboard['A'] = 'A';
alphabetToKeyboard['B'] = 'Z';
alphabetToKeyboard['C'] = 'E';
std::string str = "ABC";
std::transform(str.begin(), str.end(), str.begin(), [&alphabetToKeyboard](char c) {
return alphabetToKeyboard[c];
});
return 0;
}
I have a string, like e.g. acaddef or bbaaddgg. I have to remove from it, as fast as possible, all repeating characters. So, for example, pooaatat after should look like poat and ggaatpop should look like gatpo. Is there any built-in function or algorithm to do that quickly? I tried to search STL, but without satisfaing result.
Okay, so here are 4 different solutions.
Fixed Array
std::string str = "pooaatat";
// Prints "poat"
short count[256] = {0};
std::copy_if(str.begin(), str.end(), std::ostream_iterator<char>(std::cout),
[&](unsigned char c) { return count[c]++ == 0; });
Count Algorithm + Iterator
std::string str = "pooaatat";
// Prints "poat"
std::string::iterator iter = str.begin();
std::copy_if(str.begin(), str.end(), std::ostream_iterator<char>(std::cout),
[&](char c) { return !std::count(str.begin(), iter++, c); });
Unordered Set
std::string str = "pooaatat";
// Prints "poat"
std::unordered_set<char> container;
std::copy_if(str.begin(), str.end(), std::ostream_iterator<char>(std::cout),
[&](char c) { return container.insert(c).second; });
Unordered Map
std::string str = "pooaatat";
// Prints "poat"
std::unordered_map<char, int> container;
std::copy_if(str.begin(), str.end(), std::ostream_iterator<char>(std::cout),
[&](char c) { return container[c]++ == 0; });
AFAIK, there is no built-in algorithm for doing this. The std::unique algorithm is valid if you want to remove only consecutive duplicate characters.
However you can follow the following simple approach:
If the string contains only ASCII characters, you can form a boolean array A[256] denoting whether the respective character has been encountered already or not.
Then simply traverse the input string and copy the character to output if A[character] is still 0 (and make A[character] = 1).
In case the string contains arbitrary characters, then you can use a std::unordered_map or a std::map of char to int.
Built-in regular expressions should be efficient, i.e.
#include <regex>
[...]
const std::regex pattern("([\\w ])(?!\\1)");
string s = "ssha3akjssss42jj 234444 203488842882387 heeelloooo";
std::string result;
for (std::sregex_iterator i(s.begin(), s.end(), pattern), end; i != end; ++i)
result.append((*i)[1]);
std::cout << result << std::endl;
Of course, you can modify the cpaturing group to your needs.
The good thing is that it is supported in Visual Studio 2010 tr1 already. gcc 4.8, however, seems to have a problem with regex iterators.
I wrote a function to check whether a word is palindrome or not but "unexpectedly", that function failed quite badly, here it is:
bool isPalindrome (const string& s){
string reverse = "";
string original = s;
for (string_sz i = 0; i != original.size(); ++i){
reverse += original.back();
original.pop_back();
}
if (reverse == original)
return true;
else
return false;
}
It gives me "string iterator offset out of range error" when you pass in a string with only one character and returns true even if we pass in an empty string (although I know its because of the intialisation of the reverse variable) and also when you pass in an unassigned string for example:
string input;
isPalindrome(input);
Later, I found a better function which works as you would expect:
bool found(const string& s)
{
bool found = true;
for (string::const_iterator i = s.begin(), j = s.end() - 1; i < j; ++i, --j) {
if (*i != *j)
found = false;
}
return found;
}
Unlike the first function, this function correctly fails when you give it an unassigned string variable or an empty string and works for single characters and such...
So, good people of stackoverflow please point out to me why the first function is so bad...
Thank You.
for (string_sz i = 0; i != original.size(); ++i) {
reverse += original.back();
original.pop_back();
}
original.size() changes as you pop elements off the back. Effectively, you keep incrementing i and decrementing original.size(); they may never be equal.
if (reverse == original)
This will never be true since you've just removed all of the elements from original and added them in reverse order to reverse. original will always be empty at this point.
You're found function could very well rely on the STL std::compare function and on the begin()/end() rbegin()/rend() functions of the string. and could be a one line function :
return std::equal(s.begin(), s.end(), s.rbegin());
The std::equal() function compares two ranges of the same length.
The begin()/end() functions provide forward iterators while rbegin() provides a reverse iterator, ie an iterator that starts at the end of the string and goes to the beginning.
This is probably not what you want, but reverse is already implemented as an algorithm in STL:
bool isPalindrome( const std::string & str )
{
std::string rev( str );
std::reverse( rev.begin(), rev.end() );
return str==rev;
}
As #James McNellis points out, this can be further condensed (without needing any algorithm) by constructing the reversed string directly with reverse iterators on the original string:
bool isPalindrome( const std::string & str )
{
return str == std::string( str.rbegin(), str.rend() );
}
The loop that purports to reverse the string doesn't in fact do so. As you're removing items from the list, you're also incrementing i. In some cases I imagine it's possible for i to skip past the current size and iterate forever.
Instead of your loop, you can use reverse:
std::reverse(original.begin(), original.end());
And then do the rest of the work. It's up to your requirements if an empty string is a palindrome or not.
Your solutions are far too complicated ;)
bool is_palindrome(std::string const& s) {
if (s.empty()) return false; // if this is required.
return !lexicographical_compare(s.begin(), s.end(), s.rbegin(), s.rend());
}
Edit: Or, as Etienne noted, just use std::equal ...
bool is_palindrome(std::string const& s) {
if (s.empty()) return false; // if this is required.
return equal(s.begin(), s.end(), s.rbegin());
}