Skip to content

Instantly share code, notes, and snippets.

@pcolladosoto
Created April 4, 2023 08:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pcolladosoto/ccc0d99a8416827147b28b13873e3351 to your computer and use it in GitHub Desktop.
Save pcolladosoto/ccc0d99a8416827147b28b13873e3351 to your computer and use it in GitHub Desktop.
Procesador de vectores separados por espacios
0 0.5
1 10
2 20.5
3 30
4 40.5
5 50
6 60.5
7 70
8 80.5
9 90
/* Vamos a mostrar algún mensaje por pantalla.
* Más información -> https://en.cppreference.com/w/cpp/header/iostream
*/
#include <iostream>
/* Tenemos que poder abrir archivos. Esta cabecera define `std::fstream`.
* Más información -> https://en.cppreference.com/w/cpp/header/basic_fstream
*/
#include <fstream>
/* Vamos a usar expresiones regulares para sacar los datos.
* Para jugar con ello https://regex101.com está genial.
* Más información -> https://en.cppreference.com/w/cpp/header/regex
*/
#include <regex>
/* Vamos a usar muchas cadenas (i.e. strings).
* Más información -> https://en.cppreference.com/w/cpp/header/string
*/
#include <string>
/* En vez de la clase `mivector` vamos a usar la clase estándar `std::vector`.
* La idea es prácticamente la misma, pero al ser estándar este programa funcionará
* con mucha más facilidad en otros equipos y situaciones. En el siguiente enlace tienes
* información para ponerte a funcionar con vectores: https://www.programiz.com/cpp-programming/vectors
* Más información -> https://en.cppreference.com/w/cpp/header/vector
*/
#include <vector>
/* También vamos a hacer una versión con nuestro `mivectort`. Recuerda que debes
* poder importarlo, para lo que lo deberás tener copiado en `/usr/local/include`.
* Entiendo que ya estará ahí con lo que vimos en clase :P
*/
#include <mivectort>
/*
* Este objeto contiene la expresión regular que extrae los números
* de cada línea del archivo de entrada. En el siguiente enlace tienes
* una versión con la que puedes jugar: https://regex101.com/r/WO5A8m/2
*/
const std::regex lineRegex("([^\\s]+)\\s+([^\\s]+)");
/*
* Esta función recibe el nombre del archivo que queremos abrir para
* abrirlo, leer todos los datos, y devolver un vector de vectores de floats.
*/
std::vector<std::vector<float>> parse_file(std::string fname) {
// Definimos un flujo al que conectar el archivo.
std::fstream infile;
/* Vamos a abrir el archivo.
* open() -> https://en.cppreference.com/w/cpp/io/basic_fstream/open
* std::ios::openmode -> https://en.cppreference.com/w/cpp/io/ios_base/openmode
*/
infile.open(fname, std::ios::in);
// En este vector iremos almacenando los datos de cada linea que leamos.
std::vector<std::vector<float>> data;
// En este vector iremos almacenando los datos de cada línea.
std::vector<float> lineData;
// En esta cadena iremos guardando cada línea que vayamos leyendo.
std::string line;
// Este objeto irá almacenando los números que vayamos encontrando.
std::smatch dataMatch;
/*
* La función `std::getline` irá leyendo el archivo `infile` línea por línea. Cada
* vez que lea una linea la almacenará en `line` para que la podamos procesar. Cuando
* se haya leído todo el archivo, `std::getline` provoca que el bucle se termine.
*/
while (std::getline(infile, line)) {
// Veamos en qué línea estamos...
// std::cout << "Procesando la linea: " << line << std::endl;
// Vamos a aplicar la expresión regular a la linea.
std::regex_match(line, dataMatch, lineRegex);
// Veamos si hemos capturado dos números. El «match» contiene
// los dos números y otra captura que es la línea entera; de ahí
// la comprobación con · en vez de con 2.
if (dataMatch.size() != 3) {
std::cout << "\tNo se ha podido procesar esta línea...\n";
// A por la siguiente línea...
continue;
}
/* Añadimos la primera componente al final del vector en el que almacenamos
* los datos de la línea. La función `std::stof` convierte la primera componente
* en un `float` (de ahí la f). Hay otras versiones para double y demás. En
* https://en.cppreference.com/w/cpp/string/basic_string/stof hay más información.
*/
lineData.push_back(std::stof(dataMatch[1]));
// Hacemos lo mismo con la segunda componente;
lineData.push_back(std::stof(dataMatch[2]));
// Añadimos el vector intermedio al general en el que tenemos todos los datos.
data.push_back(lineData);
// Limpiamos el vector que usamos para cada línea para la siguiente iteración.
lineData.clear();
}
// Y devolvemos los datos ya guardados.
return data;
}
/*
* Esta función es equivalente a la anterior, pero hace uso de nuestra clase `mivectort`.
*/
mivector<mivector<float>> parse_file_mv(std::string fname) {
std::fstream infile;
infile.open(fname, std::ios::in);
mivector<mivector<float>> data;
// mivector<float> lineData;
std::string line;
std::smatch dataMatch;
while (std::getline(infile, line)) {
// std::cout << "Procesando la linea: " << line << std::endl;
std::regex_match(line, dataMatch, lineRegex);
if (dataMatch.size() != 3) {
std::cout << "\tNo se ha podido procesar esta línea...\n";
continue;
}
data.append(mivector<float>({std::stof(dataMatch[1]), std::stof(dataMatch[2])}));
}
return data;
}
/*
* Y ahora con `mivector` y usando simplemente la el operador de extracción `>>`.
*/
mivector<mivector<float>> parse_file_eo(std::string fname) {
std::fstream infile;
infile.open(fname, std::ios::in);
mivector<mivector<float>> data;
// Iremos almacenando los datos en este vector temporal con dos componentes.
mivector<float> currComponents(2);
// Tenemos que llevar la cuenta de componentes: cada 2 debemos
// insertar una file nueva.
int nComponents = 0;
// Aquí iremos guardando el valor de cada componente.
float tmpComponent;
// La gracia de todo esto es que el operador de extracción por defecto
// para en cualquier espacio en blanco. Podemos usar eso en nuestro beneficio.
// Como siempre, cuando se agote el archivo (i.e. `infile`) el bucle se para.
while (infile >> tmpComponent) {
// Aquí usamos mucho el operador módulo, que devuelve el resto de la
// división entera. Así, cuando tengamos un número par de componentes
// sobreescribimos la primera componente del vector de las líneas y cuando
// sea impar sobreescribimos la segunda.
currComponents[nComponents % 2] = tmpComponent;
// Actualizamos la cuenta para que el operador módulo vaya funcionando. La
// actualización viene después de la operación anterior para que las
// cosas cuadren.
nComponents++;
// Cada dos componentes tenemos que guardar una nueva entrada.
if (nComponents % 2 == 0)
data.append(currComponents);
}
// ¡Y listo!
return data;
}
/* Para compilarlo podemos usar lo de siempre:
* $ g++ -std=c++11 -o pvector.ex vector_parser.cpp
*
* Para ejecutarlo podemos usar:
* $ ./pvector.ex
*/
int main() {
std::cout << "Vamos a procesar el archivo `vector.txt`...\n";
// Usando vectores de la librería estándar.
std::vector<std::vector<float>> parsedData = parse_file("vector.txt");
std::cout << "Resultado del procesado con `parse_file()`:\n";
// Vamos a imprimir el vector que nos ha quedado.
for (const std::vector<float>& row : parsedData) {
std::cout << "\t( ";
for (const float& component : row)
std::cout << component << " ";
std::cout << ")\n";
}
// Usando `mivector` con expresiones regulares.
mivector<mivector<float>> parsedDataMv = parse_file_mv("vector.txt");
std::cout << "Resultado del procesado con `parse_file_mv()`:\n";
for (int i = 0; i < parsedDataMv.dimension(); i++)
std::cout << "\t(" << parsedDataMv[i][0] << ", " << parsedDataMv[i][1] << ")\n";
// Usando `mivector` con el operador de extracción.
mivector<mivector<float>> parsedDataEo = parse_file_eo("vector.txt");
std::cout << "Resultado del procesado con `parse_file_eo()`:\n";
for (int i = 0; i < parsedDataEo.dimension(); i++)
std::cout << "\t(" << parsedDataEo[i][0] << ", " << parsedDataEo[i][1] << ")\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment