Question

J'ai un tas de chaînes comme:

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

je voudrais remplacer cela par

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

Et ainsi de suite pour toutes les valeurs ASCII imprimables .

Actuellement, j'utilise boost :: regex_search pour faire correspondre & amp; # (\ d +); , créant une chaîne lorsque je traite chaque correspondance à tour de rôle (y compris l'ajout la sous-chaîne ne contenant aucune correspondance depuis la dernière correspondance trouvée).

Quelqu'un peut-il penser à une meilleure façon de le faire? Je suis ouvert aux méthodes autres que les expressions rationnelles, mais les expressions rationnelles semblaient une approche raisonnablement raisonnable dans ce cas.

Merci,

Dom

Était-ce utile?

La solution

Le gros avantage de l'utilisation d'une expression régulière est de traiter les cas délicats tels que & amp; # 38; # 38; . Le remplacement d'entité n'est pas itératif, c'est une étape simple. La regex va aussi être assez efficace: les deux caractères principaux sont fixes, donc tout ce qui ne commence pas par & amp; # sera rapidement ignoré. Enfin, la solution regex est une solution qui ne réserve pas beaucoup de surprises aux futurs responsables de la maintenance.

Je dirais qu'une regex était le bon choix.

Est-ce la meilleure regex, cependant? Vous savez que vous avez besoin de deux chiffres et si vous avez trois chiffres, le premier sera un 1. ASCII imprimable est après tout & # 32; - & # 126; . Pour cette raison, vous pouvez considérer & amp; # 1? \ D \ d; .

En ce qui concerne le remplacement du contenu, j'utiliserais le algorithme de base décrit pour 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.

Autres conseils

Cela va probablement me rapporter des votes négatifs, vu que ce n'est pas une réponse boost ou regex c ++, mais voici une solution SNOBOL. Celui-ci fonctionne pour ASCII. Je travaille sur quelque chose pour Unicode.

        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

Les solutions SNOBOL existantes ne gèrent pas correctement le cas de modèles multiples, car il n’existe qu’un seul "& amp;". La solution suivante devrait mieux fonctionner:

        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

Je ne connais pas le support regex dans boost, mais vérifiez s’il possède une méthode replace () qui prend en charge les callbacks ou les lambdas ou autres. C'est la manière habituelle de faire cela avec des regexes dans d'autres langues, je dirais.

Voici une implémentation 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)

Production:

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

J'en ai déjà vu un peu plus sur boost et je vois qu'il a une fonction regex_replace. Mais le C ++ me déroute vraiment et je ne peux donc pas savoir si vous pouvez utiliser un rappel pour la pièce remplacée. Mais la chaîne correspondant au groupe (\ d \ d) devrait être disponible dans $ 1 si je lis correctement la documentation de démarrage. Je vérifierais si j'utilisais boost.

Vous savez, tant que nous sommes hors sujet, la substitution perl a une option 'e'. Comme dans évaluer l'expression . Ex.

  

echo "Bonjour, voici un côlon de test & amp; # 58 ;. Voici un test point-virgule & amp; # 59;
En savoir plus sur le test & amp; # 38; # 65 ;. abc. & amp; # 126; .def. "
| perl -we 'sub translate {mon $ x = $ _ [0]; if (($ x = 32) & ? ($ x = 126))
{return sprintf ("% c", $ x); } else {return " & amp; # ". $ x. " ;; }}
while (< >) {s / & amp; # (1? \ d \ d); / & translate ($ 1) / ge; impression; } '

Une jolie impression qui:

#!/usr/bin/perl -w

sub translate
{
  my $x=

Vous savez, tant que nous sommes hors sujet, la substitution perl a une option 'e'. Comme dans évaluer l'expression . Ex.

  

echo "Bonjour, voici un côlon de test & amp; # 58 ;. Voici un test point-virgule & amp; # 59;
En savoir plus sur le test & amp; # 38; # 65 ;. abc. & amp; # 126; .def. "
| perl -we 'sub translate {mon $ x = $ _ [0]; if (($ x = 32) & ? ($ x = 126))
{return sprintf ("% c", $ x); } else {return " & amp; # ". $ x. " ;; }}
while (< >) {s / & amp; # (1? \ d \ d); / & translate ($ 1) / ge; impression; } '

Une jolie impression qui:

<*>

Bien que Perl soit Perl, je suis sûr qu’il existe un bien meilleur moyen d’écrire ça ...

Retour au code C:

Vous pouvez également lancer votre propre machine à états finis. Mais cela devient compliqué à entretenir plus tard.

[0]; if ( ($x >= 32) && ($x <= 126) ) { return sprintf( "%c", $x ); } else { return "&#" . $x . ";" ; } } while (<>) { s/&#(1?\d\d);/&translate($1)/ge; print; }

Bien que Perl soit Perl, je suis sûr qu’il existe un bien meilleur moyen d’écrire ça ...

Retour au code C:

Vous pouvez également lancer votre propre machine à états finis. Mais cela devient compliqué à entretenir plus tard.

Voici un autre doublon de Perl (voir la réponse de mrree ):

  • un fichier de test:
$ cat ent.txt 
Hello, &#12; here's a test colon&#58;. 
Here's a test semi-colon&#59; '&#131;'
  • le one-liner:
$ 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
  • ou en utilisant des expressions rationnelles plus spécifiques:
Hello, &#12; here's a test colon:. 
Here's a test semi-colon; '&#131;'
  • les deux monolignes produisent le même résultat:
<*>

boost :: spirit Le cadre du générateur d’analyseurs permet de créer facilement un analyseur qui transforme les NCR s désirés.

// 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');
  }
}
  • compiler:
    $ g++ -I/path/to/boost -o spirit_ncr2a spirit_ncr2a.cpp
  • exécuter:
    $ echo "Hello, &#12; here's a test colon&#58;." | spirit_ncr2a
  • sortie:
    "Hello, &#12; here's a test colon:." 

Je pensais que j'étais assez bon en regex mais je n'ai jamais vu de lambdas utilisé dans regex, éclairez-moi s'il vous plaît!

J'utilise actuellement python et l'aurais résolu avec cet oneliner:

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

Cela a-t-il un sens?

Voici un scanner NCR créé à l'aide de Flex :

/** 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 */
}

Pour créer un exécutable:

$ flex ncr2a.y
$ gcc -o ncr2a lex.yy.c -lfl

Exemple:

$ echo "Hello, &#12; here's a test colon&#58;. 
> Here's a test semi-colon&#59; '&#131;'
> &#38;#59; <-- may be recursive" \
> | ncr2a

Il imprime pour la version non récursive:

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

Et le récursif produit:

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

C’est l’un des cas où l’énoncé du problème initial n’est apparemment pas très complet, mais si vous voulez vraiment déclencher sur des cas produisant des caractères compris entre 32 et 126, c’est un changement insignifiant dans la solution. posté plus tôt. Notez que ma solution gère également le cas des modèles multiples (bien que cette première version ne gère pas les cas où certains modèles adjacents sont dans la plage et d'autres pas).

      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

Il ne serait pas particulièrement difficile de traiter ce cas (par exemple; # 131; # 58; produit "; # 131;:" ainsi:

      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

Voici une version basée sur boost :: regex_token_iterator . Le programme remplace les NCR décimales lues à partir de stdin par les caractères ASCII correspondants. et les imprime dans 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';
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top