How to properly print Haskell record containing UTF-8 Text? - haskell

I have a following Haskell record:
import Data.Text (Text)
data Person = Person
{ name :: Text
, age :: Int
}
deriving (Show)
secondPerson :: Person
secondPerson =
Person
{ name = "हर्षल पाटील"
, age = 100
}
For my name, I am using the non-english (of course, UTF-8) characters. But when I print using:
main :: IO ()
main =
putStrLn "Second Person"
*> print secondPerson
I get some encoded output:
Second Person
Person {name = "\2361\2352\2381\2359\2354 \2346\2366\2335\2368\2354", age = 100}
What is the way properly print this in Haskell? I may have multiple records with any number of text fields and need a generic way to achieve this.

You could try something like
putStrLn ("Person " ++ name secondPerson ++ " " ++ show age)
instead of print. Depending on your OS and terminal, this might give you the output you seek.
The issue here is that print call show, and show prints strings by escaping most characters which are not ASCII alphanumeric ones. Basically, show is designed so that it always outputs an ASCII string in an unambiguous form, including escaping quotes and other non-simple characters.
If you want to print more characters, you need therefore to avoid show/print and format your output manually, then call the lower level putStrLn (or putStr) which outputs a string as it is.

Related

Different outputs in GHCI

Running this with M 1 will show a new line and a will show the characters "\n" in the ghci output.
Since they are the same characters i would expect the same behaviour.
Any reason why?
data V = M Int
instance Show V where
show m = a
a :: [Char]
a = "\n"
Whenever a value is displayed in GHCi, it uses the Show instance of that object to convert it to human-readable text by applying the show function, then printing the resulting String. In your case, what is happening is:
You have defined the Show instance of V to be a constant "\n", or newline. Thus when GHCi tries to display a value of type V it outputs a newline.
By contrast, the Show instance of String is defined in such a way that for any string s, show s will output the Haskell representation of s. For instance, show "\n" will give a result of "\"\\n\" (i.e. quotation mark, backslash, n, quotation mark). (Try running show "myString" in GHCi and seeing the result for different strings to get an idea of how this works.) Thus when GHCi displays a String, it takes the string (a newline, in your case), converts it to a human-readable format with escape characters using show, and then displays that new string.
Why is show implemented in this convoluted way for strings? I’m not quite sure, but one possibility is disambiguation: show 1 is "1", but show "1" is "\"1\"". When printed to the console, the former is clearly a number, whereas the latter is clearly a string.

Haskell Parsec EOF

I have a file containing many of the following data format:
Dan Clark’s Profile Photo
Member Name
Dan Clark 2nd degree connection 2nd
Member Occupation
Founder and Headmaster at Some Company, LLC
Nina blalba’s Profile Photo
Member Name
Nina blabla 2nd degree connection 2nd
Member Occupation
Consultant - GAmes executive search
My parser to parse the above file:
module Main where
import Control.Applicative
import Control.Monad
import Text.ParserCombinators.Parsec hiding (many, (<|>))
data Contact = Contact {
name :: String,
occupation :: String,
company :: String
} deriving Show
matchContact :: Parser Contact
matchContact = do
name <- many anyChar
char '\''
string "s Profile Photo"
char '\n'
string "Member Name"
char '\n'
string name
many anyChar
char '\n'
string "Member Occupation"
char '\n'
job <- many anyChar
try $ string " at "
company <- many anyChar
try (char '\n')
return $ Contact name job company
main = do
c <- parseFromFile (many matchContact <* eof) "contacts.txt"
print c
There are many issues such as the data are not regular. But the most urgent one is that I always run into the error at the last line of the input file:
Left "contacts.txt" (line 8670, column 12):
unexpected end of input
expecting "'"
How to fix this?
The first instance you attempt many anyChar, the parser will happily parse all the rest of the file into the string name, since everything that follows clearly fulfills the criterion any character (including the newline characters). That's clearly not what you want.
Use manyTill, or restrict the choice of permitted characters so the name will end at the appropriate place.

Haskell Exception: Prelude.read: no parse

I am new to Haskell, kinda 2 days since I study Haskell, and I want to do a project with files. I did it already in c++ and now I want to do it in Haskell too.
A little part of project is about a library, where I can register a book and a person. Now I want to put book code into a file.txt but to store it as int, not as String, because I have to search and compare this code , with other codes later, and will be easier to compare.
Here is my code, and I receive the following error *** Exception: Prelude.read: no parse. Does anyone know how to solve this please?
import System.IO
main = do
putStrLn "Please put book details"
putStr "Code: "
code <- getLine
let code1 = read code
appendFile "book.txt" ("Cod:" ++ code1 ++ "\n")
This fails since you're attempting to read a String as a String.
read is used to parse a String as an object. To parse a String as a String though, you need to add explicit quotes around the String being parsed. Failing to do so will result in the error that you got. You can test this by adding "s around the input when your program asks for it. It should work.
Do you expect code1 to be an int? If that's the case, there's 2 problems (and a couple ways to solve them) :
You need to tell read what type you want it to parse the string as. To do that, use a type annotation. Add :: int after read code.
Since you can't concatenate an int, change code1 in your last line to (show code1) to convert it back to a String.
The problem with the above way is you're converting from, then to a String. You could avoid any converting by keeping it as a String by skipping the read altogether:
import System.IO
main = do
putStrLn "Please put book details"
putStr "Code: "
code <- getLine
# Check user's input to ensure it's correct.
appendFile "book.txt" ("Cod:" ++ code)

Manipulating hexadecimal data in Haskell

I have a CSV file full of logged data which I would like to process in Haskell. The data in the CSV file is in hexadecimal format. When I read it into Haskell I have strings such as "0xFF5FFFC8EC5FFEDF" which represents 8 bytes of data.
To process the data, I would like to convert the string into a data type which will allow me to do bit twiddling (bitwise AND, OR and XOR). Then when I am done I would like to convert the final result back into a hex sting so I can write it to a file.
Is this easy to do in Haskell? Which modules should I be looking at?
You can use read to parse ints or floats.
It is in the Prelude so you can use it without any additional modules.
Try:
a = "0xFF5FFFC8EC5FFEDF"
b = read a::Double
(it gives b = 1.8401707840883393e19)
Also, for parsing CSV, you may aswell make your own functions to do it.
I have just a week ago written a simple CSV parser.
module CSVUtils
( parseCSV, showCSV
, readCSV , writeCSV
, colFields
, Separator, Document
, CSV , Entry
, Field
)
where
import Data.Char
import Data.List
{-
A simple utility for working with CSV (comma-separated value) files. These
are simple textual files where fields are delimited with a character (usually a comma
or a semicolon). It is required that the CSV document is well-formed, i.e., that
it contains an equal number of fields per row.
-}
type Separator = String
type Document = String
type CSV = [Entry]
type Entry = [Field]
type Field = String
doc = "John;Doe;15\nTom;Sawyer;12\nAnnie;Blake;20"
brokenDoc = "One;Two\nThree;Four;Five"
{-
(a) Takes a separator and a string representing a CSV document and returns a
CSV representation of the document.
-}
-- !! In the homework text is said Separator is going to be Char and now the type is String
-- !! so I'm just going to take head
parseCSV :: Separator -> Document -> CSV
parseCSV sep doc
| (head sep) `notElem` doc = error $ "The character '"++sep++"' does not occur in the text"
| 1 /= length ( nub ( map length (lines doc))) = error $ "The CSV file is not well-formed"
| otherwise = [splitOn sep wrd | wrd <- lines doc ]
{-
(b) Takes a separator and a CSV representation of
a document and creates a CSV string from it.
-}
showCSV :: Separator -> CSV -> Document
showCSV sep = init . unlines . map (intercalate sep)
{-
(c) Takes a CSV document and a field number
and returns a list of fields in that column.
-}
colFields :: Int -> CSV -> [Field]
colFields n csv = [ if length field > n
then field !! n
else error $ "There is no column "++(show n)++" in the CSV document"
| field <- csv]
{-
(d) Takes a file path and a separator and returns the CSV representation of the file.
-}
readCSV :: Separator -> FilePath -> IO CSV
readCSV sep path = do
file <- readFile path
return $ parseCSV sep file
{-
(e) Takes a separator, a file path, and a CSV document and writes the document into a file.
The return type of writeCSV is a special case of IO { we need to wrap an impure
action, but do not actually have to return anything when writing. Thus, we
introduce (), or the unit type, which holds no information (consider it a 0-
tuple).
-}
writeCSV :: Separator -> FilePath -> CSV -> IO ()
writeCSV sep path csv = writeFile path (showCSV sep csv)
I am going to assume that your binary data could be of arbitrary length. Things can be simplified if, for instance, your binary data fits into an Int64.
I'll recommend the following libraries and modules:
cassava for CSV parsing
bytestring for your string type
base16-bytestring for conversion to/from hexidecimal strings
Data.Bits for bitwise operations on bytes, chars, ints, etc.
For an example of how to perform bitwise operations on a ByteStrings, have a look at the end of this tutorial at the School of Haskell:
https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/bytestring-bits-and-pieces
For examples of how to use cassava, look at the examples directory of the source repo:
https://github.com/tibbe/cassava/tree/master/examples

Haskell - Import text file to list type

I am beginner in haskell. I have a .txt file in the following format:
Company A, 100, 1000
I need to import each row to a list of one type:
type GerCred = [(String, Int, Int)]
How do I do that?
Update
This is what I have tried so far:
type GerCred = [(String,Int,Int)]
type GerCarb = [(String,Int)]
readGerCredList :: File -> IO GerCred
readGerCredList fname = do contents <- readFile fname return(read contents)
Break the problem down into bits.
First, figure out how to read the file into a single big string (hint, look for something that returns "IO String")
Then figure out how to take that string and split it into lines (hint: lines).
Then figure out how to take each line and split it into fields (hint: span, stripPrefix)
Then figure out how to convert each field into the type you need (hint: read).
Then figure out how to put it all together (hint: map)
Don't forget that a String is just a [Char].
Paul.

Resources