C++
Expresiones regulares
Buscar..
Introducción
Las expresiones regulares (a veces llamadas expresiones regulares o expresiones regulares ) son una sintaxis textual que representa los patrones que se pueden hacer coincidir en las cadenas operadas.
Las expresiones regulares, introducidas en c ++ 11 , pueden opcionalmente admitir una matriz de retorno de cadenas coincidentes u otra sintaxis textual que defina cómo reemplazar patrones coincidentes en cadenas operadas.
Sintaxis
- regex_match // Devuelve si la secuencia de caracteres completa coincidió con la expresión regular, opcionalmente capturando en un objeto coincidente
- regex_search // Devuelve si una parte de la secuencia de caracteres coincidió con la expresión regular, opcionalmente capturando en un objeto coincidente
- regex_replace // Devuelve la secuencia de caracteres de entrada modificada por una expresión regular a través de una cadena de formato de reemplazo
- regex_token_iterator // Inicializado con una secuencia de caracteres definida por iteradores, una lista de índices de captura para iterar, y una expresión regular. La desreferenciación devuelve la coincidencia actualmente indexada de la expresión regular. El incremento se mueve al siguiente índice de captura o, si está actualmente en el último índice, restablece el índice y dificulta la próxima aparición de una coincidencia de expresiones regulares en la secuencia de caracteres
- regex_iterator // Inicializado con una secuencia de caracteres definida por iteradores y una expresión regular. La anulación de referencia devuelve la parte de la secuencia de caracteres con la que coinciden actualmente todas las expresiones regulares. Incrementando encuentra la siguiente aparición de una coincidencia de expresiones regulares en la secuencia de caracteres
Parámetros
Firma | Descripción |
---|---|
bool regex_match(BidirectionalIterator first, BidirectionalIterator last, smatch& sm, const regex& re, regex_constraints::match_flag_type flags) | BidirectionalIterator es cualquier iterador personaje que proporciona a los operadores de incremento y decremento smatch pueden ser cmatch o cualquier otra otra variante de match_results que acepte el tipo de BidirectionalIterator la smatch argumento puede omitirse si no son necesarios los resultados de la expresión regular Devuelve si re coincide con todo el carácter secuencia definida por first y last |
bool regex_match(const string& str, smatch& sm, const regex re&, regex_constraints::match_flag_type flags) | string puede ser una const char* o una string valor L, las funciones que aceptan una string valor R se eliminan de forma explícita. smatch puede ser cmatch o cualquier otra variante de match_results que acepte el tipo de str smatch argumento smatch puede omitirse si los resultados de la expresión regular no son necesarios Devuelve si re coincidir con la secuencia de caracteres completa definida por str |
Ejemplos básicos de regex_match y regex_search
const auto input = "Some people, when confronted with a problem, think \"I know, I'll use regular expressions.\""s;
smatch sm;
cout << input << endl;
// If input ends in a quotation that contains a word that begins with "reg" and another word begining with "ex" then capture the preceeding portion of input
if (regex_match(input, sm, regex("(.*)\".*\\breg.*\\bex.*\"\\s*$"))) {
const auto capture = sm[1].str();
cout << '\t' << capture << endl; // Outputs: "\tSome people, when confronted with a problem, think\n"
// Search our capture for "a problem" or "# problems"
if(regex_search(capture, sm, regex("(a|d+)\\s+problems?"))) {
const auto count = sm[1] == "a"s ? 1 : stoi(sm[1]);
cout << '\t' << count << (count > 1 ? " problems\n" : " problem\n"); // Outputs: "\t1 problem\n"
cout << "Now they have " << count + 1 << " problems.\n"; // Ouputs: "Now they have 2 problems\n"
}
}
Ejemplo de regex_replace
Este código toma varios estilos de llaves y los convierte en One True Brace Style :
const auto input = "if (KnR)\n\tfoo();\nif (spaces) {\n foo();\n}\nif (allman)\n{\n\tfoo();\n}\nif (horstmann)\n{\tfoo();\n}\nif (pico)\n{\tfoo(); }\nif (whitesmiths)\n\t{\n\tfoo();\n\t}\n"s;
cout << input << regex_replace(input, regex("(.+?)\\s*\\{?\\s*(.+?;)\\s*\\}?\\s*"), "$1 {\n\t$2\n}\n") << endl;
regex_token_iterator Ejemplo
Un std::regex_token_iterator
proporciona una herramienta tremenda para extraer elementos de un archivo de valores separados por comas . Aparte de las ventajas de la iteración, este iterador también es capaz de capturar comas escapadas donde otros métodos luchan:
const auto input = "please split,this,csv, ,line,\\,\n"s;
const regex re{ "((?:[^\\\\,]|\\\\.)+)(?:,|$)" };
const vector<string> m_vecFields{ sregex_token_iterator(cbegin(input), cend(input), re, 1), sregex_token_iterator() };
cout << input << endl;
copy(cbegin(m_vecFields), cend(m_vecFields), ostream_iterator<string>(cout, "\n"));
Un problema notable con los iteradores de expresiones regulares es que el argumento de regex
debe ser un valor de l. Un valor R no funcionará .
Ejemplo de regex_iterator
Cuando el procesamiento de las capturas debe realizarse de forma iterativa, un regex_iterator
es una buena opción. La desreferenciación de un regex_iterator
devuelve un match_result
. Esto es ideal para capturas condicionales o capturas que tienen interdependencia. Digamos que queremos tokenizar algún código C ++. Dado:
enum TOKENS {
NUMBER,
ADDITION,
SUBTRACTION,
MULTIPLICATION,
DIVISION,
EQUALITY,
OPEN_PARENTHESIS,
CLOSE_PARENTHESIS
};
Podemos tokenizar esta cadena: const auto input = "42/2 + -8\t=\n(2 + 2) * 2 * 2 -3"s
con un regex_iterator
como este:
vector<TOKENS> tokens;
const regex re{ "\\s*(\\(?)\\s*(-?\\s*\\d+)\\s*(\\)?)\\s*(?:(\\+)|(-)|(\\*)|(/)|(=))" };
for_each(sregex_iterator(cbegin(input), cend(input), re), sregex_iterator(), [&](const auto& i) {
if(i[1].length() > 0) {
tokens.push_back(OPEN_PARENTHESIS);
}
tokens.push_back(i[2].str().front() == '-' ? NEGATIVE_NUMBER : NON_NEGATIVE_NUMBER);
if(i[3].length() > 0) {
tokens.push_back(CLOSE_PARENTHESIS);
}
auto it = next(cbegin(i), 4);
for(int result = ADDITION; it != cend(i); ++result, ++it) {
if (it->length() > 0U) {
tokens.push_back(static_cast<TOKENS>(result));
break;
}
}
});
match_results<string::const_reverse_iterator> sm;
if(regex_search(crbegin(input), crend(input), sm, regex{ tokens.back() == SUBTRACTION ? "^\\s*\\d+\\s*-\\s*(-?)" : "^\\s*\\d+\\s*(-?)" })) {
tokens.push_back(sm[1].length() == 0 ? NON_NEGATIVE_NUMBER : NEGATIVE_NUMBER);
}
Un problema notable con los iteradores de expresiones regulares es que el argumento de regex
debe ser un valor L, un valor R no funcionará: Visual Studio regex_iterator Bug?
Dividiendo una cuerda
std::vector<std::string> split(const std::string &str, std::string regex)
{
std::regex r{ regex };
std::sregex_token_iterator start{ str.begin(), str.end(), r, -1 }, end;
return std::vector<std::string>(start, end);
}
split("Some string\t with whitespace ", "\\s+"); // "Some", "string", "with", "whitespace"
Cuantificadores
Digamos que nos dan const string input
como un número de teléfono para validar. Podríamos comenzar requiriendo una entrada numérica con cero o más cuantificadores : regex_match(input, regex("\\d*"))
o uno o más cuantificadores : regex_match(input, regex("\\d+"))
Pero ambos se quedan cortos si la input
contiene una cadena numérica no válida como: "123" Usemos uno o más cuantificadores para asegurarnos de que obtengamos al menos 7 dígitos:
regex_match(input, regex("\\d{7,}"))
Esto garantizará que obtendremos al menos un número de teléfono de dígitos, pero la input
también podría contener una cadena numérica que sea demasiado larga como: "123456789012". Así que vamos con un cuantificador entre n y m para que la input
tenga al menos 7 dígitos pero no más de 11:
regex_match(input, regex("\\d{7,11}"));
Esto nos acerca, pero todavía se aceptan cadenas numéricas ilegales que están en el rango de [7, 11], como: "123456789" Así que hagamos que el código de país sea opcional con un cuantificador perezoso :
regex_match(input, regex("\\d?\\d{7,10}"))
Es importante tener en cuenta que el cuantificador perezoso hace coincidir la menor cantidad de caracteres posible , por lo que la única manera de que este carácter coincida es si ya hay 10 caracteres que se han comparado con \d{7,10}
. (Para hacer coincidir el primer carácter con avidez, habríamos tenido que hacer: \d{0,1}
.) El cuantificador perezoso se puede agregar a cualquier otro cuantificador.
Ahora, ¿cómo haríamos el código de área opcional y solo aceptaríamos un código de país si el código de área estaba presente?
regex_match(input, regex("(?:\\d{3,4})?\\d{7}"))
En esta expresión regular, el \d{7}
requiere 7 dígitos. Estos 7 dígitos están precedidos opcionalmente por 3 o 4 dígitos.
Tenga en cuenta que no agregamos el cuantificador perezoso : , el \d{3,4}?\d{7}
\d{3,4}?
hubiera coincidido con 3 o 4 caracteres, prefiriendo 3. En su lugar, hacemos que el grupo que no captura coincida como máximo una vez, prefiriendo no coincidir. Causando una discrepancia si la input
no incluyó el código de área como: "1234567".
En conclusión del tema del cuantificador, me gustaría mencionar el otro cuantificador adjunto que puede usar, el cuantificador posesivo . El cuantificador perezoso o el cuantificador posesivo se pueden agregar a cualquier cuantificador. La única función del cuantificador posesivo es ayudar al motor de expresiones regulares diciéndole, tome estos caracteres con avidez y nunca los abandone, incluso si causa que la expresión regular falle . Esto, por ejemplo, no tiene mucho sentido: regex_match(input, regex("\\d{3,4}+\\d{7}))
porque una input
como:" 1234567890 "no coincidiría con \d{3,4}+
siempre coincidirá con 4 caracteres, incluso si la coincidencia con 3 hubiera permitido que la expresión regular sea exitosa.
El cuantificador posesivo se utiliza mejor cuando el token cuantificado limita el número de caracteres comparables . Por ejemplo:
regex_match(input, regex("(?:.*\\d{3,4}+){3}"))
Se puede usar para hacer coincidir si la input
contenía alguno de los siguientes:
123 456 7890
123-456-7890
(123) 456-7890
(123) 456 - 7890
Pero cuando esta expresión regular realmente brilla es cuando la input
contiene una entrada ilegal :
12345 - 67890
Sin el cuantificador posesivo, el motor de expresiones regulares debe regresar y probar cada combinación de .*
Y 3 o 4 caracteres para ver si puede encontrar una combinación compatible. Con el cuantificador posesiva las expresiones regulares comienza donde el cuantificador 2º posesivo fue apagado, el carácter '0', y el motor de expresiones regulares trata de ajustar el .*
Para permitir \d{3,4}
para que coincida; cuando no se puede, la expresión regular simplemente falla, no se realiza un seguimiento de retroceso para ver si anteriormente .*
ajuste podría haber permitido una coincidencia.
Anclas
C ++ proporciona solo 4 anclas:
-
^
que afirma el inicio de la cadena -
$
que afirma el final de la cadena -
\b
que afirma un carácter\W
o el principio o el final de la cadena -
\B
que afirma un carácter\w
Digamos, por ejemplo, que queremos capturar un número con su signo:
auto input = "+1--12*123/+1234"s;
smatch sm;
if(regex_search(input, sm, regex{ "(?:^|\\b\\W)([+-]?\\d+)" })) {
do {
cout << sm[1] << endl;
input = sm.suffix().str();
} while(regex_search(input, sm, regex{ "(?:^\\W|\\b\\W)([+-]?\\d+)" }));
}
Una nota importante aquí es que el ancla no consume ningún carácter.