質問

次のような文字列がたくさんあります:

"Hello, here's a test colon:. Here's a test semi-colon&#59;"

これを

に置き換えたい

"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;#38; -> &#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&#58;. Here's a test semi-colon&#59;"
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, &#12; here's a test colon&#58;. 
Here's a test semi-colon&#59; '&#131;'
  • ワンライナー:
$ 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.txt
amp; }->()~eg' ent.txt
  • またはより具体的な正規表現を使用:
Hello, &#12; here's a test colon:. 
Here's a test semi-colon; '&#131;'
  • どちらのワンライナーも同じ出力を生成します:
<*>

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, &#12; here's a test colon&#58;." | spirit_ncr2a
  • 出力:
    "Hello, &#12; here's a test colon:." 

正規表現はかなり上手だったと思いましたが、正規表現でラムダが使用されるのを見たことがないので、教えてください!

私は現在pythonを使用していますが、このonelinerで解決できたでしょう:

''.join([x.isdigit() and chr(int(x)) or x for x in re.split('&#(\d+);',THESTRING)])

それは理にかなっていますか?

これは、元の問題ステートメントが明らかにあまり完全ではないケースの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., '&#58;' -> ':'
        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';
  }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top