I'm trying to implement a function that deletes a character from a string wherever the current index is. Below is a skeleton of what I have so far. I'm trying to rotate the character I want to remove to the end of the string then replace it with a null terminator. The code I have below does not seem to actually rotate buffer because the output I'm getting is "wor" instead of the expected output "wrd".
int main() {
char buffer[]="word";
int currIndex=2;
int endIndex=strlen(buffer);
currIndex--;
endIndex--;
rotate(buffer+currIndex,
buffer+1,
buffer+strlen(buffer));
buffer[endIndex]='\0';
cout << buffer << endl;
return 0;
}
This doesn't attempt to answer the question being asked, but rather solve the underlying problem: Removing a single character from a string.
The solution is a simple application of the std::string::erase class member:
#include <string>
#include <iostream>
int main() {
std::string word{ "word" };
std::string::size_type currIndex{ 2 };
word.erase( currIndex, 1 );
std::cout << word << std::endl;
}
Using a std::string makes things way easier because I don't have to think about pointers:
std::string buffer="word";
rotate(buffer.begin()+1, buffer.begin()+2, buffer.end());
buffer.resize(buffer.size()-1);
Demo
Alternatively, we can stick with a c-style array:
char buffer[]="word";
rotate(buffer+1, buffer+2, buffer+4);
buffer[3] = '\0';
Demo2
std::rotate accepts 3 arguments:
template< class ForwardIt >
ForwardIt rotate( ForwardIt first, ForwardIt n_first, ForwardIt last );
first is the first element in the range you want to left rotate.
nfirst is the element you want to be at the start of the range after you've rotated (this tells the algorithm how many times to left rotate, effectively)
last is the last element in the range you want to rotate.
Your code:
char buffer[]="word";
int currIndex=2;
int endIndex=strlen(buffer);
currIndex--;
endIndex--;
rotate(buffer+currIndex,
buffer+1,
buffer+strlen(buffer));
buffer[endIndex]='\0';
Was actually really close. You just got the second argument wrong. It should have been
rotate(buffer+currIndex,
buffer+2,
buffer+strlen(buffer));
buffer[endIndex]='\0';
But the code was admittedly a bit confusing written with the increments and decrements.
Related
I have written a String_numput<> facet that derives from the num_put<> facet, in order to write to a string.
The program is based on an example given by Stroustrup:
/// A num_put<> facet specialization that writes to a string
template<typename C>
class String_numput : public std::num_put<
C,
typename std::basic_string<C>::iterator>
{
public:
String_numput() :
/// this facet won't go into a locale;
/// it has a manually controlled lifetime
std::num_put<C, typename std::basic_string<C>::iterator> {1}
{
}
};
It is tested as follows:
using namespace std;
string s {};
void test(long i,
string& s,
int pos)
{
String_numput<char> f;
/// Format i into s at position pos;
/// use cout's formatting rules
f.put(s.begin() + pos, cout, ' ', i);
cout << s;
}
int main()
{
test(4567.9, s, 0);
cout << "completed" << endl;
}
http://coliru.stacked-crooked.com/a/f4e8386682471e7d
However, nothing is written to the string. The O/P is:
completed
What seems to be the problem here?
Thanks.
f.put(s.begin() + pos, cout, ' ', i);
the first argument is supposed to be an output iterator, but you give it the begin() of an empty string.
This has two problems:
it is illegal: it will overwrite the end of the empty [begin,end) range of s, so you're probably trampling some random memory, and
and it doesn't expand the string, so s.size() stays zero, so cout << s will insert zero characters of your damaged range.
You need a back_inserter instead of a string::iterator - this will actually append to your string correctly.
Note that std::num_put has two template parameters, and the second is the type expected for the iterator argument to put.
You're explicitly setting it to std::basic_string<C>::iterator, but if you change the iterator you pass to put, you need to change this second template parameter to match its type.
Based on an answer elsewhere, I have designed the solution as follows:
1) String_numput : will write a numeric to a string at a specified position. The string must be large enough to accommodate the numeric.
2) String_numapp : will append a numeric to a string using a back_inserter.
The solution is working.
I'm trying to write a short and stupid equation parser, and need to split a string around a given operator. I can split off the right side of a string by doing
return std::string(oprtr + 1, equ.end());
where equ is the string, and oprtr is an iterator for the position I need to split from. Doing this works perfectly, but splitting off the left, however, doesn't:
return std::string(equ.begin(), oprtr - 1);
====
terminate called after throwing an instance of 'std::length_error'
what(): basic_string::_S_create
I've tried a variety of other nasty workarounds that I'm really not proud of, like
return equ.substr(0, std::distance(equ.begin(), oprtr));
This one doesn't give errors, but actually just returns the entire equation. What am I doing wrong here?
Works for me with g++ 4.8.2:
#include <string>
#include <algorithm>
#include <iostream>
int main() {
std::string eq("a+b=c");
std::string::iterator opit = std::find(eq.begin(),eq.end(),'=');
std::string lhs = std::string(eq.begin(),opit);
std::cout << "lhs: " << lhs << "\n";
return 0;
}
The output is:
lhs: a+b
Seems you are doing something like this
void my_func(string equ, string::iterator oprtr)
{
string left = std::string(equ.begin(), oprtr);
}
string::iterator oprtr = equ.find('=');
my_func(equ, oprtr);
That won't work because in my_func you have two iterators to different strings. Because the original string is copied when you call my_func.
One fix is to pass by reference
void my_func(string& equ, string::iterator oprtr)
Another fix is to use integers instead of iterators. Integers aren't tied to one particular string instance like iterators are.
I am trying to find the difference in my code when I use std::find.
For my test code. I made a Vector called Test
std::vector<const char*> Test;
To test the find function, I filled the Test vector with dummy data by using push_back function
Test.push_back("F_S");
Test.push_back("FC");
Test.push_back("ID");
Test.push_back("CD");
Test.push_back("CT");
Test.push_back("DS");
Test.push_back("CR");
Test.push_back("5K_2");
Test.push_back("10K_5");
Test.push_back("10K_1");
Test.push_back("10K_2");
Test.push_back("10K_3");
Test.push_back("10K_4");
Test.push_back("10K_5");
What I want to do with the find function is to go through the Test and see if there are any repeated data. The first time a encounter the data, I will save it to a vector called Unique_Data.
std::vector<const char*> Unique_Data;
So for the 14 data points above, only 13 will be saved because 10K_5 repeated.
The Code I am using looks like this
for(int i = 0; i < Test.size(); i++)
{
if( Unique_Data.empty())
{
Unique_Data.push_back(Test[i]);
}
else if (std::find(Unique_Data.begin(), Unique_Data.end(), Test[i]) != Unique_Data.end())
{
// Move on to next index
}
else
{
Unique_Data.push_back(Test[i]);
}
}
The problem I am having is when I am using the dummy data. I am getting a correct answer for Unique_Data.
However, if I save the actual data into the Test vector which are saved in linked list. I get that they are all unique.
The code looks like this
p_curr = List.p_root;
while(p_curr != NULL)
{
// id starts from 0
if(atoi(p_curr->id) == 14) break;
Test.push_back(p_curr->Descriptor);
p_curr = p_curr->p_next;
}
I tested with the same 14 data. They are all const char* types. However, when I used the linked list data. The find function thinks all the data is unique.
Can anyone tell me what is wrong with this?
Using C-style strings is a bit tricky, they are just a pointer, and pointers are compared by identity. Two C strings with the same sequence of characters, but different addresses will compare different.
const char first[] = "Hi";
const char second[] = "Hi";
assert(first == second); // will fail!
There are two solutions to this problem. The simple one is using std::string in your container, as std::string will provide value comparisons. The alternative is to pass a comparison functor to std::find as a last argument. But this will still leave the problem of managing the lifetime of the const char*-s stored in the vector.
This is a pointers problem. You're not storing strings in your array, you're storing the memory address of the data in the string.
This strange behaviour is probably because in your example case you have literal strings that cannot be changed, so the compiler is optimising the storage, and when two strings are the same then it stores the same address for all strings that have the same text.
In your real data example, you have a bunch of strings that hold the same data, but each of these strings lives at a different memory address, so the find function is saying that all strings have a different address.
In summary, your find function is looking at the memory address of the string, not the data (text) in the string. If you use std::strings then this problem will disappear.
I would highly recommend using strings, as performance is going to be more than good enough and they eliminate a vast number of problems.
As David Rodriguez mentions in his answer, you're only comparing pointers, and not the contents of the strings themselves. Your solution will work as is if you were storing std::strings instead of char const *. With the latter, you need to resort to std::find_if and a predicate that calls strcmp to determine whether the strings are identical.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
int main()
{
std::vector<const char*> Test;
Test.push_back("F_S");
Test.push_back("FC");
Test.push_back("ID");
Test.push_back("CD");
Test.push_back("CT");
Test.push_back("DS");
Test.push_back("CR");
Test.push_back("5K_2");
Test.push_back("10K_5");
Test.push_back("10K_1");
Test.push_back("10K_2");
Test.push_back("10K_3");
Test.push_back("10K_4");
Test.push_back("10K_5");
std::vector<const char*> Unique_Data;
for(auto const& s1 : Test) {
if(std::find_i(Unique_Data.cbegin(), Unique_Data.cend(),
[&](const char *s2) { return std::strcmp(s1, s2) == 0; })
== Unique_Data.cend()) {
Unique_Data.push_back(s1);
}
}
for(auto const& s : Unique_Data) {
std::cout << s << '\n';
}
}
Here's a live example
I am wondering, if anyone could explain to me the second argument in the vector.insert() method:
iterator insert (iterator position, const value_type& val);
For example, I have a vector of type wstring and I would like to insert a wstring at a given position. I have figured out how to set the position using an iterator:
wstring word = "test";
int insertion_pos = 3;
iterator it = words.begin();
words.insert( it + insertion_pos, word );
But what about that second argument? How can I pass a wstring object to the insert() method?
Thanks a lot.
Cheers,
Martin
Full example:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <wchar.h>
#include <vector>
using namespace std;
int main(void) {
// Initialize the vecor with three words.
vector<wstring> words;
wstring word1 = "FirstWord"; // Error msg: no viable conversion from 'const char [10]' to 'wstring' (aka
// 'basic_string<wchar_t>')
wstring word2 = "SecondWord"; // same here
wstring word3 = "ThirdWord"; // same here
words.push_back(word1);
words.push_back(word2);
words.push_back(word3);
// Now try to insert a new word at position 2 (i.e. between "SecondWord "and "ThirdWord"
int position = 2;
wstring word4 = "InsertThis"; // same error as above
iterator it = words.begin(); // Error: use of class template iterator requires template
// arguments
words.insert( it + position, word4 );
// Invalid arguments ' Candidates are: __gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>
// insert(__gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>,
// const std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>> &) void
// insert(__gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>,
// unsigned long int, const std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>> &) void
// insert(__gnu_cxx::__normal_iterator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
// *,std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>>>,
// #10000, #10000) '
return EXIT_SUCCESS;
}
Thanks for the clear example of the issue. Here is a modified version with some comments about changes. It compiles for me using clang on Mac OS X.
One change is the "L" in front of the string literal. This is an indicator that the string literal to follow is of type wchar_t. See also this.
Wide character/unicode/utf support is something I would add only if it's needed in the problem you're trying to solve.
// #include <stdio.h> prefer "cstdio" to stdio.h; not used in example
// #include <stdlib.h> same
#include <iostream>
#include <string>
// #include <wchar.h> not used in example
#include <vector>
using namespace std;
// simplify to main()
int main() {
// Initialize the vecor with three words.
vector<wstring> words;
wstring word1(L"FirstWord"); // Use Constructor, no assignment operator=
wstring word2(L"SecondWord");
wstring word3(L"ThirdWord");
words.push_back(word1);
words.push_back(word2);
words.push_back(word3);
int position = 2;
wstring word4(L"InsertThis");
// iterator depends on type of container
vector<wstring>::iterator it = words.begin();
words.insert( it + position, word4 );
for (const std::wstring& w : words)
std::wcout << w << " ";
std::wcout << std::endl;
return EXIT_SUCCESS;
}
Understanding the insert call
The prototype for the vector's insert member function is:
iterator insert( iterator pos, const T& value );
where T is the type you give as a template parameter, i.e. std::wstring in this case.
Iterators have operator+ overloaded, with the following semantics: iterator it + integer 2 returns a new iterator with a position 2 "increments" past the iterator it.
words.insert( it + position, word4 );
Suggestion
One thing to be careful about it how you determine an inerstion position.
I think it would be better practice (more maintainable) to use an iterator to "walk along" the vector, rather than using iterator+offset. If you're not very comfortable with iterators, this would be a chance to learn how to use them.
This would avoid a potential situation, discussed in a previous version of this answer, where you accidentally offset your iterator past the end of the vector, leading to a segmentation violation.
How can input a word and reverse the output of it. I made a function to calculate the length of the word and from here I have to reverse the word depending on the length of it.
How can I do that?
#include<iostream>
using std::cout;
using std::endl;
using std::cin;
int LengthOfString( const char *); // declaring prototype for length of the string
int reverse(const char []);
int main()
{
char string1[100];
cout<<"Enter a string: ";
cin>>string1;
cout<<"Length of string is "<<LengthOfString(string1);
system("PAUSE");
return 0;
}
int LengthOfString( const char *x)
{
int index;
for(index = 0; *x!='\0';x++,index++);
return index;
}
int reverse(const char y[])
{
/* my attempted loop, its not right i know.
a[] = *index; // length of the word
for(int i=0; i<=index/2; i++)
for(j=0; j == length, j--) */
}
This wheel has already been invented, and exists in the standard library.
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
std::string word;
std::cout << "Enter a word: ";
std::cin >> word;
std::reverse(word.begin(), word.end());
std::cout << "Reverse: " << word << std::endl;
return 0;
}
To understand exactly what's going on here, there are a few things that you must cover first:
data structures (classes)
containers
iterators
I hope you already know what a class is. In case you're still in introductory stuff, a class is basically a user defined collection of state and behavior. The author can choose to restrict access to the state or behavior of a class for a variety of reasons. In the case of std::string, the standard library string class, all of the state is hidden and only behavior is accessible.
The string class is a container that contains characters. There are numerous other container classes, each of which with different strengths and weaknesses. The string class contains a sequence of characters with a strict order. Other containers exist, such as std::set, std::vector, std::list, and others. std::string bears a passing resemblance to std::vector, and is a distant cousin of std::list. Each collection behaves differently and is suited for different things.
You might think you need to understand how the string class stores its data in order to reverse it, but you don't. This is where iterators come in. std::string owns a typedef, std::string::iterator, which is a special object which stores the location of a single element in a string. std::reverse is a library function which takes 2 iterators and repeatedly swaps their contents and moves them towards each other. This looks like this as it's happening:
v v <-- positions of iterators (start at the start, end at the end)
ABC <-- initial state
v v <-- the end iterator moved back
ABC
v v
CBA <-- the iterators swapped their values
vv <-- the begin iterator moved forward
CBA
V <-- the end iterator moved back; both iterators are in the same place
CBA <-- therefore, we're done, the string is reversed
One thing about iterators is they're kind of like pointers. In fact, you can pass pointers to some functions that expect iterators because they behave syntactically the same. Therefore, you should be able to write your own reverse function that uses pointers that basically does the same thing this did, except with char *s.
Here's some pseudocode that you should be able to write the function with (I won't write it out completely because it's homework):
namespace BaidNation
{
void reverse(char *begin, char *end)
{
loop forever
{
if (end equals begin):
done;
move end backwards;
if (end equals begin):
done;
swap end's and begin's characters;
move begin forwards;
}
}
}
Keep in mind that BaidNation::reverse (as well as std::reverse) expects for end the iterator that references the element AFTER the end of the collection, not the one that references the last element. How does it then make sense to use this?
Your LengthOfString function returns the number of non-null characters in a string. Since arrays are zero-indexed, we know that, like any other array, if we check string1 + LengthOfString(string1), we'll get a pointer to the character after the end which is, for once, exactly what we want.
Thus, we can use this to reverse the string:
BaidNation::reverse(string1, string1 + LengthOfString(string1));
If you have to use exactly the signature earlier, you can adapt this design into the other one:
int reverse(const char str[])
{
char *start = str, *end = str + LengthOfString(str);
BaidNation::reverse(start, end);
}
Based on the fact that the return type of your prototype function is int, it looks to me like you want to do an in-place reversal of a string. You first need to find out how long the string is (although you computed that before, you didn't pass the result to this function), then swap elements until you get to the middle. To make this work you need to pass, not a const char[], but just a char* (indicating that you will be changing the content):
int reverse(char* y)
{
int ii, n;
n = LengthOfString(y); // "no built in functions - otherwise, use strlen()
for(ii=0; ii<n/2;ii++) {
char temp;
temp = y[ii];
y[ii] = y[n - ii - 1];
y[n - ii] = temp;
}
}
Declare a new char* of the same length, and then loop as follows -
for(int i=0;i<stringLength;i++){
newString[i]=oldString[stringLength-i];
}
return newString;
Also you might want to consider using the String class instead of char*.