5

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

"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_searchmatch を使用して&#(\d+);、各一致を順番に処理しながら文字列を構築しています (最後に見つかった一致以降に一致を含まない部分文字列を追加することを含みます)。

誰かがそれを行うためのより良い方法を考えることができますか? 私は正規表現以外の方法を受け入れますが、この場合、正規表現は合理的に賢明なアプローチのようです。

ありがとう、

ドム

4

12 に答える 12

9

&正規表現を使用する大きな利点は、エンティティの置換が反復的ではなく、単一のステップであるなどのトリッキーなケースに対処できることです。正規表現もかなり効率的です。先頭の 2 文字が固定されているため、で始まらないものはすばやくスキップされ&#ます。最後に、正規表現のソリューションは、将来のメンテナーにとって驚くべきことではありません。

正規表現が正しい選択だったと思います。

しかし、それは最高の正規表現ですか?2 桁が必要で、3 桁の場合、最初の桁は 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.
于 2009-01-09T13:56:10.260 に答える
3

これはc++、ブースト、または正規表現の応答ではないため、おそらく私にいくらかの反対票を獲得するでしょうが、これがSNOBOLソリューションです。これはASCIIで機能します。Unicode用の何かに取り組んでいます。

        NUMS = '1234567890'
MAIN    LINE = INPUT                                :F(END)
SWAP    LINE ?  '&#' SPAN(NUMS) . N ';' = CHAR( N ) :S(SWAP)
        OUTPUT = LINE                               :(MAIN)
END
于 2009-01-09T14:54:40.907 に答える
3
* 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
于 2009-01-09T18:42:17.577 に答える
2

ブーストでの正規表現のサポートについてはわかりませんが、コールバックやラムダなどをサポートする 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;"

私は今boostを見てきましたが、regex_replace関数があることがわかりました。しかし、C++ は本当に混乱するので、置換部分にコールバックを使用できるかどうかわかりません。しかし、ブースト ドキュメントを正しく読めば、(\d\d) グループに一致する文字列は $1 で利用できるはずです。ブーストを使用しているかどうかを確認します。

于 2009-01-09T13:31:00.420 に答える
1

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 "こんにちは、  ここにテスト コロンがあります:." | | Spirit_ncr2a
  • 出力:
    「こんにちは、  ここにテスト コロンがあります:.」 
于 2009-01-21T15:23:24.277 に答える
1

既存の SNOBOL ソリューションは、「&」が 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
于 2009-01-09T21:53:21.233 に答える
1

これは別のPerlのワンライナーです(@mrreeの回答を参照):

  • テストファイル:
$猫ent.txt
こんにちは  ここにテストコロンがあります:.
これがテスト用のセミコロンです; 「ƒ」
  • ワンライナー:
$ perl -pe's~&#(1?\d\d);~
> sub{ return chr($1) if (31 < $1 && $1 < 127); $& }->()~eg' ent.txt
  • またはより具体的な正規表現を使用する:
$ perl -pe"s~&#(1(?:[01][0-9]|2[0-6])|3[2-9]|[4-9][0-9]); ~chr($1)~eg" ent.txt
  • どちらのワンライナーも同じ出力を生成します。
こんにちは  ここにテストコロンがあります:.
これはテスト用のセミコロンです。「ƒ」
于 2009-01-10T16:25:19.163 に答える
1

ここで話題から外れている限り、perl 置換には 'e' オプションがあります。評価式のように。例えば

echo "こんにちは、ここにテスト コロンがあります:. ここにテスト セミコロンがあります;
さらにテスト &#65;. abc.~.def."
| | perl -we 'sub translate { my $x=$_[0]; if ( ($x >= 32) && ($x <= 126) )
{ return sprintf("%c",$x); } else { return "&#".$x.";"; } }
while (<>) { s/&#(1?\d\d);/&translate($1)/ge; 印刷; }'

次のことをきれいに印刷します。

#!/usr/bin/perl -w

sub translate
{
  my $x=$_[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 コードに戻る:

独自の有限状態マシンをロールすることもできます。しかし、それは面倒で、後で維持するのが面倒です。

于 2009-01-09T23:57:27.330 に答える
0

をベースにしたバージョンですboost::regex_token_iterator。プログラムは、読み取った10 進数のNCRstdinを対応する 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';
  }
}
于 2009-01-21T01:13:44.010 に答える
0

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

私は現在pythonを使用しており、このワンライナーで解決したでしょう:

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

それは意味がありますか?

于 2009-01-09T13:53:45.083 に答える
0

これは、元の問題ステートメントが明らかに完全ではないケースの 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; は ";#131;:" も生成します:

      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
于 2009-01-18T17:27:24.467 に答える
0

Flexを使用して作成された NCR スキャナーは次のとおりです。

/** 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, &#12; here's a test colon&#58;. 
> Here's a test semi-colon&#59; '&#131;'
> &#38;#59; <-- may be recursive" \
> | ncr2a

非再帰バージョンの場合は次のように出力されます。

こんにちは  ここにテストコロンがあります:.
これはテスト用のセミコロンです。「ƒ」
; <-- 再帰的かもしれません

そして、再帰的なものは以下を生成します:

こんにちは  ここにテストコロンがあります:.
これはテスト用のセミコロンです。「ƒ」
; <-- 再帰的かもしれません
于 2009-01-11T20:08:32.503 に答える