c++ regex boost ascii ncr

c++ - Reemplazo de Regex : a ":", etc.



boost ascii (12)

Esto probablemente me gane algunos votos, ya que no se trata de una respuesta de c ++, boost o regex, pero aquí hay una solución de SNOBOL. Este funciona para ASCII. Estoy trabajando en algo para Unicode.

NUMS = ''1234567890'' MAIN LINE = INPUT :F(END) SWAP LINE ? ''&#'' SPAN(NUMS) . N '';'' = CHAR( N ) :S(SWAP) OUTPUT = LINE :(MAIN) END

Tengo un montón de cadenas como:

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

Me gustaría reemplazar eso con

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

Y así sucesivamente para todos los valores ASCII imprimibles .

En este momento estoy usando boost::regex_search para hacer coincidir &#(/d+); , construyendo una cadena a medida que proceso cada partida por turno (incluida la adición de la subcadena que no contiene coincidencias desde la última coincidencia que encontré).

¿Alguien puede pensar en una mejor manera de hacerlo? Estoy abierto a los métodos no regex, pero regex parecía un enfoque razonablemente sensato en este caso.

Gracias,

Dom


La gran ventaja de usar una expresión regular es lidiar con casos complicados como & El reemplazo de la entidad no es iterativo, es un solo paso. La expresión regular también será bastante eficiente: los dos caracteres principales son fijos, por lo que saltará rápidamente cualquier cosa que no comience con &# . Finalmente, la solución de expresiones regulares es una sin muchas sorpresas para futuros mantenedores.

Yo diría que una expresión regular fue la elección correcta.

Sin embargo, ¿es la mejor expresión regular? Usted sabe que necesita dos dígitos y si tiene 3 dígitos, el primero será un 1. El ASCII imprimible es después de todo  -~ . Por ese motivo, podría considerar &#1?/d/d; .

En cuanto a reemplazar el contenido, utilizaría el algoritmo básico descrito para 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.


Las soluciones SNOBOL existentes no manejan el caso de patrones múltiples correctamente, debido a que solo hay un "&". La siguiente solución debería funcionar mejor:

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


Pensé que era bastante bueno en Regex, pero nunca había visto lambdas en Regex. ¡Por favor, ilumíname!

Actualmente estoy usando Python y lo habría resuelto con este oneliner:

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

¿Eso tiene sentido?


* 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


No sé sobre el soporte de expresiones regulares en boost, pero compruebe si tiene un método de replace () que admita callbacks o lambdas o algo así. Esa es la forma habitual de hacer esto con expresiones regulares en otros idiomas, diría yo.

Aquí hay una implementación de 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)

Productor:

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

He buscado algunos en boost ahora y veo que tiene una función regex_replace. Pero C ++ realmente me confunde, así que no puedo entender si podría usar una devolución de llamada para la pieza de repuesto. Pero la cadena que coincide con el grupo (/ d / d) debe estar disponible en $ 1 si leo los documentos de refuerzo correctamente. Lo verificaría si estuviera usando boost.


Aquí hay un escáner NCR creado con 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 */ }

Para hacer un ejecutable:

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

Ejemplo:

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

Imprime para la versión no recursiva:

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

Y el recursivo produce:

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


Ya sabes, mientras estemos fuera del tema aquí, la sustitución perl tiene una opción ''e''. Como en evaluar la expresión . P.ej

echo "Hola, aquí hay una prueba de colon & # 58; Aquí hay una prueba de punto y coma & # 59;
Prueba adicional & # 38; # 65 ;. abc. & # 126; .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; impresión; } ''

Pretty-impresión que:

#!/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; }

Aunque perl es perl, estoy seguro de que hay una forma mucho mejor de escribir eso ...

Volver al código C:

También podría hacer rodar su propia máquina de estados finitos. Pero eso se vuelve complicado y problemático de mantener más adelante.


Este es uno de esos casos en los que el enunciado original del problema aparentemente no es muy completo, parece, pero si realmente desea desencadenar en casos que producen caracteres entre 32 y 126, ese es un cambio trivial a la solución que publiqué anteriormente. Tenga en cuenta que mi solución también maneja el caso de múltiples patrones (aunque esta primera versión no manejaría los casos en que algunos de los patrones adyacentes están dentro del rango y otros no).

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

No sería particularmente difícil manejar ese caso (por ejemplo, # 131; # 58; produce "; # 131 ;:" también:

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


Aquí hay una versión basada en boost::regex_token_iterator . El programa reemplaza los NCR decimales leídos de stdin por los caracteres ASCII correspondientes y los imprime en 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''; } }


El framework boost :: spirit parser generator permite crear fácilmente un analizador sintáctico que transforma las NCR deseables.

// 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''); } }

  • compilar:

$ g++ -I/path/to/boost -o spirit_ncr2a spirit_ncr2a.cpp

  • correr:

$ echo "Hello, &#12; here''s a test colon&#58;." | spirit_ncr2a

  • salida:

"Hello, &#12; here''s a test colon:."


Aquí hay otro delineador de Perl (vea la respuesta de @mrree ):

  • un archivo de prueba:

$ cat ent.txt Hello, &#12; here''s a test colon&#58;. Here''s a test semi-colon&#59; ''&#131;''

  • el delineador:

$ perl -pe''s~&#(1?/d/d);~ > sub{ return chr($1) if (31 < $1 && $1 < 127); $& }->()~eg'' ent.txt

  • o usando expresiones regex más específicas:

$ perl -pe"s~&#(1(?:[01][0-9]|2[0-6])|3[2-9]|[4-9][0-9]);~chr($1)~eg" ent.txt

  • ambos de una sola línea producen el mismo resultado:

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