Leyendo y escribiendo archivos
Quizás una de las tareas principales de todo código es leer (y escribir) información en archivos. Para ello F# utiliza en su base las funciones que el ecosistema .NET ofrece, con numerosas alternativas. Veamos algunas de ellas:
let fileName = "../data/El reloj de arena.txt"
let readFile (fileName: string) =
let lines = File.ReadAllLines(fileName)
lines
let lines = readFile fileName
La función ReadAllLines
devuelve una secuencia, donde cada línea es un elemento:
lines
|> Seq.iteri (fun i l -> printfn "%d: %s" i l)
0: El reloj de arena
1: JLB
2:
3: Está bien que se mida con la dura
4: Sombra que una columna en el estío
5: Arroja o con el agua de aquel río
6: En que Heráclito vio nuestra locura
7:
8: El tiempo, ya que al tiempo y al destino
9: Se parecen los dos: la imponderable
10: Sombra diurna y el curso irrevocable
11: Del agua que prosigue su camino.
12:
13: Está bien, pero el tiempo en los desiertos
14: Otra substancia halló, suave y pesada,
15: Que parece haber sido imaginada
16: Para medir el tiempo de los muertos.
17:
18: Surge así el alegórico instrumento
19: De los grabados de los diccionarios,
20: La pieza que los grises anticuarios
21: Relegarán al mundo ceniciento
22:
23: Del alfil desparejo, de la espada
24: Inerme, del borroso telescopio,
25: Del sándalo mordido por el opio
26: Del polvo, del azar y de la nada.
27:
28: ¿Quién no se ha demorado ante el severo
29: Y tétrico instrumento que acompaña
30: En la diestra del dios a la guadaña
31: Y cuyas líneas repitió Durero?
32:
33: Por el ápice abierto el cono inverso
34: Deja caer la cautelosa arena,
35: Oro gradual que se desprende y llena
36: El cóncavo cristal de su universo.
37:
38: Hay un agrado en observar la arcana
39: Arena que resbala y que declina
40: Y, a punto de caer, se arremolina
41: Con una prisa que es del todo humana.
42:
43: La arena de los ciclos es la misma
44: E infinita es la historia de la arena;
45: Así, bajo tus dichas o tu pena,
46: La invulnerable eternidad se abisma.
47:
48: No se detiene nunca la caída
49: Yo me desangro, no el cristal. El rito
50: De decantar la arena es infinito
51: Y con la arena se nos va la vida.
52:
53: En los minutos de la arena creo
54: Sentir el tiempo cósmico: la historia
55: Que encierra en sus espejos la memoria
56: O que ha disuelto el mágico Leteo.
57:
58: El pilar de humo y el pilar de fuego,
59: Cartago y Roma y su apretada guerra,
60: Simón Mago, los siete pies de tierra
61: Que el rey sajón ofrece al rey noruego,
62:
63: Todo lo arrastra y pierde este incansable
64: Hilo sutil de arena numerosa.
65: No he de salvarme yo, fortuita cosa
66: De tiempo, que es materia deleznable.
lines
|> Seq.where (fun l -> l.Contains("tiempo"))
|> Seq.iteri (fun i l -> printfn "%d: %s" i l)
0: El tiempo, ya que al tiempo y al destino
1: Está bien, pero el tiempo en los desiertos
2: Para medir el tiempo de los muertos.
3: Sentir el tiempo cósmico: la historia
4: De tiempo, que es materia deleznable.
Utilizando algunos métodos de seq
, podemos procesar el poema y separarlo en estrofas:
lines
|> Seq.skip 3
|> Seq.chunkBySize 5
|> Seq.map (fun estrofa ->
estrofa
|> Seq.where (fun verso -> not (String.IsNullOrEmpty(verso)))
)
|> Seq.mapi (fun i e -> (i+1,e))
|> Seq.iter (fun (i,l) -> printfn "%d: %A" i l)
1: seq
["Está bien que se mida con la dura"; "Sombra que una columna en el estío";
"Arroja o con el agua de aquel río"; "En que Heráclito vio nuestra locura"]
2: seq
["El tiempo, ya que al tiempo y al destino";
"Se parecen los dos: la imponderable"; "Sombra diurna y el curso irrevocable";
"Del agua que prosigue su camino."]
3: seq
["Está bien, pero el tiempo en los desiertos";
"Otra substancia halló, suave y pesada,"; "Que parece haber sido imaginada";
"Para medir el tiempo de los muertos."]
4: seq
["Surge así el alegórico instrumento"; "De los grabados de los diccionarios,";
"La pieza que los grises anticuarios"; "Relegarán al mundo ceniciento"]
5: seq
["Del alfil desparejo, de la espada"; "Inerme, del borroso telescopio,";
"Del sándalo mordido por el opio"; "Del polvo, del azar y de la nada."]
6: seq
["¿Quién no se ha demorado ante el severo";
"Y tétrico instrumento que acompaña"; "En la diestra del dios a la guadaña";
"Y cuyas líneas repitió Durero?"]
7: seq
["Por el ápice abierto el cono inverso"; "Deja caer la cautelosa arena,";
"Oro gradual que se desprende y llena"; "El cóncavo cristal de su universo."]
8: seq
["Hay un agrado en observar la arcana"; "Arena que resbala y que declina";
"Y, a punto de caer, se arremolina"; "Con una prisa que es del todo humana."]
9: seq
["La arena de los ciclos es la misma";
"E infinita es la historia de la arena;"; "Así, bajo tus dichas o tu pena,";
"La invulnerable eternidad se abisma."]
10: seq
["No se detiene nunca la caída"; "Yo me desangro, no el cristal. El rito";
"De decantar la arena es infinito"; "Y con la arena se nos va la vida."]
11: seq
["En los minutos de la arena creo"; "Sentir el tiempo cósmico: la historia";
"Que encierra en sus espejos la memoria";
"O que ha disuelto el mágico Leteo."]
12: seq
["El pilar de humo y el pilar de fuego,";
"Cartago y Roma y su apretada guerra,";
"Simón Mago, los siete pies de tierra";
"Que el rey sajón ofrece al rey noruego,"]
13: seq
["Todo lo arrastra y pierde este incansable"; "Hilo sutil de arena numerosa.";
"No he de salvarme yo, fortuita cosa";
"De tiempo, que es materia deleznable."]
A diferencia de ReadAllLines
, la función ReadAllText
lee el archivo como un string
let readFileAsTxt (fileName: string) =
let lines = File.ReadAllText(fileName)
lines
let poema = readFileAsTxt fileName
printfn "%A" poema
"El reloj de arena
JLB
Está bien que se mida con la dura
Sombra que una columna en el estío
Arroja o con el agua de aquel río
En que Heráclito vio nuestra locura
El tiempo, ya que al tiempo y al destino
Se parecen los dos: la imponderable
Sombra diurna y el curso irrevocable
Del agua que prosigue su camino.
Está bien, pero el tiempo en los desiertos
Otra substancia halló, suave y pesada,
Que parece haber sido imaginada
Para medir el tiempo de los muertos.
Surge así el alegórico instrumento
De los grabados de los diccionarios,
La pieza que los grises anticuarios
Relegarán al mundo ceniciento
Del alfil desparejo, de la espada
Inerme, del borroso telescopio,
Del sándalo mordido por el opio
Del polvo, del azar y de la nada.
¿Quién no se ha demorado ante el severo
Y tétrico instrumento que acompaña
En la diestra del dios a la guadaña
Y cuyas líneas repitió Durero?
Por el ápice abierto el cono inverso
Deja caer la cautelosa arena,
Oro gradual que se desprende y llena
El cóncavo cristal de su universo.
Hay un agrado en observar la arcana
Arena que resbala y que declina
Y, a punto de caer, se arremolina
Con una prisa que es del todo humana.
La arena de los ciclos es la misma
E infinita es la historia de la arena;
Así, bajo tus dichas o tu pena,
La invulnerable eternidad se abisma.
No se detiene nunca la caída
Yo me desangro, no el cristal. El rito
De decantar la arena es infinito
Y con la arena se nos va la vida.
En los minutos de la arena creo
Sentir el tiempo cósmico: la historia
Que encierra en sus espejos la memoria
O que ha disuelto el mágico Leteo.
El pilar de humo y el pilar de fuego,
Cartago y Roma y su apretada guerra,
Simón Mago, los siete pies de tierra
Que el rey sajón ofrece al rey noruego,
Todo lo arrastra y pierde este incansable
Hilo sutil de arena numerosa.
No he de salvarme yo, fortuita cosa
De tiempo, que es materia deleznable."
Entonces, podemos usar el método Split
del módulo System.String
para separar el texto en versos usando el caracter \n
(final de línea):
let p = poema.Split("\n")
printfn "%A" p
[|"El reloj de arena "; "JLB"; ""; "Está bien que se mida con la dura";
"Sombra que una columna en el estío"; "Arroja o con el agua de aquel río";
"En que Heráclito vio nuestra locura"; "";
"El tiempo, ya que al tiempo y al destino";
"Se parecen los dos: la imponderable"; "Sombra diurna y el curso irrevocable";
"Del agua que prosigue su camino."; "";
"Está bien, pero el tiempo en los desiertos";
"Otra substancia halló, suave y pesada,"; "Que parece haber sido imaginada";
"Para medir el tiempo de los muertos."; "";
"Surge así el alegórico instrumento"; "De los grabados de los diccionarios,";
"La pieza que los grises anticuarios"; "Relegarán al mundo ceniciento"; "";
"Del alfil desparejo, de la espada"; "Inerme, del borroso telescopio,";
"Del sándalo mordido por el opio"; "Del polvo, del azar y de la nada."; "";
"¿Quién no se ha demorado ante el severo";
"Y tétrico instrumento que acompaña"; "En la diestra del dios a la guadaña";
"Y cuyas líneas repitió Durero?"; ""; "Por el ápice abierto el cono inverso";
"Deja caer la cautelosa arena,"; "Oro gradual que se desprende y llena";
"El cóncavo cristal de su universo."; "";
"Hay un agrado en observar la arcana"; "Arena que resbala y que declina";
"Y, a punto de caer, se arremolina"; "Con una prisa que es del todo humana.";
""; "La arena de los ciclos es la misma";
"E infinita es la historia de la arena;"; "Así, bajo tus dichas o tu pena,";
"La invulnerable eternidad se abisma."; ""; "No se detiene nunca la caída";
"Yo me desangro, no el cristal. El rito"; "De decantar la arena es infinito";
"Y con la arena se nos va la vida."; ""; "En los minutos de la arena creo";
"Sentir el tiempo cósmico: la historia";
"Que encierra en sus espejos la memoria"; "O que ha disuelto el mágico Leteo.";
""; "El pilar de humo y el pilar de fuego,";
"Cartago y Roma y su apretada guerra,"; "Simón Mago, los siete pies de tierra";
"Que el rey sajón ofrece al rey noruego,"; "";
"Todo lo arrastra y pierde este incansable"; "Hilo sutil de arena numerosa.";
"No he de salvarme yo, fortuita cosa"; "De tiempo, que es materia deleznable."|]
Escribiendo en archivos
Vamos a editar un poco el poema para que se pueda ver como Markdown.
let poemaConFormato =
lines
|> Seq.map (fun l -> l.Replace("tiempo","_tiempo_")) // Cambiamos tiempo por itálica
poemaConFormato
|> Seq.iter (fun v -> printfn "%s" v)
El reloj de arena
JLB
Está bien que se mida con la dura
Sombra que una columna en el estío
Arroja o con el agua de aquel río
En que Heráclito vio nuestra locura
El _tiempo_, ya que al _tiempo_ y al destino
Se parecen los dos: la imponderable
Sombra diurna y el curso irrevocable
Del agua que prosigue su camino.
Está bien, pero el _tiempo_ en los desiertos
Otra substancia halló, suave y pesada,
Que parece haber sido imaginada
Para medir el _tiempo_ de los muertos.
Surge así el alegórico instrumento
De los grabados de los diccionarios,
La pieza que los grises anticuarios
Relegarán al mundo ceniciento
Del alfil desparejo, de la espada
Inerme, del borroso telescopio,
Del sándalo mordido por el opio
Del polvo, del azar y de la nada.
¿Quién no se ha demorado ante el severo
Y tétrico instrumento que acompaña
En la diestra del dios a la guadaña
Y cuyas líneas repitió Durero?
Por el ápice abierto el cono inverso
Deja caer la cautelosa arena,
Oro gradual que se desprende y llena
El cóncavo cristal de su universo.
Hay un agrado en observar la arcana
Arena que resbala y que declina
Y, a punto de caer, se arremolina
Con una prisa que es del todo humana.
La arena de los ciclos es la misma
E infinita es la historia de la arena;
Así, bajo tus dichas o tu pena,
La invulnerable eternidad se abisma.
No se detiene nunca la caída
Yo me desangro, no el cristal. El rito
De decantar la arena es infinito
Y con la arena se nos va la vida.
En los minutos de la arena creo
Sentir el _tiempo_ cósmico: la historia
Que encierra en sus espejos la memoria
O que ha disuelto el mágico Leteo.
El pilar de humo y el pilar de fuego,
Cartago y Roma y su apretada guerra,
Simón Mago, los siete pies de tierra
Que el rey sajón ofrece al rey noruego,
Todo lo arrastra y pierde este incansable
Hilo sutil de arena numerosa.
No he de salvarme yo, fortuita cosa
De _tiempo_, que es materia deleznable.
File.WriteAllLines("../El reloj de arena.md",poemaConFormato2)
Para que quede escrito tal cual poema, tendríamos que agregar una línea vacía entre cada verso, porque una estrofa tal como está en el archivo quedaría así
El tiempo, ya que al tiempo y al destino Se parecen los dos: la imponderable Sombra diurna y el curso irrevocable Del agua que prosigue su camino.
Una manera es intercalar una línea vacía entre cada verso:
let poemaConFormato2 =
lines
|> Seq.map (fun l -> l.Replace("tiempo","_tiempo_"))
|> Seq.map (fun l -> seq {l ; ""})
|> Seq.concat
poemaConFormato2
|> Seq.iter (fun v -> printfn "%s" v)
El reloj de arena
JLB
Está bien que se mida con la dura
Sombra que una columna en el estío
Arroja o con el agua de aquel río
En que Heráclito vio nuestra locura
El _tiempo_, ya que al _tiempo_ y al destino
Se parecen los dos: la imponderable
Sombra diurna y el curso irrevocable
Del agua que prosigue su camino.
Está bien, pero el _tiempo_ en los desiertos
Otra substancia halló, suave y pesada,
Que parece haber sido imaginada
Para medir el _tiempo_ de los muertos.
Surge así el alegórico instrumento
De los grabados de los diccionarios,
La pieza que los grises anticuarios
Relegarán al mundo ceniciento
Del alfil desparejo, de la espada
Inerme, del borroso telescopio,
Del sándalo mordido por el opio
Del polvo, del azar y de la nada.
¿Quién no se ha demorado ante el severo
Y tétrico instrumento que acompaña
En la diestra del dios a la guadaña
Y cuyas líneas repitió Durero?
Por el ápice abierto el cono inverso
Deja caer la cautelosa arena,
Oro gradual que se desprende y llena
El cóncavo cristal de su universo.
Hay un agrado en observar la arcana
Arena que resbala y que declina
Y, a punto de caer, se arremolina
Con una prisa que es del todo humana.
La arena de los ciclos es la misma
E infinita es la historia de la arena;
Así, bajo tus dichas o tu pena,
La invulnerable eternidad se abisma.
No se detiene nunca la caída
Yo me desangro, no el cristal. El rito
De decantar la arena es infinito
Y con la arena se nos va la vida.
En los minutos de la arena creo
Sentir el _tiempo_ cósmico: la historia
Que encierra en sus espejos la memoria
O que ha disuelto el mágico Leteo.
El pilar de humo y el pilar de fuego,
Cartago y Roma y su apretada guerra,
Simón Mago, los siete pies de tierra
Que el rey sajón ofrece al rey noruego,
Todo lo arrastra y pierde este incansable
Hilo sutil de arena numerosa.
No he de salvarme yo, fortuita cosa
De _tiempo_, que es materia deleznable.
O, siguiendo estas instrucciones, agregar dos espacios al final del texto.
let poemaConFormato3 =
lines
|> Seq.map (fun l -> l.Replace("tiempo","_tiempo_"))
|> Seq.map (fun l -> l + " ")
File.WriteAllLines("../data/El reloj de arena 2.md",poemaConFormato3)
Para aprender más sobre IO, pueden ver este blog de Ángel Muñoz.