固定長データ
Posted feedbacks - Haskell
敢えて冗長な書き方でやってみました。
メンテナンス性を考慮したつもりなんですが、見やすいかどうかは微妙です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | --
-- 固定長データを参照する
--
module Main where
import System
--
-- base length
--
firstNameLen = 12
lastNameLen = 12
sexLen = 1
ageLen = 3
yearLen = 4
monthLen = 2
dateLen = 2
breakFastLen = 500
lunchLen = 500
dinnerLen = 500
--
-- base position
--
firstNamePos = 0
lastNamePos = firstNamePos + firstNameLen
sexPos = lastNamePos + lastNameLen
agePos = sexPos + sexLen
yearPos = agePos + ageLen
monthPos = yearPos + yearLen
datePos = 0
breakFastPos = datePos + dateLen
lunchPos = breakFastPos + breakFastLen
dinnerPos = lunchPos + lunchLen
--
-- length
--
userHeaderLen = firstNameLen + lastNameLen + sexLen + ageLen + yearLen + monthLen
userDataLen = dateLen + breakFastLen + lunchLen + dinnerLen
userLen = userHeaderLen + userDataLen * 31
--
-- position
--
userPos n = userLen * n
userDataPos n = userHeaderLen + userDataLen * n
--
-- access utility
--
cut d (pos, len) = take len $ drop pos d
getUser d n = cut d (userPos n, userLen)
getUserData user n = cut user (userDataPos n, userDataLen)
getFirstName user = cut user (firstNamePos, firstNameLen)
getLastName user = cut user (lastNamePos, lastNameLen)
getSex user = cut user (sexPos, sexLen)
getAge user = cut user (agePos, ageLen)
getYear user = cut user (yearPos, yearLen)
getMonth user = cut user (monthPos, monthLen)
getDate user n = cut (getUserData user n) (datePos, dateLen)
getBreakFast user n = cut (getUserData user n) (breakFastPos, breakFastLen)
getLunch user n = cut (getUserData user n) (lunchPos, lunchLen)
getDinner user n = cut (getUserData user n) (dinnerPos, dinnerLen)
--
-- example
--
main = do
args <- getArgs
contents <- if (not.null) args
then readFile $ head args
else getContents
let user = getUser contents 0
putStrLn $ show $ getLastName user
putStrLn $ show $ getFirstName user
-- where
-- user n = "Jyunichiro " ++ "Koizumi " ++ "F" ++ "66" ++
-- "2008" ++ "03" ++ days
-- days = show $ map (\x -> (dayNo x) ++ foods) [1..31]
-- dayNo n = reverse $ take 2 $ reverse $ "0" ++ (show n)
-- foods = concat $ replicate 3 (replicate 500 ' ')
|
こんなのはどうでしょう?Parsecとか、Genericsを使って、もっとかっこよくレコード定義を書くとそれがそのまま読み込み関数にならないかと思ったのですが、結局割りと基本的なHaskellのコードになってしまったのですが、ほかの言語のバージョンと張り合うために簡潔さを追求してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | module Main where
rdStr len = sequence $ replicate len getChar
chomp = reverse.((dropWhile (== ' ')).reverse)
rdMenu = do
[day, b, l, d] <- mapM (rdStr) [2, 500, 500, 500]
return $ ((read day)::Int, chomp b, chomp l, chomp d)
rdRecord = do
[lnm, fnm, [gen], ag, yr, mo] <- mapM (rdStr) [12, 12, 1, 3, 4, 2]
ms <- sequence $ replicate 31 (rdMenu)
return $ (chomp lnm, chomp fnm, gen, (read ag)::Int, (read yr)::Int, (read mo)::Int, ms)
main = (sequence $ replicate 500 (rdRecord)) >>= print
|
あと、テストデータ生成コード...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | module Main where
import Text.Printf
putDayMenu :: Int -> IO()
putDayMenu d = do
printf "%2d" d
putMenu
putMenu
putMenu
where
menu = "Menu entry"
putMenu = putStr $ menu ++ (take (500 - length menu) $ repeat ' ')
putRecord = do
putStr $ take 12 $ drop 1 $ cycle ['0'..'9']
putStr $ take 12 $ drop 1 $ cycle ['0'..'9']
putStr "M"
putStr " 40"
putStr "2008"
putStr "03"
mapM_ (putDayMenu) [1..31]
main = sequence_ $ replicate 500 (putRecord)
|
Data.Genericsの勉強もかねて、Generics版を作ってみました。最初のイメージどおり、データの定義をfieldとして一箇所にまとめることができました...結果的に、大きくなっちゃいましたけど...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | module Main where
import Data.Generics
rdStr len = sequence $ replicate len getChar
chomp = reverse.((dropWhile (== ' ')).reverse)
data Field = Integer Int | Str String | Ch Char
deriving (Data, Typeable, Show)
field = ((12::Int, Str ""), -- last name
(12::Int, Str ""), -- first name
(1::Int, Ch ' '), -- gender
(3::Int, Integer 0), -- age
(4::Int, Integer 0), -- year
(2::Int, Integer 0), -- month
replicate 31 ((2::Int, Integer 0), --day
(500::Int, Str ""), --breakfast
(500::Int, Str ""), --lulnch
(500::Int, Str ""))) -- dinner
readDB = everywhereM (mkM strReader) $ replicate 500 field
where
strReader :: (Int, Field) -> IO (Int, Field)
strReader (cch, Str _) = do
str <- rdStr cch
return (cch, Str $ chomp str)
strReader (cch, Integer _) = do
str <- rdStr cch
return (cch, Integer $ read str)
strReader (cch, Ch _) = do
(ch: _) <- rdStr cch
return (cch, Ch ch)
main = readDB >>= print
|



Mymelo #6060() Rating5/7=0.71
固定長のデータが記載されたファイルを読み込むプログラムを作成してください。読み込んだデータは、複数の値を格納できるデータ型に格納してください。
ファイルには、すべて ascii 文字で以下のデータが格納されています。デリミタはなく、固定長で格納されています。レコードとレコードのあいだも改行はありません。
以上の形式のデータ500人分を読みこんで、データを複数の値を格納できるデータ型に格納してください。データに大して何か処理を行う必要はなく、すぐに破棄してかまいません。
この問題は、このようなファイルをどのように扱うかを知りたくて作成しました。
[ reply ]