Saltar al contenido principal
LibreTexts Español

2.4: Entrada y salida de archivos

  • Page ID
    55090
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Hasta ahora, todos los datos con los que hemos estado trabajando han sido “codificados” en nuestros programas. En la vida real, sin embargo, estaremos buscando datos de archivos externos.

    Podemos acceder a datos que existen en un archivo externo (un archivo de texto, generalmente) usando un manejador de archivo, un tipo especial de tipo de datos al que podemos conectarnos usando una fuente externa. En su forma más simple, un manejador de archivo para leer datos funciona como una tubería: el sistema operativo pone datos (generalmente cadenas) en un extremo, y extraemos datos del otro extremo, generalmente una línea a la vez. Los manejadores de archivos para escribir en archivos mueven los datos en la dirección opuesta.

    imagen

    Para usar un manejador de archivo en Python, tenemos que decirle al intérprete que queremos utilizar algunas funciones de entrada/salida poniendo primero el comando especial import io cerca de la parte superior de nuestro programa. Este comando importa el módulo io, poniendo a disposición funciones adicionales que existen dentro de él. Estas funciones también utilizan una sintaxis de punto para especificar el módulo en el que existen, que es confusamente similar a la sintaxis de puntos discutida anteriormente para los métodos. Para asociar un gestor de archivo llamado fhandle con el archivo “filename.txt", por ejemplo, el comando es fhandle = io.open (” filename.txt “, “rU”), donde open () es una función en el módulo io importado. La r indica que estamos creando un manejador de archivo de solo lectura, y la U le permite saber al intérprete que queremos una interpretación “universal” para nuevas líneas, para permitir la lectura de archivos creados en sistemas Microsoft Windows o sistemas similares a Unix. [1] El tipo de datos de manejador de archivo tiene un método llamado .readline (), que devuelve la siguiente línea disponible de la tubería como una cadena; así, line = fhandle.readline () extrae una cadena (línea) del manejador de archivo y la asigna a line.

    Vamos a crear un archivo pequeño con tres líneas, cada una de las cuales contiene un ID de gen, una pestaña y un valor de contenido de GC correspondiente.

    II.4_2_PY_35_IDS_List

    Ahora vamos a crear un programa que lea e imprima los datos. Cuando terminemos de leer datos de un archivo, necesitamos ejecutar el método .close () del identificador de archivo. El uso de .close () hace que el identificador del archivo avise al sistema operativo de que hemos terminado de leer los datos del identificador. En este punto el sistema operativo es libre de eliminar la “tubería” de back-end y cualquier dato que pueda haber en él.

    II.4_3_PY_36_LEER_LINES

    Si el archivo no existe o no es legible por el programa, obtendrá un IOError que indica No dicho archivo o directorio o Permiso denegado con el nombre del archivo que intentó abrir. [2] Debido a que nuestro archivo existe, podemos ejecutar con éxito el código e inspeccionar los resultados:

    II.4_4_PY_36_2_LEER_LINES_OUT

    Aquí hay algunas cosas a tener en cuenta. Primero, cada vez que llamamos a fhandle.readline (), el identificador del archivo devuelve una cadena diferente: la siguiente cadena esperando ser sacada de la tubería. Segundo, vemos que la salida contiene nuestras tres líneas, pero separadas por líneas en blanco: esto se debe a que ya hay caracteres de “nueva línea” en el archivo. No vemos estos caracteres de nueva línea, pero podemos representarlos nosotros mismos si queremos, en una cadena con el código de control\ n. Del mismo modo, los caracteres de tabulación tienen un código de control como\ t. El archivo es en realidad una sola cadena serial, y el método.readline () pide al identificador del archivo que devuelva todo hasta e incluyendo el siguiente\ n.

    II.4_5_Newlines_en_str

    Al final, la razón de las líneas en blanco en nuestra salida es que la función print (), para nuestra conveniencia, agrega un\ n al final de cualquier cadena que imprimimos (de lo contrario, la mayor parte de nuestra salida estaría en la misma línea). Así, cada línea leída del archivo se está imprimiendo con dos\ n caracteres. Aprenderemos a imprimir sin una nueva línea adicional más adelante, cuando aprendamos a escribir datos en archivos. Por ahora, resolveremos el problema eliminando espacios en blanco iniciales y finales (espacios, tabulaciones y nuevas líneas) pidiéndole a una cadena que ejecute su método.strip ().

    II.4_6_py_37_string_strip

    Aunque estamos llamando de nuevo a import io, es solo por claridad: un programa solo necesita importar un módulo una vez (y generalmente estos se recopilan cerca de la parte superior del programa). Aquí está la salida modificada:

    II.4_7_py_37_2_string_strip_out

    Si te sientes aventurero (¡y deberías!) , puede probar el encadenamiento de métodos, donde la sintaxis de punto para los métodos se puede agregar siempre que el método anterior devuelva el tipo correcto.

    II.4_8_PY_38_método_encadenamiento

    Para vincular esto a conceptos anteriores, después de haber extraído cada línea y despojado del espacio en blanco final, podemos usar .split (“\ t”) para dividir cada línea en una lista de cadenas. A partir de ahí, podemos usar float () para convertir el segundo elemento de cada uno en un tipo float, y calcular el contenido medio de GC.

    II.4_9_PY_38_2_MEAN_GC

    Lo anterior imprime el contenido promedio de GC de las tres líneas como 0.523333333. (Debido a que .strip () también devuelve una cadena, podríamos tener más métodos encadenados, como en line1_list = fhandle.readline () .strip () .split (“\ t”).)

    Debido a que los manejadores de archivos funcionan como una tubería, no permiten el “acceso aleatorio”; podemos obtener el siguiente bit de datos del extremo de la tubería, pero eso es todo. [3] Uno podría pensar que un comando como line5 = fhandle [4] funcionaría, pero en su lugar produciría un error como TypeError: '_io.bufferedReader' el objeto no tiene atributo '__getitem__'.

    Por otro lado, al igual que las listas, los manejadores de archivos son iterables, lo que significa que podemos usar un for-loop para acceder a cada línea en orden. Un programa simple para leer líneas de un archivo e imprimirlas una a la vez (sin líneas en blanco adicionales) podría verse así:

    II.4_10_py_39_lines_loop

    Al igual que .readline (), usando un for-loop extrae líneas de la tubería. Entonces, si llama a .readline () dos veces en un controlador de archivo adjunto a un archivo con 10 líneas, y luego ejecuta un for-loop en ese controlador de archivo, el for-loop iterará sobre las 8 líneas restantes. Esta llamada podría ser útil si desea eliminar una línea de encabezado de una tabla de texto antes de procesar las líneas restantes con un bucle, por ejemplo.

    Redacción de datos

    Escribir datos en un archivo funciona de la misma manera que leer datos: abrimos un controlador de archivo (que de nuevo funciona como una tubería), y llamamos a un método en el controlador llamado .write () para escribirle cadenas. En este caso, en lugar de usar el parámetro “ru” al llamar a io.open (), usaremos “w” para indicar que queremos escribir en el archivo. Tenga en cuenta: cuando abre un identificador de archivo para escribir de esta manera, sobrescribe cualquier contenido existente del archivo. Si prefieres agregar al archivo, puedes usar “a”. [4]

    A diferencia de la función print (), el método .write () de un identificador de archivo no incluye automáticamente un carácter de nueva línea adicional “\ n”. Así, si deseas escribir varias líneas en un manejador de archivo, tienes que agregarlas tú mismo. Aquí hay un ejemplo que imprime los números del 0 al 9 en un archivo, uno por línea.

    imagen

    Como se mencionó anteriormente, un identificador de archivo abierto para escritura funciona como una tubería que es establecida por el sistema operativo. Al escribir, sin embargo, colocamos datos en la tubería, y el sistema operativo extrae los datos del otro lado, escribiéndolos en el archivo en el disco.

    imagen

    Debido a que el sistema operativo está ocupado manejando solicitudes similares para muchos programas en el sistema, es posible que no llegue a extraer los datos de la tubería y escribir en el archivo en el disco de inmediato. Entonces, es importante que recordemos llamar al método .close () en el controlador de archivo cuando terminemos de escribir datos; esto le dice al sistema operativo que cualquier información que quede en la tubería debe ser vaciada y guardada en el disco, y que la estructura de la tubería se puede limpiar. Si nuestro programa se estrellara (o se matara) antes de que el mango se cerrara correctamente, es posible que los datos en la tubería nunca se escriban.

    Computación de una media

    Pongamos en práctica muchas de las habilidades que hemos aprendido hasta ahora para calcular la media de los valores E a partir de la salida de BLAST, que se formatea como una tabla de texto simple con pestañas que separan las columnas. Así es como se ve el archivo, pz_blastx_yeast_top1.txt, en menos -S. Aunque el lector tendrá que tomar nuestra palabra para ello, la undécima columna de este archivo contiene valores E como 5e-14, 2e-112 y 1e-18.

    II.4_13_PY_41_BLAST_LESS

    A la hora de resolver un problema como este, suele ser una buena idea escribir primero, en inglés simple (o en el lenguaje de no programación que prefieras), la estrategia que pretendes utilizar para resolver el problema. La estrategia aquí será la siguiente: la media de un conjunto de números se define como su suma, dividida por el recuento de los mismos. Tendremos que mantener al menos dos variables importantes, eval_sum y counter. Después de abrir el archivo con io.open (), podemos recorrer las líneas y extraer cada valor E. (Esto requerirá limpiar la línea con .strip (), dividirla en pedazos usando .split (“\ t”), y finalmente convertir el valor E en un flotador en lugar de usar la cadena.) Por cada línea que veamos, podemos agregar el valor E extraído a la variable eval_sum, y agregaremos 1 a la variable de contador también. Al final, podemos simplemente reportar eval_sum/counter.

    A menudo ayuda convertir esta estrategia en algo a medio camino entre el lenguaje natural y el código, llamado pseudocódigo, que puede ayudar enormemente a organizar tus pensamientos, particularmente para programas complejos:

    import io
    open fhandle
    
    counter = 0
    sum_eval = 0.0
    
    for each line in fhandle
    linestripped = line.strip()
    break into a list of strings with line_list = linestripped.split("\t")
    eval as a string is in line_list at index 10 (11th column)
    add float(eval) to sum_eval and save in sum_eval
    add 1 to count and save in count
      
    mean = sum_eval divided by counter
    print("mean is " + mean)

    Con el pseudocódigo esbozado, podemos escribir el código real para nuestro programa. Cuando se ejecuta, imprime de manera confiable La media es: 1.37212611293e-08.

    II.4_14_PY_42_BLAST_Ejemplo

    Tenga en cuenta que el código Python real (en blast_mean.py) terminó pareciéndose bastante al pseudocódigo, este es uno de los puntos de venta frecuentemente citados para Python. (Por esta razón, también saltaremos el paso de pseudocódigo para la mayoría de los ejemplos en este libro, aunque todavía puede ser una técnica valiosa a la hora de programar en cualquier lenguaje).

    Esto puede parecer una buena cantidad de trabajo para calcular una media simple, pero es una consecuencia de escribir software “desde cero”, ¡y tenemos que empezar por alguna parte! Adicionalmente, el beneficio de aprender estas técnicas dará sus frutos a largo plazo a la hora de resolver problemas novedosos.

    El Proceso de Programación

    Aunque el proceso de diseñar una estrategia (y pseudocódigo) parece tedioso, es muy recomendable. A medida que avanzas en tus habilidades, tus estrategias y pseudocódigo se volverán más concisos y de nivel superior, pero nunca debes saltarte los pasos de planeación antes de codificar. (Por otro lado, también existe el peligro de una planificación excesiva, pero esto afecta más a menudo a equipos de codificadores que trabajan en proyectos grandes).

    Hay otra cosa que vale la pena señalar en este punto: programas como el anterior casi nunca se escriben de arriba a abajo en su totalidad, ¡al menos no sin requerir una depuración significativa! Puede olvidar que el resultado de dividir una cadena da como resultado una lista de cadenas, lo que resulta en un error en una línea como eval_sum = eval_sum + eval_str porque eval_sum es un float, mientras eval_str es una cadena. Después de encontrar y corregir este error, puede encontrar otro error más si intenta imprimir con una línea como print (“La media es:" + media), porque nuevamente los tipos no coinciden y no se pueden concatenar. Después de todo esto, quizás encuentres que la media resultante es inesperadamente un número grande, como 131.18, solo para enterarte que fue porque usaste accidentalmente eval_str = line_list [11], olvidando que los índices de lista comienzan en 0.

    Hay dos estrategias para evitar largas cadenas de errores molestos como este, y errores más difíciles de encontrar (y por lo tanto más peligrosos) que dan como resultado una salida incorrecta que obviamente no es incorrecta. La primera estrategia es escribir solo unas pocas líneas a la vez, y probar las líneas recién agregadas con declaraciones print () que revelan si están haciendo lo que se supone que deben hacer. En el ejemplo anterior, puede escribir algunas líneas y hacer algunas impresiones para asegurarse de que pueda abrir con éxito el manejador del archivo y leerlo (si el archivo es grande, cree una versión más pequeña para probar). Luego escribe un bucle for-loop simple y haz algo de impresión para asegurarte de que puedes recorrer con éxito las líneas del archivo y dividirlas en listas de cadenas. Continúe rellenando parte del código en el for-loop, nuevamente imprimiendo y probando para asegurarse de que el código esté haciendo lo que cree que es. Y así sucesivamente a través del desarrollo iterativo.

    La segunda estrategia, para evitar los errores más peligrosos que no son inmediatamente obvios, es probar y desarrollar tus programas usando pequeños archivos para los que puedas calcular la respuesta a mano (o algún otro proceso confiable). Cuando sabe por pruebas que su programa o incluso solo partes de su programa produce la salida prevista, es mucho menos probable que los errores ocultos se escapen por las grietas. Algunas personas producen varios insumos pequeños que representan la variedad de diferentes situaciones de entrada, ejecutan las pruebas y comparan los resultados con los resultados esperados de manera automatizada. Esto se conoce como “pruebas unitarias”, una práctica común en el desarrollo de software profesional.

    Por último, ¡recuerda que cada programador, sin importar su nivel de experiencia, se frustra de vez en cuando! Esto es completamente normal, y una buena estrategia cuando esto sucede es dar un breve paseo, o incluso tomar un descanso para el día, recogiendo de nuevo al día siguiente. Algunos encuentran que programan eficientemente por la mañana, otros a altas horas de la noche. Hagas lo que hagas, evita la tentación de programar por prueba y error, ajustando sin pensar el código con la esperanza de que produzca la salida que deseas. Incluso si tienes éxito de esta manera (lo que rara vez sucede), es probable que produzca código con errores insidiosos, y bien puede ser ilegible incluso para ti mismo dentro de unos días.

    Ejercicios

    1. Escriba un programa Python para calcular la desviación estándar de muestra de los valores E en el archivo pz_blastx_yeast_top1.txt. Como recordatorio, la desviación estándar de la muestra se define como la raíz cuadrada de la suma de las diferencias cuadradas de la media, dividida por el número de valores menos 1:

      <span translate=\ [stdev =\ sqrt {\ frac {\ suma_ {eval_ {i}\ en evals} (eval_ {i} - media) ^ {2}} {n-1}}\]” title="Rendido por Quicklatex.com” src=” https://bio.libretexts.org/@api/deki...1dc65f8_l3.png "/>

      Para lograr esto, necesitarás hacer dos pasadas sobre los datos: una como en el ejemplo para calcular la media, y otra para calcular la suma de las diferencias cuadradas. Esto significa que necesitarás acceder a los valores de E dos veces. En lugar de cerrar y volver a abrir el archivo de datos, debe crear una lista inicialmente vacía a la que pueda agregar cada valor E (en su primera pasada sobre los datos) para su uso posterior.

      Para calcular la raíz cuadrada de un flotador, deberá importar el módulo matemático llamando a import math cerca de la parte superior de su programa. Entonces la función math.sqrt () devolverá la raíz cuadrada de un float; por ejemplo, math.sqrt (3.0) devolverá el float 1.7320508.

    2. Si a_list es una lista, entonces b_list = reverse (a_list) crea un “listreverseiterator” que permite a uno recorrer los elementos con un bucle for en orden inverso. Usando esta información, escriba un programa llamado reverse_blast.py que lea el contenido de pz_blastx_yeast_top1.txt y escriba las líneas en orden inverso a un archivo llamado pz_blastx_yeast_top1_reversed.txt.
    3. Un quine (después del lógico y filósofo W. V. Quine) es un programa no vacío que imprime exactamente su propio código fuente. Utilice el módulo io y los manejadores de archivo para escribir una quine llamada quine.py. (Técnicamente, a los programas Quine no se les permite abrir archivos. ¿Se puede escribir un programa que imprima su propio código fuente sin utilizar el módulo io?)

    1. Python incluye una función simple open () que funciona en gran medida de la misma manera que la io.open () que se muestra aquí. En las versiones más recientes de Python (Python 3.0 y superiores), open () es en realidad un atajo a io.open (), mientras que en versiones anteriores las dos operan de manera ligeramente diferente. Para mayor consistencia, sigamos con un io.open () explícito, que funciona igual en todas las versiones recientes (Python 2.7 y superiores). Con respecto al parámetro “U”, los sistemas basados en Microsoft y Unix utilizan diferentes codificaciones para nuevas líneas. El parámetro “U” maneja esto de manera transparente, de manera que todo tipo de nuevas líneas pueden considerarse como basadas en Unix,\ n, incluso si el archivo en cuestión fue creado en un sistema Microsoft.
    2. En muchos idiomas, es una buena práctica probar si un identificador de archivo se abrió con éxito y, si no, salir del programa con un error. También podríamos hacer tales pruebas en Python, pero debido a que Python por defecto se cierra con un error informativo en esta situación de todos modos, no nos preocuparemos tanto por verificar los errores aquí. Python también incluye un especial con palabra clave que maneja tanto la apertura como el cierre del archivo: La documentación deII.4_15_PY_42_B_con Python recomienda usar con de esta manera, ya que es un poco más seguro en casos de bloqueos del programa que ocurren cuando un controlador de archivo está abierto. Aún así, dejamos esta idea a una nota al pie de página, ya que lo abierto y el cierre son explícitos y directos.
    3. En el uso más sofisticado de los mangos de archivo, nuestra analogía de tuberías se descompone. Es posible “rebobinar” o “adelantar” la posición actual del sistema operativo en el archivo que se está leyendo, si sabemos hasta dónde debe moverse la “cabeza de lectura” para llegar a los datos que queremos. Quizás una mejor analogía para los mangos de archivos sería una cinta magnética de cassette anticuada, tal vez una analogía adecuada dado que los archivos alguna vez se almacenaban en tales cintas.
    4. También es posible abrir un manejador de archivo para lectura y escritura simultáneas. No lo cubriremos aquí, ya que esa funcionalidad es menos necesaria.

    This page titled 2.4: Entrada y salida de archivos is shared under a CC BY-NC-SA license and was authored, remixed, and/or curated by Shawn T. O’Neil (OSU Press) .