سؤال

لدي مجموعة من الأوتار مثل:

"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+);, ، بناء سلسلة أثناء معالجة كل مباراة بدورها (بما في ذلك إلحاق السلسلة الفرعية التي لا تحتوي على أي مطابقات منذ آخر مباراة وجدتها).

هل يمكن لأي شخص التفكير في طريقة أفضل للقيام بذلك؟ أنا منفتح على الأساليب غير Regex ، لكن Regex بدا نهجًا معقولًا في هذه الحالة.

شكرًا،

دوم

هل كانت مفيدة؟

المحلول

الميزة الكبيرة لاستخدام regex هي التعامل مع الحالات الصعبة مثل & استبدال الكيان ليس تكرارًا ، إنه خطوة واحدة. سيكون Regex أيضًا فعالًا إلى حد ما: يتم إصلاح الشخصيات الرائدة ، لذلك سيتخلى بسرعة عن أي شيء لا يبدأ به &#. أخيرًا ، فإن حل Regex هو واحد بدون الكثير من المفاجآت للمحامون في المستقبل.

أود أن أقول أن regex كان الخيار الصحيح.

هل هو أفضل regex ، رغم ذلك؟ أنت تعلم أنك بحاجة إلى رقمين ، وإذا كان لديك 3 أرقام ، فإن الأول سيكون 1. ASCII قابل للطباعة هو بعد كل شيء  -~. لهذا السبب ، يمكنك التفكير &#1?\d\d;.

بالنسبة لاستبدال المحتوى ، سأستخدم الخوارزمية الأساسية الموصوفة لـ Boost :: regex :: استبدال :

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 ++ أو BOOST أو REGEX ، ولكن إليك حل 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 الحالية مع حالة النماذج المتعددة بشكل صحيح ، وذلك بسبب وجود واحد فقط "&". يجب أن يعمل الحل التالي بشكل أفضل:

        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

لا أعرف عن دعم regex في Boost ، ولكن تحقق مما إذا كان لديه طريقة استبدال () تدعم عمليات الاسترجاعات أو lambdas أو بعض ذلك. هذه هي الطريقة المعتادة للقيام بذلك باستخدام regexes بلغات أخرى أقول.

إليك تطبيق 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 دولار إذا قرأت مستندات التعزيز بشكل صحيح. كنت أتحقق من ذلك إذا كنت أستخدم Boost.

أنت تعرف ، طالما أننا خارج الموضوع هنا ، فإن استبدال Perl لديه خيار "E". كما في تقييم التعبير. على سبيل المثال

Echo "مرحبًا ، إليك اختبار Colon:. إليك اختبار شبه كولون ؛
مزيد من الاختبار &#65 ؛. ABC. ~ .def. "
| perl -we 'sub Translate {my $ x = $ _ [0] ؛ if (($ x> = 32) && ($ x <= 126))
{return Sprintf ("٪ c" ، $ x) ؛ } آخر {return "&#". $ x. "؛" ؛ }}
بينما (<>) {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;
}

على الرغم من أن بيرل كان بيرل ، إلا أنني متأكد من أن هناك طريقة أفضل بكثير لكتابة ذلك ...


العودة إلى رمز C:

يمكنك أيضا لفة آلة الحالة المحدودة الخاصة بك. لكن هذا يصبح فوضويًا ومزعجًا للحفاظ عليه لاحقًا.

إليك خط آخر بيرل واحد (انظر @إجابة 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); $& }->()~eg' ent.txt
  • أو استخدام المزيد من regex:
$ perl -pe"s~&#(1(?:[01][0-9]|2[0-6])|3[2-9]|[4-9][0-9]);~chr($1)~eg" ent.txt
  • كلاهما واحد ينتج نفس الإخراج:
Hello, &#12; here's a test colon:. 
Here's a test semi-colon; '&#131;'

تعزيز :: الروح يسمح إطار عمل مولد المحللون بسهولة بإنشاء محلل يحول مرغوبًا فيه 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:." 

أعتقد أنني كنت جيدًا في Regex لكنني لم أر Lambdas أبدًا في Regex ، من فضلك Enlighten Me!

أنا أستخدم Python حاليًا وسأحلها مع هذا oneliner:

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

هل هذا منطقي؟

إليك ماسح ضوئي 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

يطبع للنسخة غير المثيرة:

Hello, &#12; here's a test colon:.
Here's a test semi-colon; '&#131;'
&#59; <-- may be recursive

وتنتج العودية:

Hello, &#12; here's a test colon:.
Here's a test semi-colon; '&#131;'
; <-- may be recursive

هذه واحدة من تلك الحالات التي يبدو أن بيان المشكلة الأصلي لا يكتمل للغاية ، ولكن إذا كنت تريد حقًا تشغيل الحالات التي تنتج أحرفًا تتراوح بين 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

إليك نسخة تستند إلى boost::regex_token_iterator. البرنامج يحل محل عشري NCRقراءة من stdin من خلال أحرف 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