Bienvenidos a este nuevo capítulo de este Curso Gratis de Programación # 24 Estructuras Anidadas.
¿Te gustaría enterarte de cuando lanzamos descuentos y nuevos cursos?
Estructuras anidadas
Podemos encontrarnos con un registo que tenga varios datos, y que a su vez ocurra que uno de esos datos esté formado por varios datos más sencillos. Para hacerlo desde C++, incluiríamos un “struct” dentro de otro, así:
Vamos a hacer un ejemplo completo que use tablas (“arrays”), registros (“struct”) y que además manipule cadenas. La idea va a ser la siguiente: Crearemos un programa que pueda almacenar datos de hasta 1000 ficheros (archivos de ordenador). Para cada fichero, debe guardar los siguientes datos: Nombre del fichero (max 40 letras), Tamaño (en KB, número de 0 a2.000.000.000).
El programa mostrará un menú que permita al usuario las siguientesoperaciones:1- Añadir datos de un nuevo fichero2- Mostrar los nombres de todos los ficheros almacenados3- Mostrar ficheros que sean de más de un cierto tamaño (por ejemplo, 2000 KB). 4- Ver todos los datos de un cierto fichero (a partir de su nombre)5- Salir de la aplicación (como todavía no sabemos almacenar los datos, éstos se perderán). No debería resultar difícil.
Vamos a ver directamente una de las formas en que se podría plantear y luego comentaremos alguna de las mejoras que se podría (incluso se debería) hacer. Una opción que podemos a tomar para resolver este problema es la de contar el número de fichas que tenemos almacenadas, y así podremos añadir de una en una.
Si tenemos0 fichas, deberemos almacenar la siguiente (la primera) en la posición 0; si tenemos dos fichas, serán la 0 y la 1, luego añadiremos en la posición 2; en general, si tenemos “n” fichas, añadiremos cada nueva ficha en la posición “n”. Por otra parte, para revisar todas las fichas, recorreremos desde la posición 0 hasta la n-1, haciendo algo como
o bien algo como
El resto del programa no es difícil: sabemos leer y comparar textos y números. Sólo haremos dos consideraciones: No se comportará correctamente si los textos (nombre del fichero, por ejemplo)contienen espacios, porque aún no sabemos leer textos con espacios. Hemos limitado el número de fichas a 1000, así que, si nos piden añadir, deberíamos asegurarnos antes de que todavía tenemos hueco disponible. Con todo esto, nuestro fuente quedaría así:
Funciona, y hace todo lo que tiene que hacer, pero es mejorable. Por supuesto, en un caso real es habitual que cada ficha tenga que guardar más información que sólo esos dos apartados de ejemplo que hemos previsto esta vez. Si nos muestra todos los datos en pantalla y se trata de muchos datos, puede ocurrir que aparezcan en pantalla tan rápido que no nos dé tiempo a leerlos, así que sería deseable que parase cuando se llenase la pantalla de información (una pausa tras mostrar cada 25 datos).
Por supuesto, se nos pueden ocurrir muchas más preguntas que hacerle sobre nuestros datos. Y además, cuando salgamos del programa se borrarán todos los datos que habíamos tecleado, pero eso es lo único “casi inevitable”, porque aún no sabemos manejar ficheros.
Archivos
Primero defnamos en que consiste un archivo: un archivo es un conjunto de datos (muy a bajo nivel podemos decir que son un conjunto de bits) que se encuentra almacenado en un medio de almacenamiento secundario. Un medio de almacenamiento secundario es accedido a través de un periférico, por lo tanto podemos decir que un archivo se encuentra en una memoria externa.
Generalizando, podemos decir que un archivo es un conjunto de datos que hace referencia a un aspecto de la información, por esto podemos clasifcar a los archivos como: archivos de vídeo, archivos de sonido, archivo de imágenes, etc, también como archivos podemos identifcar a los archivos fuente (programas en texto hechos por el programador) y archivos ejecutables (programas en binario creados por el programa intérprete), pero especialmente podemos identifcar los que más nos interesan: los archivos de datos.
Estructura de un archivo de datos
Hay muchos tipos de archivos de datos, algunos son dependientes de un lenguaje de programación específco o de un programa específco como por ejemplo los archivos DBF que contienen tablas de datos de los lenguajes Dbase, Fox y Clipper, o los archivos MDB que son una base de datos diseñada por el programa ACCESS de Microsoft.
Los archivos de datos que nos interesan son los denominados archivos planos en los cuales se almacena la información agrupadas en registros organizados de forma secuencial, donde cada registro contiene uno o más campos que son datos atómicos equivalentes a los que puede contener una simple variable de memoria. Si el archivo es de los denominados de tamaño de registro fjo, como los archivos DBF mencionados es bastante facil hacer un esquema de su
estructura:
Supongamos que deseamos almacenar datos de un grupo de personas, específcamente de cada persona: el nombre, el Nº de DNI, la edad, el peso y su altura. Entonces podemos representar la estructura el archivo como si se tratara de una tabla bidimensional donde las columnas son los campos y las flas son los registros:
Entonces en el archivo se grabará solamente el contenido no grisado de la tabla en formato ASCII, un dato a continuación de otro y un registro inmediatamente después del otro, por lo que si miramos el archivo con un editor de texto se verá algo como:
En el ejemplo, cada campo se separa por una coma y cada registro tiene un separador con el símbolo ←. Obsérvese que en esta estructura de archivo no se guardan los nombres de los campos ni los números de registro. Comandos y funciones relativas a archivos
En el algoritmo se puede hacer relativamente pocas tareas con un archivo, específcamente defniremos 5 tareas:
Abrir un archivo:
La primer tarea para trabajar con archivos en cualquier lenguaje es ejecutar una instrucción que asocie un archivo en un medio de almacenamiento a un canal de comunicación esto se hace con la instrucción: editor de texto se verá algo como:
Abrir “nombre de archivo” cómo #1
donde “nombre de archivo” es el nombre por el cual el sistema operativo reconoce al archivo, puede ser escrito literalmente o también puede ser almacenado como una cadena de texto en una variable. Por otra parte #1 es el número de canal de comunicaciones abierto, pero en un algoritmo no tendremos por qué limitarnos a procesar un solo archivo, por lo tanto cuando se abra otro archivo llevará el #2, y así sucesivamente.
En algunos lenguajes se suele indicar además si el archivo será abierto para lectura solamente o para escritura para que el sistema operativo sepa como debe manejar los bloqueos (cuando se graba sólo se permite que un único proceso utilice el archivo, por lo tanto se lo bloquea a los demás procesos).
Leer el archivo
Para obtener datos del archivo a fn de procesarlos por el algoritmo se utilizará la instrucción leer pero agregándole el canal desde donde se obtendrá los datos. La idea es se lea un registro completo, que todos los campos de datos se copien en variables del algoritmo. La instrucción será entonces algo como:
Leer #1, variable1, variable2, variable3…
Detectar el final del archivo
Pero cuando se lee secuencialmente un archivo siempre hay que procesar un registro tras otro hasta leer todo el archivo o hasta decidir si no se requieren más datos. Cuando se debe procesar el archivo completo, para detectar el fn al del archivo y no intentar leer datos inexistentes se utiliza una función que devolverá el estado de fn de archivo.
La función que se utiliza en la mayoría de los lenguajes (y que utilizaremos en el pseudocódigo) es la sigla EOF() que corresponde a la palabra inglesa “end of fle”, en castellano deberíamos utilizar algo como FDA() (Fin De Archivo) pero por costumbre seguiremos utilizando las sigla en inglés. Más adelante veremos un ejemplo de cómo utilizarla cuando veamos el proceso de lectura secuencial de un archivo.
Un detalle muy importante es que la función EOF() devuelve un valor verdadero cuando se trató de leer más allá del último registro, o sea, cuando la instrucción LEER trajo datos erróneos que no deberán ser procesados. Por lo tanto el uso más acertado de la instrucción de lectura en el ciclo es posterior al proceso de datos.
Esto suena bastante confuso… ¿Se entiende con lo dicho en el párrafo anterior que debo procesar primero el registro y posteriormente leerlo? Es un absurdo, Lo que se debe entender es que al fnalizar el proceso del registro en curso se debe leer el próximo registro a
procesar, ahí el problema entonces estaría con el primer registro del archivo. Por eso siempre la primera lectura es antes de comenzar el ciclo de proceso de los datos del archivo.
Guardar datos en un archivo
Así como se utiliza la instrucción leer para obtener los datos almacenados en un archivo, para guardarlos se utiliza la instrucción escribir de la siguiente forma:
Escribir #1, variable1, variable2, variable3…
Cierre de un archivo
Cuando se fnaliza la tarea de proceso asociada a un archivo es buena costumbre indicar que se libere el canal e comunicaciones. Si bien en los sistemas operativos modernos esto se hace automáticamente, es preferible no utilizar más recursos de los que necesitamos por una cuestión de rendimiento y de seguridad en los datos.
Para indicar que el canal de comunicaciones se debe cerrar y por lo tanto el sistema operativo debe tomar los recaudos necesarios para que los datos queden asentados en el archivo (es normal que el sistema operativo utilice una memoria caché asociada al archivo) se debe escribir la instrucción cerrar indicando el canal asociaciado, por ejemplo:
Cerrar #1
Algoritmos típicos
Creación de un archivo
El proceso necesario para crear un archivo normalmente implica cargar los datos desde el teclado para luego validarlos y si son correctos guardarlos en el archivo. Repitiendo esta tarea hasta que se ingresen todos los datos. Hagamos un ejemplo, supongamos que queremos tener la lista de nombres, sexo, pesos y alturas de un grupo de personas. Tomaremos de indicación de fn de la carga cuando el nombre sea “X”.
Proceso cargaArchivo
abrir "DATOS.TXT" como #1
Mostrar "Ingrese el nombre: " Sin Saltar
Leer nombre
Mientras mayúsculas(nombre) <> "X" Hacer
Mostrar "Ingrese sexo (M/F): " Sin Saltar
Leer sexo
sexo = mayúsculas(sexo)
si sexo = "M" o sexo="F" Entonces
mostrar "Ingrese Peso (en kg): " Sin Saltar
leer peso
si peso > 0 y peso < 200 Entonces
Mostrar "Ingrese Altura (en m): " Sin Saltar
Leer altura
si altura > 0.40 y altura < 2.5 Entonces
Escribir #1, nombre, sexo, peso, altura
Sino
Mostrar "Error en altura ingresada"
FinSi
Sino
Mostrar "Error en el peso ingresado"
FinSi
Sino
Mostrar "Error en el sexo ingresado"
FinSi
Mostrar "Ingrese el nombre: " Sin Saltar
Leer nombre
FinMientras
cerrar #1
FinProceso
Lectura del archivo creado.
El proceso de lectura es muy similar en su estructura al anterior, cambian las condiciones del ciclo y el origen de la lectura y la escritura, por lo demás es similar. Planteamos el algoritmo para leer el archivo generado, pensemos que los datos no hace falta validarlos pues ya están validados en la carga. Entonces el algoritmo resulta mucho más simple:
Proceso leerArchivo
abrir "DATOS.TXT" como #1
Mostrar "Datos almacenados: "
Leer #1, nomb, sex, pe, alt
Mientras no eof() Hacer
Mostrar nomb, sex, pe, alt
Leer #1, nomb, sex, pe, alt
FinMientras
cerrar #1
FinProceso#
Nota importante: Cuando leemos un archivo debemos utilizar el mismo orden de campos que el algoritmo que se utilizó para su creación. Nótese que se requiere que se respete solamente el orden, no hace falta que los nombres de las variables sean los mismos.
Trabajar con 2 archivos
Veamos un ejemplo donde utilicemos dos archivos. Supongamos que tenemos el archivo con la clasifcación del índice de masa corporal (ver en Wikipedia IMC, https://es.wikipedia.org/wiki/%C3%8Dndice_de_masa_corporal).
Supongamos que el archivo se llama “IMC.DAT” y contiene el estado y el rango de valores (desde y hasta) que abarca ese estado, con los siguientes valores
la forma de calcular el IMC es el peso dividido la altura al cuadrado (o sea IMC = kg/m2).
Pues bien, la idea sería mostrar nuevamente el archivo pero ahora indicando según su IMC en que estado nutricional se encuentra la persona. El algoritmo entonces necesitará procesar dos archivos, el archivo IMC y el cargado con pesos y alturas. Para agilizar el proceso y no tener que andar buscando el rango de IMC dentro del archivo (abriendo el archivo cada vez que se busca un rango) y como los datos del IMC son realmente pocos, entonces cargaremos dos arreglos: un vector y una matriz, luego obtendremos la clasifcación de los datos cargados en memoria.
Proceso muestraIMC
dimension IMC_estado[6]
dimension IMC_valor[6,2]
i <- 1
// primer paso, cargar el archivo IMC dentro de los arreglos
abrir "IMC.DAT" como #1
Leer #1, IMC_estado[i], IMC_valor[i,1], IMC_valor[i,2]
Mientras no eof(#1) Hacer
i <- i + 1
Leer #1, IMC_estado[i], IMC_valor[i,1], IMC_valor[i,2]
FinMientras
cerrar #1
// Ahora procesar el archivo con los pesos y alturas
abrir "DATOS.TXT" como #2
Mostrar "Estado nutricional: "
Leer #2, nombre, sexo, peso, altura
Mientras no eof(#2) Hacer
IMC <- peso / ( altura ^ 2 )
Mostrar nombre, "Estado nutricional: " Sin Saltar
Mostrar estado_IMC(IMC, IMC_Estado, IMC_valor )
Leer #2, nombre, sexo, peso, altura
FinMientras
cerrar #2
FinProceso
Funcion estado <- estado_IMC( IMC, e, v)
i <- 1
// Buscando el rango que corresponde al IMC
// mientras no supere el final del arreglo i <= 6
// y no encuentre que el IMC entra dentro de un rango
Mientras i <= 6 y no ( IMC > v[i,1] y IMC <= v[i,2] ) Hacer
i <- i + 1
FinMientras
si i = 7 Entonces
estado <- "Fuera de rango!"
Sino
estado <- e[i]
FinSi
FinFuncion
Corte de control
Se denomina corte de control a un algoritmo típico en el proceso de datos que consiste en procesar un archivo que obligatoriamente debe estar ordenado por grupos de varios niveles para obtener totales por grupo. Veamos un ejemplo: supongamos que tenemos un archivo que contiene una lista de personal de una empresa que tiene varias sucursales en todo el país, y que esta lista tiene los siguientes campos:
- • Código de provincia
- • Nombre de sucursal
- • Cargo (1-encargado de sucursal, 2- administrativo, 3-operativo)
- • Apellido y Nombres del empleado
- • DNI del empleado
Supongamos que el archivo está ordenado primero por código de provincia, luego por Nombre de sucursal y por último está ordenado por cargo. Además tenemos otro archivo cuyos registros contienen dos campos: código de provincia y nombre de provincia.
Queremos realizar un algoritmo que nos permita conocer cuantos empleados de cada tipo de cargo hay en cada sucursal. Cuantos empleados en total hay en cada provincia y cuantos empleados hay en total en todo el país. De paso, para complicar el algoritmo queremos saber cuantos empleados tiene la sucursal que más empleados tiene.
Digamos que por pantalla queremos que nos salga la información más o menos así:
Listado de Cantidad de personal
Proceso muestraIMC
dimension IMC_estado[6]
dimension IMC_valor[6,2]
i <- 1
// primer paso, cargar el archivo IMC dentro de los arreglos
abrir "IMC.DAT" como #1
Leer #1, IMC_estado[i], IMC_valor[i,1], IMC_valor[i,2]
Mientras no eof(#1) Hacer
i <- i + 1
Leer #1, IMC_estado[i], IMC_valor[i,1], IMC_valor[i,2]
FinMientras
cerrar #1
// Ahora procesar el archivo con los pesos y alturas
abrir "DATOS.TXT" como #2
Mostrar "Estado nutricional: "
Leer #2, nombre, sexo, peso, altura
Mientras no eof(#2) Hacer
IMC <- peso / ( altura ^ 2 )
Mostrar nombre, "Estado nutricional: " Sin Saltar
Mostrar estado_IMC(IMC, IMC_Estado, IMC_valor )
Leer #2, nombre, sexo, peso, altura
FinMientras
cerrar #2
FinProceso
Funcion estado <- estado_IMC( IMC, e, v)
i <- 1
// Buscando el rango que corresponde al IMC
// mientras no supere el final del arreglo i <= 6
// y no encuentre que el IMC entra dentro de un rango
Mientras i <= 6 y no ( IMC > v[i,1] y IMC <= v[i,2] ) Hacer
i <- i + 1
FinMientras
si i = 7 Entonces
estado <- "Fuera de rango!"
Sino
estado <- e[i]
FinSi
FinFuncion
Algoritmo
Un posible algoritmo para solucionar el problemas sería:
Proceso CorteControl
empleadosTotal <- 0
maxEmpleadosSuc <- 0
Dimension Provincias[24,2]
cargaProvincias(Provincias)
Abrir "personal.dat" Como #2
// Leo el primer registro
Leer #2, codProv, NombSuc, Cargo, AyN, DNI
// Titulos generales
Mostrar "Listado de Cantidad de Personal"
// Corte de nivel general
Mientras no eof(#1) Hacer
// Titulos de 1er corte (Provincia)
Mostrar " Provincia: ", nombreProvincia( codProv, Provincias )
// Variable auxiliar para detectar el corte de control
auxCodProv <- codProv
// Acumulador por provincia a 0
empleadosProv <- 0
// Primer Corte de control
Mientras no eof(#1) y auxCodProv = codProv Hacer
// Titulos de 2do corte (Sucursal)
Mostrar " Sucursal: ", NombSuc
// Variable auxiliar para detectar el corte de control
auxNombSuc <- NombSuc
// Acumulador por sucursal a 0
empleadosSuc <- 0
// Segundo Corte de control
Mientras no eof(#1) y auxCodProv = codProv y auxNombSuc = NombSuc Hacer
// Variable auxiliar para detectar el corte de control
auxCargo <- Cargo
// Acumulador por cargo a 0
empleadosCargo <- 0
// Tercer Corte de control
Mientras no eof(#1) y auxCodProv = codProv y auxNombSuc = NombSuc y auxCargo = Cargo Hacer
// Cuento los empleados
empleadosCargo <- empleadosCargo + 1
// Leo el siguiente registro
Leer #2, codProv, NombSuc, Cargo, AyN, DNI
FinMientras
// Total del 3er Corte de control
Mostrar " ", nombreCargo( auxCargo ), empleadosCargo
// Acumulo para el nivel anterior
empleadosSuc <- empleadosSuc + empleadosCargo
FinMientras
// Total del 2do Corte de control
// Busco el máximo de empleados por sucursal
Si maxEmpleadosSuc < empleadosSuc Entonces
maxEmpleadosSuc <- empleadosSuc
FinSi
// Acumulo para el nivel anterior
empleadosProv <- empleadosProv + empleadosSuc
FinMientras
// Total del 1er Corte de control
Mostrar " Total empleados Provincia: " , empleadosProv
// Acumulo para el nivel anterior
empleadosTotal <- empleadosTotal + empleadosProv
FinMientras
// Fin del proceso – Totales Generales
Cerrar #2
Mostrar "Total empleados del País: " , empleadosTotal
Mostrar "La sucursal con más empleados tiene: ", maxEmpleadosSuc, " empleados"
FinProceso
// con este subproceso Cargo el archivo de provincias al vector
SubProceso cargaProvincias( Por Referencia Prov )
i <- 0
Abrir "provincias.dat" Como #1
Leer #1, codProv, nombProv
Mientras no eof(#1) Hacer
i <- i + 1
Prov[i,1] <- codProv
Prov[i,2] <- nombProv
Leer #1, codProv, nombProv
FinMientras
Cerrar #1
FinSubProceso
// Busco el nombre de la provincia por su código
Funcion nombre <- nombreProvincia( codigo, Provincias )
i <- 1
// En el siguiente ciclo busco coincidencia en el código
// y verifico no pasarme del final del vector
Mientras i <= 24 y codigo <> Provincias[i,1] Hacer
i <- i + 1
FinMientras
// Al finalizar el ciclo analizo porqué salió del ciclo
si i = 25 Entonces
// si terminó porque se pasó
nombre <- "Provincia Desconocida"
Sino
// sino terminó porque encontró el código
nombre <- Provincias[i,2]
FinSi
FinFuncion
// Devuelvo el nombre del cargo por su código
Funcion nombre <- nombreCargo( codCargo )
Segun codCargo Hacer
1:
nombre <- "Encargados: "
2:
nombre <- "Administrativos: "
3:
nombre <- "Operativos: "
De Otro Modo:
nombre <- "Código erróneo: "
Fin Segun
FinFuncion
Conclusión
Si prestamos atención al algoritmo, hay dos campos que no han sido utilizados por el algoritmo, Apellido y Nombre y DNI del empleado. Sin embargo como estos datos están físicamente guardados en el archivo y ocupan lugar, deben ser leídos obligatoriamente para que el sistema controle el fnal del registro y no se pierda la sincronización de datos.
No te detengas, sigue avanzando
Aquí tienes un propósito para este 2024 que debes considerar seriamente: si has querido mejorar tus habilidades en hacking, Ciberseguridad y programación ahora es definitivamente el momento de dar el siguiente paso. ¡Desarrolla tus habilidades aprovechando nuestros cursos a un precio increíble y avanza en tu carrera!
Python Practicando. Desde 0 hasta Desarrollador en Python
Aprende Python, donde iniciamos desde 0, sin conocimientos previos hasta desarrollar aplicaciones con mucha practica!
Calificación: 4,6 de 5 (20.833 calificaciones) 249.493 estudiantes Creado por Alvaro Chirou • 1.800.000+ Enrollments Worldwide, Walter Coto || +450,000 Estudiantes Español.
Lo que aprenderás
- Ejercitar la lógica de programación
- Comprender cómo la vida cotidiana puede ser fácilitada o simulada con código
- Aprender programación desde cero
- Usar Visual Studio Code como Editor de Código
- Conocer y aprender el lenguaje de programación Python
- Ser un programador desde cero, sin conocimiento en otro lenguaje o con algo previo
- Mejorar las habilidades de programación, mejorar procesos y fácilitar la comprensión de código
- Preparar un entorno dónde programar en Python
- Operaciones aritméticas y jerarquía de Python
- Manejo de cadenas en Python
- Digitar datos por teclado con Python
- Mostrar Datos por Pantalla al usuario en Python
- Operadores Relacionales de Python
- Operadores Lógicos de Python
- Condicionales en Python
- Estructuras de Datos: Listas, Tuplas y Diccionarios
- Iteraciones y bucles repetitivos de Python
- Segmentar Código y hacerlo más eficaz con las Funciones en Python
- Gestionar posibles errores que puedan dar tus programas
- Programación Orientada a Objetos
- HTML y CSS
- Selenium Web Driver con Python
- Ejercitar todo lo Aprendido con Ejercicios
Este curso incluye:
- 25,5 horas de vídeo bajo demanda
- 21 artículos
- 148 recursos descargables
- Acceso en dispositivos móviles y TV
- Certificado de finalización
Python es Hoy uno de los lenguajes más utilizados por Excelencia.
Esto se debe por su simpleza al momento de Desarrollar aplicaciones.
Por su capacidad de procesamiento a altas velocidades con grandes volúmenes de información.
Es un increíble lenguaje con el cual si no sabes programar, podrás aprender.
Y si ya sabes desarrollar, te aconsejo aprenderlo ya que en el mercado cada vez se solicitan más desarrolladores en Python.
Aspirar al trabajo que desean, o mejorar sus ingresos con un aumento de salario.
Python se utiliza para muchisimas cosas como:
- Machine Learning
- Data Science
- Inteligencia Artificial.
- Y mucho más!
En este curso te acompañare en el proceso por el cual aprenderás las bases del lenguaje, para luego determinar qué camino quieres seguir.
Te invito que me acompañes a conocer este Gran Lenguaje!
Aprende con nuestros más de 100 cursos que tenemos disponibles para vos
¿Te gustaría enterarte de cuando lanzamos descuentos y nuevos cursos?
Sobre los autores
Álvaro Chirou
Yo soy Álvaro Chirou, tengo más de 20 Años de experiencia trabajando en Tecnología, eh dado disertaciones en eventos internacionales como OWASP, tengo más de 1.800.000 estudiantes en Udemy y 100 formaciones profesionales impartidas en la misma. Puedes serguirme en mis redes:
Laprovittera Carlos
Soy Laprovittera Carlos. Con más de 20 años de experiencia en IT brindo Educación y Consultoría en Seguridad de la Información para profesionales, bancos y empresas. Puedes saber más de mi y de mis servicios en mi sitio web: laprovittera.com y seguirme en mis redes:
¿Quieres iniciarte en hacking y ciberseguridad pero no sabes por dónde empezar? Inicia leyendo nuestra guia gratuita: https://achirou.com/como-iniciarse-en-ciberseguridad-y-hacking-en-2024/ que te lleva de 0 a 100. Desde los fundamentos más básicos, pasando por cursos, recursos y certificaciones hasta cómo obtener tu primer empleo.