正規表現の置換:に“:”等
質問
次のような文字列がたくさんあります:
"Hello, here's a test colon:. Here's a test semi-colon;"
これを
に置き換えたい
"Hello, here's a test colon:. Here's a test semi-colon;"
その他すべての印刷可能なASCII値。
現在、 boost :: regex_search
は&#(\ d +);
に一致し、各一致を順番に処理するときに文字列を作成します(追加を含む)最後に見つかった一致以降に一致を含まない部分文字列)。
誰もがそれを行うより良い方法を考えることができますか?私は非正規表現の方法を受け入れていますが、この場合、正規表現は合理的に賢明なアプローチのように見えました。
ありがとう、
Dom
解決
正規表現を使用する大きな利点は、&
などのトリッキーなケースに対処することです。エンティティの置換は反復的ではなく、1つのステップです。正規表現もかなり効率的になります。2つのリードキャラクターが修正されるため、&#
で始まらないものはすべてスキップされます。最後に、正規表現ソリューションは、将来のメンテナーにとって大きな驚きのないものです。
正規表現が正しい選択だったと思います。
しかし、それは最高の正規表現ですか? 2桁が必要であり、3桁の場合、最初の1は1になります。印刷可能なASCIIは、すべての -~
になります。そのため、&#1?\ d \ d;
を検討できます。
コンテンツの置換については、 boost :: regex :: replaceの基本アルゴリズムの説明:
For each match // Using regex_iterator<>
Print the prefix of the match
Remove the first 2 and last character of the match (&#;)
lexical_cast the result to int, then truncate to char and append.
Print the suffix of the last match.
他のヒント
これはおそらく、C ++、ブースト、または正規表現の応答ではないため、いくつかのダウン票を獲得しますが、ここにSNOBOLソリューションがあります。これはASCIIで機能します。ユニコードの何かに取り組んでいます。
NUMS = '1234567890'
MAIN LINE = INPUT :F(END)
SWAP LINE ? '&#' SPAN(NUMS) . N ';' = CHAR( N ) :S(SWAP)
OUTPUT = LINE :(MAIN)
END
* Repaired SNOBOL4 Solution
* &#38; -> &
digit = '0123456789'
main line = input :f(end)
result =
swap line arb . l
+ '&#' span(digit) . n ';' rem . line :f(out)
result = result l char(n) :(swap)
out output = result line :(main)
end
既存のSNOBOLソリューションは、「&amp;」が1つしかないため、複数パターンのケースを適切に処理しません。次の解決策はより良く機能するはずです:
dd = "0123456789"
ccp = "#" span(dd) $ n ";" *?(s = s char(n)) fence (*ccp | null)
rdl line = input :f(done)
repl line "&" *?(s = ) ccp = s :s(repl)
output = line :(rdl)
done
end
boostでの正規表現のサポートについては知りませんが、コールバックやラムダなどをサポートするreplace()メソッドがあるかどうかを確認してください。これは私が言う他の言語の正規表現でこれを行う通常の方法です。
Pythonの実装は次のとおりです。
s = "Hello, here's a test colon:. Here's a test semi-colon;"
re.sub(r'&#(1?\d\d);', lambda match: chr(int(match.group(1))), s)
生産:
"Hello, here's a test colon:. Here's a test semi-colon;"
今、ブーストをいくつか見てきましたが、regex_replace関数があります。しかし、C ++は私を本当に混乱させるので、置換部分にコールバックを使用できるかどうかはわかりません。ブーストドキュメントを正しく読んだ場合、(\ d \ d)グループに一致する文字列は$ 1で利用できるはずです。ブーストを使用していた場合はチェックアウトします。
やっぱり、ここで話題になっていない限り、perlの置換には 'e'オプションがあります。 式を評価のように。例:
echo&quot;こんにちは、テストコロンです&#58;。テストセミコロン&amp;#59;
さらにテスト&amp;#38;#65;。 abc。&amp;#126; .def。&quot;
| perl -we 'sub translate {my $ x = $ _ [0]; if(($ x&gt; = 32)&amp;&amp;($ x&lt; = 126))
{return sprintf(&quot;%c&quot;、$ x); } else {return&quot;&amp;#&quot;。$ x。&quot ;;&quot ;; }}
while(&lt;&gt;){s /&amp;#(1?\ d \ d); /&amp; translate($ 1)/ ge;印刷; } '
きれいに印刷する:
#!/usr/bin/perl -w
sub translate
{
my $x=やっぱり、ここで話題になっていない限り、perlの置換には 'e'オプションがあります。 式を評価のように。例:
echo&quot;こんにちは、テストコロンです&#58;。テストセミコロン&amp;#59;
さらにテスト&amp;#38;#65;。 abc。&amp;#126; .def。&quot;
| perl -we 'sub translate {my $ x = $ _ [0]; if(($ x&gt; = 32)&amp;&amp;($ x&lt; = 126))
{return sprintf(&quot;%c&quot;、$ x); } else {return&quot;&amp;#&quot;。$ x。&quot ;;&quot ;; }}
while(&lt;&gt;){s /&amp;#(1?\ d \ d); /&amp; translate($ 1)/ ge;印刷; } '
きれいに印刷する:
<*>
perlはperlですが、それを書くもっと良い方法があると確信しています...
Cコードに戻る:
独自の有限状態マシンをロールすることもできます。しかし、後で維持するのは面倒で面倒です。
[0];
if ( ($x >= 32) && ($x <= 126) )
{
return sprintf( "%c", $x );
}
else
{
return "&#" . $x . ";" ;
}
}
while (<>)
{
s/&#(1?\d\d);/&translate($1)/ge;
print;
}
perlはperlですが、それを書くもっと良い方法があると確信しています...
Cコードに戻る:
独自の有限状態マシンをロールすることもできます。しかし、後で維持するのは面倒で面倒です。
別のPerlのワンライナーです( @mrreeの回答を参照)。
- テストファイル:
$ cat ent.txt Hello,  here's a test colon:. Here's a test semi-colon; 'ƒ'
- ワンライナー:
$ perl -pe's~&#(1?\d\d);~ > sub{ return chr($1) if (31 < $1 && $1 < 127);$ perl -pe"s~&#(1(?:[01][0-9]|2[0-6])|3[2-9]|[4-9][0-9]);~chr($1)~eg" ent.txtamp; }->()~eg' ent.txt
- またはより具体的な正規表現を使用:
Hello,  here's a test colon:. Here's a test semi-colon; 'ƒ'
- どちらのワンライナーも同じ出力を生成します:
boost :: spirit パーサージェネレーターフレームワークにより、望ましい NCR を変換するパーサーを簡単に作成できます。
// spirit_ncr2a.cpp
#include <iostream>
#include <string>
#include <boost/spirit/include/classic_core.hpp>
int main() {
using namespace BOOST_SPIRIT_CLASSIC_NS;
std::string line;
while (std::getline(std::cin, line)) {
assert(parse(line.begin(), line.end(),
// match "&#(\d+);" where 32 <= $1 <= 126 or any char
*(("&#" >> limit_d(32u, 126u)[uint_p][&putchar] >> ';')
| anychar_p[&putchar])).full);
putchar('\n');
}
}
- コンパイル:
$ g++ -I/path/to/boost -o spirit_ncr2a spirit_ncr2a.cpp
- 実行:
$ echo "Hello,  here's a test colon:." | spirit_ncr2a
- 出力:
"Hello,  here's a test colon:."
正規表現はかなり上手だったと思いましたが、正規表現でラムダが使用されるのを見たことがないので、教えてください!
私は現在pythonを使用していますが、このonelinerで解決できたでしょう:
''.join([x.isdigit() and chr(int(x)) or x for x in re.split('&#(\d+);',THESTRING)])
それは理にかなっていますか?
Flex を使用して作成されたNCRスキャナーです。 a>:
/** ncr2a.y: Replace all NCRs by corresponding printable ASCII characters. */
%%
&#(1([01][0-9]|2[0-6])|3[2-9]|[4-9][0-9]); { /* accept 32..126 */
/**recursive: unput(atoi(yytext + 2)); skip '&#'; `atoi()` ignores ';' */
fputc(atoi(yytext + 2), yyout); /* non-recursive version */
}
実行可能ファイルを作成するには:
$ flex ncr2a.y
$ gcc -o ncr2a lex.yy.c -lfl
例:
$ echo "Hello,  here's a test colon:.
> Here's a test semi-colon; 'ƒ'
> &#59; <-- may be recursive" \
> | ncr2a
非再帰バージョン用に印刷します:
Hello,  here's a test colon:. Here's a test semi-colon; 'ƒ' ; <-- may be recursive
そして再帰的なものは以下を生成します:
Hello,  here's a test colon:. Here's a test semi-colon; 'ƒ' ; <-- may be recursive
これは、元の問題ステートメントが明らかにあまり完全ではないケースの1つですが、32から126の間の文字を生成するケースでのみトリガーしたい場合は、ソリューションへの些細な変更です以前に投稿されました。私のソリューションは複数パターンのケースも処理することに注意してください(ただし、この最初のバージョンでは、隣接するパターンの一部が範囲内にあり、他のパターンが範囲内にない場合は処理できません)。
dd = "0123456789"
ccp = "#" span(dd) $ n *lt(n,127) *ge(n,32) ";" *?(s = s char(n))
+ fence (*ccp | null)
rdl line = input :f(done)
repl line "&" *?(s = ) ccp = s :s(repl)
output = line :(rdl)
done
end
そのケースを処理することは特に難しいことではありません(例:;#131;#58;は&quot ;;#131;:&quot;も生成します:
dd = "0123456789"
ccp = "#" (span(dd) $ n ";") $ enc
+ *?(s = s (lt(n,127) ge(n,32) char(n), char(10) enc))
+ fence (*ccp | null)
rdl line = input :f(done)
repl line "&" *?(s = ) ccp = s :s(repl)
output = replace(line,char(10),"#") :(rdl)
done
end
boost :: regex_token_iterator
。プログラムは、 stdin
から読み取られた10進数の NCR を対応するASCII文字に置き換えます。 stdout
に出力します。
#include <cassert>
#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
int main()
{
boost::regex re("&#(1(?:[01][0-9]|2[0-6])|3[2-9]|[4-9][0-9]);"); // 32..126
const int subs[] = {-1, 1}; // non-match & subexpr
boost::sregex_token_iterator end;
std::string line;
while (std::getline(std::cin, line)) {
boost::sregex_token_iterator tok(line.begin(), line.end(), re, subs);
for (bool isncr = false; tok != end; ++tok, isncr = !isncr) {
if (isncr) { // convert NCR e.g., ':' -> ':'
const int d = boost::lexical_cast<int>(*tok);
assert(32 <= d && d < 127);
std::cout << static_cast<char>(d);
}
else
std::cout << *tok; // output as is
}
std::cout << '\n';
}
}