unicode - utf8 - lectura y escritura de archivos en python
¿Cómo hackear GHCi(o Abrazos) para que imprima caracteres Unicode sin guardar? (7)
Mire el problema: normalmente, en el entorno interactivo Haskell, los caracteres Unicode no latinos (que forman parte de los resultados) se imprimen con escapes, incluso si la configuración regional permite tales caracteres (en oposición a la salida directa a través de putStrLn
, putChar
que se ve bien y legible): los ejemplos muestran GHCi y Hugs98:
$ ghci
GHCi, version 7.0.1: http://www.haskell.org/ghc/ :? for help
Prelude> "hello: привет"
"hello: /1087/1088/1080/1074/1077/1090"
Prelude> ''Я''
''/1071''
Prelude> putStrLn "hello: привет"
hello: привет
Prelude> :q
Leaving GHCi.
$ hugs -98
__ __ __ __ ____ ___ _________________________________________
|| || || || || || ||__ Hugs 98: Based on the Haskell 98 standard
||___|| ||__|| ||__|| __|| Copyright (c) 1994-2005
||---|| ___|| World Wide Web: http://haskell.org/hugs
|| || Bugs: http://hackage.haskell.org/trac/hugs
|| || Version: September 2006 _________________________________________
Hugs mode: Restart with command line option +98 for Haskell 98 mode
Type :? for help
Hugs> "hello: привет"
"hello: /1087/1088/1080/1074/1077/1090"
Hugs> ''Я''
''/1071''
Hugs> putStrLn "hello: привет"
hello: привет
Hugs> :q
[Leaving Hugs]
$ locale
LANG=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=
$
Podemos adivinar que se debe a que print
y show
se usan para formatear el resultado, y estas funciones hacen todo lo posible para formatear los datos de una manera canónica y máximamente portátil, por lo que prefieren escapar de los caracteres extraños (quizás, incluso se detalla) en un estándar para Haskell):
$ ghci
GHCi, version 7.0.1: http://www.haskell.org/ghc/ :? for help
Prelude> show ''Я''
"''//1071''"
Prelude> :q
Leaving GHCi.
$ hugs -98
Type :? for help
Hugs> show ''Я''
"''//1071''"
Hugs> :q
[Leaving Hugs]
$
Pero aún sería bueno si supiéramos cómo hackear GHCi o Hugs para imprimir estos caracteres de una manera bastante legible para los humanos, es decir, directamente, sin escabullirse. Esto se puede apreciar cuando se usa el entorno interactivo Haskell con fines educativos, para un tutorial / demostración de Haskell frente a un público no inglés al que se le quiere mostrar algo de Haskell en su lenguaje humano.
¡En realidad, no solo es útil para fines educativos sino también para la depuración! Cuando tiene funciones definidas en cadenas que representan palabras de otros idiomas, con caracteres que no son ASCII. Entonces, si el programa es específico del idioma, y solo las palabras de otro idioma tienen sentido como los datos, y usted tiene funciones que están definidas solo en tales palabras, es importante para la depuración en GHCi ver estos datos.
Para resumir mi pregunta: ¿Qué formas de hackear los entornos interactivos Haskell existentes para una impresión más amigable de Unicode en los resultados están ahí? ("Más amistoso" significa incluso "más simple" en mi caso: me gustaría print
en GHCi o Abrazos para mostrar caracteres no latinos de la manera directa y simple como lo hacen putChar
, putStrLn
, es decir, sin putChar
).
(Quizás, además de GHCi y Hugs98, también echaré un vistazo a los modos existentes de Emacs para interactuar con Haskell y ver si pueden presentar los resultados de una manera bonita, sin escabullirse).
Opción 1 (mala):
Modificar esta línea de código:
https://github.com/ghc/packages-base/blob/ba98712/GHC/Show.lhs#L356
showLitChar c s | c > ''/DEL'' = showChar ''//' (protectEsc isDec (shows (ord c)) s)
Y recompilar ghc.
Opción 2 (mucho trabajo):
Cuando el tipo GHCi comprueba una declaración analizada, termina en tcRnStmt
que se basa en mkPlan
(ambos en https://github.com/ghc/ghc/blob/master/compiler/typecheck/TcRnDriver.lhs ). Esto intenta escribir compruebe varias variantes de la instrucción que se escribió incluyendo:
let it = expr in print it >> return [coerce HVal it]
Específicamente:
print_it = L loc $ ExprStmt (nlHsApp (nlHsVar printName) (nlHsVar fresh_it))
(HsVar thenIOName) placeHolderType
Todo lo que podría necesitar cambiar aquí es printName
(que se une a System.IO.print
). Si, en cambio, está relacionado con algo así como printGhci
que se implementó como:
class ShowGhci a where
showGhci :: a -> String
...
-- Bunch of instances?
instance ShowGhci Char where
... -- The instance we want to be different.
printGhci :: ShowGhci a => a -> IO ()
printGhci = putStrLn . showGhci
Ghci podría entonces cambiar lo que se imprime al poner diferentes instancias en contexto.
Ahora que sé ghci -interactive-print
, esta es una gran característica. ¡Muchas gracias por escribir la pregunta y las respuestas! Por cierto, las impresoras bonitas existentes que puedo encontrar en la web tienen algunos casos de esquina , y el problema de escribir un buen show
Unicode resultó ser más complicado de lo que parece.
Por lo tanto, decidí escribir un paquete Haskell unicode-show para este propósito, que (con suerte) imprime bien las cadenas de esquinas y los tipos compuestos .
Mis mejores deseos, que este paquete sea útil para las personas que buscaron este Q & A :)
Ha habido algún progreso con este tema; gracias a bravit (Vitaly Bragilevsky) !:
- trabajo en progreso: Даёшь кириллицу в GHCi! - 2 - alrededor del ticket relacionado;
- el resultado del trabajo: Даёшь кириллицу en GHCi! - 3 - con el parche y otro para los documentos de bravit (Vitaly Bragilevsky). Estas mejoras se han confirmado: 1 y 2 .
Probablemente incorporado en GHC 7.6.1. (¿Lo es?..)
Cómo hacer que se imprima cirílico ahora :
El parámetro pasado a GHCi debe ser una función que puede imprimir cirílico. No se ha encontrado tal función en Hackage. Entonces, tenemos que crear un contenedor simple, como por ahora:
module UPPrinter where import System.IO import Text.PrettyPrint.Leijen upprint a = (hPutDoc stdout . pretty) a >> putStrLn ""
Y ejecute
ghci
esta manera:ghci -interactive-print=UPPrinter.upprint UPPrinter
Por supuesto, esto se puede escribir de una vez por todas en
.ghci
.
Problema práctico: crear un agradable Show
alternativo
Entonces, ahora hay un problema práctico: ¿qué usar como sustituto del Show
estándar que escapa a los símbolos deseados?
Usar el trabajo de otros: otras lindas impresoras
Arriba, se sugiere Text.PrettyPrint.Leijen
, probablemente porque se sabe que no escapan tales símbolos en cadenas.
Nuestro propio Show basado en Show - atractivo, pero no práctico
¿Qué tal escribir nuestro propio Show
, por ejemplo, ShowGhci
como se sugirió en una respuesta aquí? ¿Es práctico?
Para guardar el trabajo que define las instancias para una clase alternativa de Show
(como ShowGhci
), uno podría sentirse tentado de usar las instancias existentes de Show
por defecto, solo redefinir la instancia para String
y Char
. Pero eso no funcionará, porque si usas showGhci = show
, entonces para cualquier información compleja que contenga cadenas show
es "hard-compiled" para llamar a old show
para mostrar la cadena. Esta situación requiere la capacidad de pasar diferentes diccionarios que implementan la misma interfaz de clase a funciones que usan esta interfaz ( show
pasará a sub show
s). Cualquier extensión GHC para esto?
Basarse en Show
y querer redefinir solo las instancias de Char
y String
no es muy práctico, si desea que sea tan "universal" (ampliamente aplicable) como Show
.
Programa de re-análisis
Una solución más práctica (y breve) se encuentra en otra respuesta aquí: analizar el resultado del show
para detectar caracteres y cadenas, y volver a formatearlos. (Aunque parece un poco feo semánticamente, la solución es corta y segura en la mayoría de los casos (si no hay citas usadas para otros fines en el show
; no debe ser el caso para cosas estándar, porque la idea de show
es ser más, o -a menos correcto Haskell analizable.)
Tipos semánticos en tus programas
Y un comentario más.
En realidad, si nos importa depurar en GHCi (y no simplemente demostrar Haskell y querer tener un resultado bonito), la necesidad de mostrar letras que no sean ASCII debe venir de alguna presencia inherente de estos caracteres en su programa (de lo contrario, para la depuración, puede sustituirlos por caracteres latinos o no les importa que se muestren los códigos). En otras palabras, hay algún SIGNIFICADO en estos caracteres o cadenas desde el punto de vista del dominio del problema. (Por ejemplo, recientemente me he involucrado en el análisis gramatical de ruso, y las palabras rusas como parte de un diccionario de ejemplo estaban "inherentemente" presentes en mi programa. Su trabajo solo tendría sentido con estas palabras específicas. Así que necesitaba léelos cuando se depura.)
Pero mire, si las cuerdas tienen algún significado, entonces ya no son cuerdas simples; son datos de un tipo significativo. Probablemente, el programa sería aún mejor y más seguro si declarara un tipo especial para este tipo de significados.
Y luego, ¡hurra !, simplemente defines tu instancia de Show
para este tipo. Y está bien con la depuración de su programa en GHCi.
Como ejemplo, en mi programa de análisis gramatical, he hecho:
newtype Vocable = Vocable2 { ortho :: String } deriving (Eq,Ord)
instance IsString Vocable -- to simplify typing the values (with OverloadedStrings)
where fromString = Vocable2 . fromString
y
newtype Lexeme = Lexeme2 { lemma :: String } deriving (Eq,Ord)
instance IsString Lexeme -- to simplify typing the values (with OverloadedStrings)
where fromString = Lexeme2 . fromString
(el extra fromString
aquí es porque podría cambiar la representación interna de String
a ByteString
o lo que sea)
Además de poder show
bien, me sentí más seguro porque no podría mezclar diferentes tipos de palabras al componer mi código.
Las cosas cambiarán en la próxima versión 7.6.1 de Ghci, ya que proporciona una nueva opción de Ghci llamada: -interactive-print. Aquí está copiado de ghc-manual: (Y escribí myShow y myPrint de la siguiente manera)
2.4.8. Using a custom interactive printing function
[New in version 7.6.1] By default, GHCi prints the result of expressions typed at the prompt using the function System.IO.print. Its type signature is Show a => a -> IO (), and it works by converting the value to String using show.
This is not ideal in certain cases, like when the output is long, or contains strings with non-ascii characters.
The -interactive-print flag allows to specify any function of type C a => a -> IO (), for some constraint C, as the function for printing evaluated expressions. The function can reside in any loaded module or any registered package.
As an example, suppose we have following special printing module:
module SpecPrinter where
import System.IO
sprint a = putStrLn $ show a ++ "!"
The sprint function adds an exclamation mark at the end of any printed value. Running GHCi with the command:
ghci -interactive-print=SpecPrinter.sprinter SpecPrinter
will start an interactive session where values with be printed using sprint:
*SpecPrinter> [1,2,3]
[1,2,3]!
*SpecPrinter> 42
42!
A custom pretty printing function can be used, for example, to format tree-like and nested structures in a more readable way.
The -interactive-print flag can also be used when running GHC in -e mode:
% ghc -e "[1,2,3]" -interactive-print=SpecPrinter.sprint SpecPrinter
[1,2,3]!
module MyPrint (myPrint, myShow) where
-- preparing for the 7.6.1
myPrint :: Show a => a -> IO ()
myPrint = putStrLn . myShow
myShow :: Show a => a -> String
myShow x = con (show x) where
con :: String -> String
con [] = []
con li@(x:xs) | x == ''/"'' = ''/"'':str++"/""++(con rest)
| x == ''/''' = ''/''':char:''/''':(con rest'')
| otherwise = x:con xs where
(str,rest):_ = reads li
(char,rest''):_ = reads li
Y funcionan bien:
*MyPrint> myPrint "asf萨芬速读法"
"asf萨芬速读法"
*MyPrint> myPrint "asdffasdfd"
"asdffasdfd"
*MyPrint> myPrint "asdffa撒旦发"
"asdffa撒旦发"
*MyPrint> myPrint ''此''
''此''
*MyPrint> myShow ''此''
"''/27492''"
*MyPrint> myPrint ''此''
''此''
Lo que sería ideal es un parche para ghci que le permita al usuario :set
una función para mostrar resultados que no sean show
. No existe tal característica actualmente. Sin embargo, la sugerencia de Don para una macro a :def
(con o sin el paquete de texto) no está nada mal.
Podría cambiar a usar el paquete ''text'' para IO. P.ej
Prelude> :set -XOverloadedStrings
Prelude> Data.Text.IO.putStrLn "hello: привет"
hello: привет
El paquete forma parte de la distribución estándar de Haskell, la plataforma Haskell , y proporciona un tipo de texto Unicode inmutable, empaquetado y eficiente con operaciones IO. Muchas codificaciones son compatibles .
Usando un archivo .ghci, puede configurar -XOverloadStrings para que esté activado por defecto, y escribir una macro :def
para introducir un comando :text
que muestre un valor solo a través del text
. Eso funcionaria.
Una forma de hackear esto es envolver GHCi en un contenedor de shell que lea sus caracteres Unicode extendidos y no seleccionados. Esta no es la forma de Haskell, por supuesto, pero cumple su función :)
Por ejemplo, esto es un contenedor ghci-esc
que usa sh
y python3
(3 es importante aquí):
#!/bin/sh
ghci "$@" | python3 -c ''
import sys
import re
def tr(match):
s = match.group(1)
try:
return chr(int(s))
except ValueError:
return s
for line in sys.stdin:
sys.stdout.write(re.sub(r"//([0-9]{4})", tr, line))
''
Uso de ghci-esc
:
$ ./ghci-esc
GHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for help
> "hello"
"hello"
> "привет"
"привет"
> ''Я''
''Я''
> show ''Я''
"''/Я''"
> :q
Leaving GHCi.
Tenga en cuenta que no todo lo anterior se hace correctamente, pero esta es una manera rápida de mostrar la salida de Unicode a su audiencia.