Replace string in a vector of structs C++ - c++

I'm new to C++ and I got stuck.
I have a problem replacing | with ,. I have no problem finding |, but replace function doesn't seem to work. What am I doing incorrectly? Any help appreciated.
Header File:
struct Document
{
string text;
int NumLines;
};
struct Find {
const string text;
Find(const string& text) : text(text) {}
bool operator()(const Document& j) const {
return j.text == text;
}
};
class DataRecord
{
private:
vector <Document> field;
public:
void Replace();
}
cpp. file for this function
void DataRecord::Replace ()
{
vector<Document>::iterator it = replace(field.begin(),field.end(),Find("|"),"," );
}

What you are attempting isn't clear, but if all you want to do is replace all the "|" for "," in every Document in field, the simplest approach might be a loop:
for (auto& f : field) :
std::replace(f.text.begin(), f.text.end(), '|', ',');

If I understand correctly, you are trying to replace documents in the sequence fields with a string literal. This does not work.
std::replace semantics:
std::replace(It begin, It end, Predicate P, Value v)
where:
*begin (and any element in the sequence [begin, end)) yields a value of type Value.
Predicate has the semantics P(const Value&) -> bool.
v in the value that should be set instead of the elements matching the predicate.
In your case, the fourth argument (v) should be of type Document, not a string literal.
You should create a document instance that specifies what should replace the Document instances matching the predicate (because you cannot replace them with string instances or string literals).
Edit: Alternately, you could add an implicit Document constructor that creates an instance from a string, but creating implicit constructors like that is usually a bad idea.

Related

Is it possible to construct a modifiable view of portion in a string?

I have a match table with start and end indices of portions, in array (in a callback) - I wrap that array into vector of strings - now recently I did have the need to modify the original portions of the string.
struct regexcontext {
std::vector<std::optional<std::string>> matches;
std::string subject;
};
int buildmatchvector(size_t(*offset_vector)[2], int max, regexcontext* pcontext) {
pcontext->matches.clear();
ranges::transform(ranges::span{ offset_vector, max }, std::back_inserter(pcontext->matches), [&](const auto& refarr) {
return refarr[0] == -1 ? std::optional<std::string> {} : std::optional<std::string>{ pcontext->subject.substr(refarr[0], refarr[1] - refarr[0]) };
});
return 0;
}
Is it possible to change the above definition in a way that by modifying the match vector I will modify the subject string as well.
I've heard of string view but I've also heard it can't be modified with a variable sized string.
Note I'm using ranges-v3 which is the only library that implements standard ranges at the moment plus the nonstandard ranges::span which allows me to compile on msvc (since std::span doesn't work there for some reason).
As long as you only need to change characters to others, but not add or remove characters, then you could use a vector of span. Supporting addition or removal would be much more complicated and I don't think there's any simple solution in the standard library. Example:
return refarr[0] == -1
? span<char> {}
: span<char> {
&pcontext->subject[refarr[0]],
refarr[1] - refarr[0]
};
Note that any invalidating operation on the pointed string would invalidate these spans, so it would be a good idea to make the string private.

String_numput<> facet not writing to string

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.

std::regex escape special characters for use in regex

I'm string to create a std::regex(__FILE__) as part of a unit test which checks some exception output that prints the file name.
On Windows it fails with:
regex_error(error_escape): The expression contained an invalid escaped character, or a trailing escape.
because the __FILE__ macro expansion contains un-escaped backslashes.
Is there a more elegant way to escape the backslashes than to loop through the resulting string (i.e. with a std algorithm or some std::string function)?
File paths can contain many characters that have special meaning in regular expression patterns. Escaping just the backslashes is not enough for robust checking in the general case.
Even a simple path, like C:\Program Files (x86)\Vendor\Product\app.exe, contains several special characters. If you want to turn that into a regular expression (or part of a regular expression), you would need to escape not only the backslashes but also the parentheses and the period (dot).
Fortunately, we can solve our regular expression problem with more regular expressions:
std::string EscapeForRegularExpression(const std::string &s) {
static const std::regex metacharacters(R"([\.\^\$\-\+\(\)\[\]\{\}\|\?\*)");
return std::regex_replace(s, metacharacters, "\\$&");
}
(File paths can't contain * or ?, but I've included them to keep the function general.)
If you don't abide by the "no raw loops" guideline, a probably faster implementation would avoid regular expressions:
std::string EscapeForRegularExpression(const std::string &s) {
static const char metacharacters[] = R"(\.^$-+()[]{}|?*)";
std::string out;
out.reserve(s.size());
for (auto ch : s) {
if (std::strchr(metacharacters, ch))
out.push_back('\\');
out.push_back(ch);
}
return out;
}
Although the loop adds some clutter, this approach allows us to drop a level of escaping on the definition of metacharacters, which is a readability win over the regex version.
Here is polymapper.
It takes an operation that takes and element and returns a range, the "map operation".
It produces a function object that takes a container, and applies the "map operation" to each element. It returns the same type as the container, where each element has been expanded/contracted by the "map operation".
template<class Op>
auto polymapper( Op&& op ) {
return [op=std::forward<Op>(op)](auto&& r) {
using std::begin;
using R=std::decay_t<decltype(r)>;
using iterator = decltype( begin(r) );
using T = typename std::iterator_traits<iterator>::value_type;
std::vector<T> data;
for (auto&& e:decltype(r)(r)) {
for (auto&& out:op(e)) {
data.push_back(out);
}
}
return R{ data.begin(), data.end() };
};
}
Here is escape_stuff:
auto escape_stuff = polymapper([](char c)->std::vector<char> {
if (c != '\\') return {c};
else return {c,c};
});
live example.
int main() {
std::cout << escape_stuff(std::string(__FILE__)) << "\n";
}
The advantage of this approach is that the action of messing with the guts of the container is factored out. You write code that messes with the characters or elements, and the overall logic is not your problem.
The disadvantage is polymapper is a bit strange, and needless memory allocations are done. (Those could be optimized out, but that makes the code more convoluted).
EDIT
In the end, I switched to #AdrianMcCarthy 's more robust approach.
Here's the inelegant method in which I solved the problem in case someone stumbles on this actually looking for a workaround:
std::string escapeBackslashes(const std::string& s)
{
std::string out;
for (auto c : s)
{
out += c;
if (c == '\\')
out += c;
}
return out;
}
and then
std::regex(escapeBackslashes(__FILE__));
It's O(N) which is probably as good as you can do here, but involves a lot of string copying which I'd like to think isn't strictly necessary.

How should I iterate this map according to a given string and then append its value?

Edited
Why I'm asking...
Yesterday, I started a project to create a Morse code translator which creates a file or appends to an existing file, translated Morse code from a given string or from the file text given.
Mainly,I have no idea in hell how to get this map to work with the string in which I want to return and I feel as if I've tried everything I can Google or read in documentation.
additionally...
I've left my horrendous attempt at iterating through the data structures , this time using vectors, having exhausted tries with map methods. I'm sure I'm missing simple syntx with the map structure but I left the last attempt up because I believe it conveys my intention quite clearly due to its baroque nature.
So to be more specific, what's the best way to access this map and return it through this function.
initial design
getTranslation()
/* #brief: Program returns string which is a translation
* of the Rvalue string which it takes as a argument
* #param text: string of letters, numbers and some symbols to be translated
* #return translation: translated string appended with map values
*/
string getTranslation (const string&& text) noexcept(true)
{
//return value
auto translation = "";
map <string,string> morseKey;
morseKey ["A"] = ".-";
morseKey ["B"] = "-...";
morseKey ["C"] = "-.-.";
morseKey ["D"] = "-...";
//...
// I was going to attempt to
// unpack to vectors then compare as vectors of
// strings because of consistent issues with
// type safety errors
// i've tried iterating over it differently
// but this is last hope code here
// any help on how to accomplish this in
// a better way but still retain the
// use of a map because of its ability
//to hold all sorts of characters
//would be greatly appreciated
/*
vector <string> mSymbol;
for (auto itr : morseKey)
{
mSymbols.push_back(itr.first);
}
vector <string> vText;
for (auto itr : text)
{
vText.push_back(itr);
}
for (int i = 0; i < text.length(); i++)
{
if (vText[i] == mSymbol[i])
{
translation += morseKey.at(i);
}
}
*/
translation = "*SCAFFOLDING* FUNCTION NOT COMPLETE";
return translation;
}
Edit:
Wow, Iv'e received some really good input and I believe that my issues are rooted in the fact that using auto caused translation to be as a const char* which wouldn't allow me to make my map a map std::map<char,string> morseKey. Also my Rvalue cast via move was apparently unnecessarily (I had a feeling). So I'm going to implement the knowledge I've gained from and post that before I mark my answer.
Edit 2
I removed the auto and 'translation' is now declared as a string
getTranslation's signature takes a const string&
I initialize morseKey as
static map <char,string> const morseKey = {{'A', ".-"},...
but get the compiler error of
'invalid conversion from ‘const char*’ to ‘const char&’
I don't understand why this is, or what makes either a pointer or ref in this situation and therefore how to fix it.
Wow... you practiced a lot of concepts, and you are just learning to code!!!
I'm sure you will be a successful programmer (but you should know it needs a lot more practicing!)
But about your "getTranslation" function, I changed it a little to:
#include <algorithm>
#include <cctype>
/*...*/
string getTranslation (string text) noexcept(true)
{
map <char,string> morseKey;
morseKey ['A'] = ".-";
morseKey ['B'] = "-...";
morseKey ['C'] = "-.-.";
/*...*/
string translation {""};
std::transform(text.begin(), text.end(), text.begin(), toupper);
for (it: text){
translation += morseKey[it];
}
return translation;
}
As you may know, map is an associative array; it means you don't need to iterate over all of its element to find your interesting element. You should associate a key to its corresponding record. In your case you should associate a char (not a string) to a string; so you should define your "morseKey" as:
map <char, string> morseKey;
and when you want to associate a character such as 'A' to ".-" you should do something like:
morseKey ['A'] = ".-"; /*instead of morsKey["A"] = ".-" */;
also when you used "auto" in defining your "translation" variable, compiler will consider it as a "const char*". so you should explicitly define your "translation" variable as:
string translation {""};
In addition because our "morseKey" map contains just uppercase of alphabets, we should convert alphabet characters of "text" variable to uppercase. This can be done very easily by:
std::transform(text.begin(), text.end(), text.begin(), toupper);
but for using this command you should include two libraries:
#include <algorithm> /*for "transform" */
#include <cctype> /*for "touppper" */
Also you shouldn't consider "text" variable as rvalue any more (because we modify it) so I change your function delcarion to:
string getTranslation (string text) noexcept(true)
Finally you should just iterate over your "text" variable and find corresponding Morse value of each character and append it to your return value; This also can be done very easily by:
for (it: text){
translation += morseKey[it];
}
Have fun with programming!
My reply for your second edit:
I think your information is not enough; perhaps it's better to ask your question as a new question, and also provide it with more details, such as in which line you got this compile error or any other useful details that you may think it can be helpful.
The function could be defined the following way. ( Note: I would make the map as a global variable in some namespace and initialize it with an array of std::pair(s) ).
std::string getTranslation( const std::string&& text) noexcept(true)
{
std::string translation;
std::map <std::string, std::string> morseKey;
morseKey ["A"] = ".-";
//...
for ( char c : text )
{
c = std::toupper( c );
auto it = morseKey.find( c );
if ( it != morseKey.end() ) translation.push_back( it->second );
}
return translation;
}

How to use std::string as key in stxxl::map

I am trying to use std::string as a key in the stxxl::map
The insertion was fine for small number of strings about 10-100.
But while trying to insert large number of strings about 100000 in it, I am getting segmentation fault.
The code is as follows:
struct CompareGreaterString {
bool operator () (const std::string& a, const std::string& b) const {
return a > b;
}
static std::string max_value() {
return "";
}
};
// template parameter <KeyType, DataType, CompareType, RawNodeSize, RawLeafSize, PDAllocStrategy (optional)>
typedef stxxl::map<std::string, unsigned int, CompareGreaterString, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> name_map;
name_map strMap((name_map::node_block_type::raw_size)*3, (name_map::leaf_block_type::raw_size)*3);
for (unsigned int i = 0; i < 1000000; i++) { /// Inserting 1 million strings
std::stringstream strStream;
strStream << (i);
Console::println("Inserting: " + strStream.str());
strMap[strStream.str()]=i;
}
In here I am unable to identify why I am unable to insert more number of strings. I am getting segmentation fault exactly while inserting "1377". Plus I am able to add any number of integers as key. I feel that the variable size of string might be causing this trouble.
Also I am unable to understand what to return for max_value of the string. I simply returned a blank string.
According to documentation:
CompareType must also provide a static max_value method, that returns a value of type KeyType that is larger than any key stored in map
Because empty string happens to compare as smaller than any other string, it breaks this precondition and may thus cause unspecified behaviour.
Here's a max_value that should work. MAX_KEY_LEN is just an integer which is larger or equal to the length of the longest possible string key that the map can have.
struct CompareGreaterString {
// ...
static std::string max_value() {
return std::string(MAX_KEY_LEN, std::numeric_limits<unsigned char>::max());
}
};
I have finally found the solution to my problem with great help from Timo bingmann, user2079303 and Martin Ba. Thank you.
I would like to share it with you.
Firstly stxxl supports POD only. That means it stores fixed sized structures only. Hence std::string cannot be a key. stxxl::map worked for about 100-1000 strings because they were contained in the physical memory itself. When more strings are inserted it has to write on disk which is internally causing some problems.
Hence we need to use a fixed string using char[] as follows:
static const int MAX_KEY_LEN = 16;
class FixedString {
public:
char charStr[MAX_KEY_LEN];
bool operator< (const FixedString& fixedString) const {
return std::lexicographical_compare(charStr, charStr+MAX_KEY_LEN,
fixedString.charStr, fixedString.charStr+MAX_KEY_LEN);
}
bool operator==(const FixedString& fixedString) const {
return std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
}
bool operator!=(const FixedString& fixedString) const {
return !std::equal(charStr, charStr+MAX_KEY_LEN, fixedString.charStr);
}
};
struct comp_type : public std::less<FixedString> {
static FixedString max_value()
{
FixedString s;
std::fill(s.charStr, s.charStr+MAX_KEY_LEN, 0x7f);
return s;
}
};
Please note that all the operators mainly((), ==, !=) need to be overriden for all the stxxl::map functions to work
Now we may define fixed_name_map for map as follows:
typedef stxxl::map<FixedString, unsigned int, comp_type, DATA_NODE_BLOCK_SIZE, DATA_LEAF_BLOCK_SIZE> fixed_name_map;
fixed_name_map myFixedMap((fixed_name_map::node_block_type::raw_size)*5, (fixed_name_map::leaf_block_type::raw_size)*5);
Now the program is compiling fine and is accepting about 10^8 strings without any problem.
also we can use myFixedMap like std::map itself. {for ex: myFixedMap[fixedString] = 10}
If you are using C++11, then as an alternative to the FixedString class you could use std::array<char, MAX_KEY_LEN>. It is an STL layer on top of an ordinary fixed-size C array, implementing comparisons and iterators as you are used to from std::string, but it's a POD type, so STXXL should support it.
Alternatively, you can use serialization_sort in TPIE. It can sort elements of type std::pair<std::string, unsigned int> just fine, so if all you need is to insert everything in bulk and then access it in bulk, this will be sufficient for your case (and probably faster depending on the exact case).