"/"
内のすべてのstd::string
を"\/"
で置き換えて、std::string
内のすべてのスラッシュをエスケープする簡単な方法はありますか?
おそらくこれを行う最も簡単な方法は ブースト文字列アルゴリズムライブラリ を使用することです。
boost::replace_all(myString, "/", "\\/");
std::string result = boost::replace_all_copy(myString, "/", "\\/");
答えはノーです...すでに標準ライブラリによって提供されているワンライナーを意味する場合、「簡単な」方法はありません。ただし、その機能を実装することは難しくありません。
まず最初に、\
を\\
およびその他の特殊文字に置き換える必要があると思います。この場合、ildjarnが提供するreplaceAll
実装を使用するのは面倒です(同じ文字列を何度か置き換える必要があります)。
私の意見では、明示的なchar *
アプローチを使用しても何も勝るものがない文字列処理の多くのケースがあります。ただし、この特定のケースでは、おそらくインデックスを使用するだけで十分です。
std::string escape(const std::string& s)
{
int n = s.size(), wp = 0;
std::vector<char> result(n*2);
for (int i=0; i<n; i++)
{
if (s[i] == '/' || s[i] == '\\')
result[wp++] = '\\';
result[wp++] = s[i];
}
return std::string(&result[0], &result[wp]);
}
基本的には、文字列の上に移動し、特別な文字の前に\
文字を追加するというアイデアです(上記では/
と\
を処理しただけですが、アイデアはわかります)。結果は最大で2*n
であることがわかっているため、全体を処理するように事前割り当てしますO(n)(replaceAll
アプローチは、代わりに残りの文字列を右側に配置してO(n ^ 2)にします。"this is a test with /slashes/ that should be /escaped/"
のような短い文字列の場合でも、replaceAll
は1回だけで、代わりにescape
の2つの特殊文字を処理します。
また、この関数は文字列を適切に変更するのではなく、個別の文字列を自然に返します(IMOの方が優れたインターフェイスです)。タイミングの比較では、呼び出しごとに文字列を作成する必要があったので、定数が追加されたため、結果は同等にシフトしました。時間。
上記の読み取り/書き込みアプローチは、より複雑な置換(たとえば、>
を>
で置き換えるか、印刷可能な範囲にない文字を%xx
エンコーディングで置き換える)にも簡単に拡張できます。文字列(1パスのみ)。
これを行う方法の例は cppreference.com std::string::replace
ページ :
std::string& replaceAll(std::string& context, std::string const& from, std::string const& to)
{
std::size_t lookHere = 0;
std::size_t foundHere;
while((foundHere = context.find(from, lookHere)) != std::string::npos)
{
context.replace(foundHere, from.size(), to);
lookHere = foundHere + to.size();
}
return context;
}
文字列内のすべての部分文字列を別の部分文字列で置き換えるには:
#include <iostream>
void replace_all(std::string& input, const std::string& from, const std::string& to) {
size_t pos = 0;
while ((pos = input.find(from, pos)) != std::string::npos) {
input.replace(pos, from.size(), to);
pos += to.size();
}
}
int main() {
std::string str("i am a geek/nerd/crazy person.");
replace_all(str, "/", "\\/");
std::cout << str << '\n';
}
出力:
$ g++-6.1.0 -std=c++17 -g -Og -Werror -Wall -Wextra -pedantic -Wold-style-cast -Wnon-virtual-dtor -Wshadow -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wmisleading-indentation -fsanitize=address,leak,undefined; ./a.out
i am a geek\/nerd\/crazy person.
さまざまなキャラクターをエスケープできるストリーミング実装を作成するために、私は質問を推測しました。
ストリーミングは実際には大量のビスケットを使用します[1]。そうしないと、ヒープの断片化/パフォーマンスの問題が発生するからです。また、サンプルが示すように、これにより、ほぼすべてのソースに格納されている文字列をエスケープすることができます。
#include <iostream>
#include <iterator>
#include <set>
#include <sstream>
#include <string>
template <class _II, class _OI>
static _OI escapeSomeChars(const _II inIt, const _II endIt, _OI outIt)
{
for (_II it=inIt; it!=endIt; ++it)
switch (*it)
{
case '\0': outIt++ = '\\'; outIt++ = '0'; break;
case '\n': outIt++ = '\\'; outIt++ = 'n'; break;
case '\\':
case '"' :
case '$' :
case '/' : outIt++ = '\\';
default : outIt++ = *it;
}
return outIt;
}
static std::string escapeSomeChars(const std::string& input)
{
std::ostringstream os;
escapeSomeChars(input.begin(), input.end(), std::ostream_iterator<char>(os));
return os.str();
}
namespace /*anon*/ {
struct rawchar { // helper - see e.g. http://bytes.com/topic/c/answers/436124-copy-istream_iterator-question
char _c; rawchar(char c=0) : _c(c) {}
operator const char&() const { return _c; }
friend std::istream& operator>>(std::istream& is, rawchar& out) { return is.get(out._c); }
};
}
int main()
{
static const char data[] = "\"I will \\$one day \\have \\all \\\\my slash\\es escaped, much \\like\\ in the source!\n\"";
// use the overload for std::string
std::cout << escapeSomeChars(data);
std::cout << std::endl;
// streaming in & out:
std::istringstream is(data);
escapeSomeChars(std::istream_iterator<rawchar>(is), std::istream_iterator<rawchar>(), std::ostream_iterator<char>(std::cout));
std::cout << std::endl;
// but you don't need an istream, you can use any STL iterator range
escapeSomeChars(data, data+sizeof(data)/sizeof(data[0]), std::ostream_iterator<char>(std::cout));
std::cout << std::endl;
// but any source and target will do:
std::string asstring(data);
std::set<char> chars(asstring.begin(), asstring.end());
asstring.clear();
escapeSomeChars(chars.begin(), chars.end(), std::back_inserter(asstring));
std::cout << "Unique characters in data: '" << asstring << "', but properly escaped!" << std::endl;
return 0;
}
コンパイラーによって最適化されるため、スイッチを選択しました。エスケープ可能な文字の動的なセットの場合、私はある種のルックアップを優先します(std :: findを使用するベクトルはそうしますが、大きなセットの場合は、set :: findを指定したstd :: setがより良い選択になります)。
お役に立てれば
[1]を参照してください。私が最近遭遇したこの美しいバグ: GParted:Simplified cleanup_cursor()implementation