HaskellでPLEAC (todo)

1-6章は既にPLEAC-Haskellにあるのでそちらを参照してください。

もとのレシピ(perl)はPLEACを参照のこと

7 章 ファイルアクセス

レシピ7.1 ファイルをオープンする

Opening a File

openFileを使う

import System.Environment

main = do
  (file:_) <- getArgs
  h <- openFile file ReadMode

レシピ7.2 普通と違うファイル名でファイルをオープンする

できるのか?

レシピ7.3 ファイル名に含まれるチルダを展開する

Expanding Tildes in Filenames

getpwnamがみつからなかったので書いた。

import Data.List.Split
import Control.Applicative
import Text.Regex
import System.Environment

getpwnam username = do
  passwds <- map (splitOn ":") <$> lines <$> readFile "/etc/passwd"
  return (filter (\line -> (line!!0)==username) passwds)

main :: IO ()
main = do
  (path:_) <- getArgs
  case matchRegexAll (mkRegex "^~([^/]*)") path of
    Nothing -> putStrLn path
    Just (_,uhome,subdir,cs) ->
        if length cs == 1 && length (cs!!0) /= 0
        then do
          dir <- getpwnam (cs!!0)
          if (length dir) == 1
          then putStrLn $ ((dir!!0)!!5) ++ subdir
          else putStrLn $ "Error: " ++ uhome ++ " not found"
        else do
          homedir <- lookup "HOME" <$> getEnvironment
          case homedir of
            Just home -> putStrLn $ home ++ subdir
            Nothing -> putStrLn "Error: Env[HOME] not found"

レシピ7.4 エラーをファイル名で報告する

Making Perl Report Filenames in Errors

エラーの内容を出力してないのでこれは駄目だな

import System.Environment
import Control.Exception
import Prelude hiding (catch)
import System.Exit

main = do
  (file:_) <- getArgs
  catch (putStr =<< readFile file) $
            \e -> return (e::SomeException) >> print ("Error on reading file: " ++ file)

レシピ7.5 一時ファイルを作成する

Creating Temporary Files

openTempFile を使う

import System.Environment
import System.IO

main :: IO ()
main = do
  (dir:prefix:_) <- getArgs
  (filePath,h) <- openTempFile dir prefix
  putStrLn filePath

レシピ7.6 プログラムの内部にファイルを格納する

Storing Files Inside Your Program Text

これは出来ないんじゃないのかな

レシピ7.7 フィルタを書く

レシピ7.8 ファイルを一時ファイルを使ってその場で更新する

レシピ7.9 ファイルを-iスイッチを使ってその場で更新する

レシピ7.10 一時ファイルを使わずにファイルをその場で更新する

レシピ7.11 ファイルをロックする

レシピ7.12 出力をフラッシュする

レシピ7.13 プログラムの実行をブロックせずに多くのファイルハンドルから読み込む

レシピ7.14 非ブロック型I/Oを行なう

レシピ7.15 読み取るバイト数を決定する

レシピ7.16 ファイルハンドルを変数に格納する

レシピ7.17 オープンされた出力ファイルハンドルをキャッシュする

レシピ7.18 多くのファイルハンドルに同時に書き出す

レシピ7.19 ファイルディスクリプタを数値でオープンまたはクローズする

レシピ7.20 ファイルハンドルをコピーする

プログラム7.1 netlock

プログラム7.2 lockarea

8 章 ファイルコンテンツ

レシピ8.1 継続文字のある行を読む

Reading Lines with Continuation Characters

もとのレシピは行ごとに読んで末尾にバックスラッシュついてるかチェックし てるのだけど、書いたコードは\\nを探して無視するようになっている。

import System.Environment

ccappend :: String -> String
ccappend [] = []
ccappend (c:[]) = [c]
ccappend (c1:c2:cs)
    | c1 == '\\' && c2 == '\n' = ccappend (cs)
    | otherwise         = c1:ccappend (c2:cs)

main = do
  args <- getArgs
  content <- readFile (args!!0)
  putStr $ ccappend content

レシピ8.2 ファイルの行数(段落数、レコード数)をカウントする

Counting Lines (or Paragraphs or Records) in a File

applicative

countlines = (length . lines) <$> readFile

Monad

countlines = liftM (length . lines) $ readFile

レシピ8.3 ファイル内のすべての単語を処理する

Processing Every Word in a File

words関数で単語のリストに分解してmapすればいい。

import System.Environment

main = do
  args <- getArgs
  content <- readFile (args!!0)
  print $ words content

レシピ8.4 ファイルを行単位または段落単位で逆向きに読む

Reading a File Backwards by Line or Paragraph

reverseする

import System.Environment

main = do
  args <- getArgs
  content <- readFile (args!!0)
  print $ reverse . lines $ content

レシピ8.5 成長するファイルを追いかけながら読む

Trailing a Growing File

ファイルハンドルがEOFだったらthreadDelayで一秒待ってループ。そうじゃな かったら文字を読み込んで出力

import System.IO
import System.Environment
import Control.Concurrent

main = do
  args <- getArgs
  h <- openFile (args!!0) ReadMode
  loop h
  where loop h = do
          end <- hIsEOF h
          if end then (threadDelay 1000000) >> loop h
          else do
            c <- hGetChar h
            putChar c
            hFlush stdout
            loop h

レシピ8.6 ファイルから行をランダムに取り出す

Picking a Random Line from a File

一行をランダムに取り出す。アルゴリズムはよく知られたやつ。

import System.Environment
import System.Random
import Control.Applicative

randomNumGen :: Int -> IO Int
randomNumGen n = getStdRandom (randomR (0, n))

choiceLine :: String -> [(Int, String)] -> IO String
choiceLine s [] = return s
choiceLine s ((n, line):cs) = do
  n' <- randomNumGen n
  if n' < 1 then choiceLine line cs
  else choiceLine s cs

main :: IO ()
main = do
  args <- getArgs
  choiced <- choiceLine "" =<< zip [1..] <$> lines <$> readFile (args!!0)
  putStrLn choiced

レシピ8.7 ファイル内のすべての行をシャッフルしたい

Randomizing All Lines

import System.Environment
import System.Random.Shuffle
import Control.Applicative

main :: IO ()
main = do
  args <- getArgs
  shuffled <- shuffleM =<< lines <$>  readFile (args!!0)
  mapM_ putStrLn shuffled

レシピ8.8 ファイル内の特定の行を読み込む

Reading a Particular Line in a File

import System.Environment
import Control.Applicative

main :: IO ()
main = do
  (file:lineNum:_) <- getArgs
  line <- flip (!!) (pred (read lineNum :: Int)) . lines <$>  readFile file
  putStrLn line

レシピ8.9 可変長テキストフィールドを処理する

Processing Variable-Length Text Fields

Text.RegexのsplitRegex を使えばいい

import Text.Regex

splitRegex (mkRegex "\tb") "a\tba\tba\tbab" -- ["a","a","a","ab"]

レシピ8.10 ファイルの最後の行を削除する

Removing the Last Line of a File

import System.Environment
import Control.Applicative

main :: IO ()
main = do
  (file:_) <- getArgs
  lastElimLines <- (init . lines) <$>  readFile file
  mapM_ putStrLn lastElimLines

レシピ8.11 バイナリファイルを処理する

Processing Binary Files

System.IOにhSetBinaryModeがある

レシピ8.12 ランダムアクセスI/Oを使用する

Using Random-Access I/O

hSeekをつかう

import System.IO
import System.Environment

main = do
  (file:num:_) <- getArgs
  h <- openFile file ReadMode
  hSeek h AbsoluteSeek (read num :: Integer)
  c <- hGetChar h
  putChar c

レシピ8.13 ランダムアクセスファイルを更新する

レシピ8.14 バイナリファイルから文字列を読み取る

レシピ8.15 固定長レコードを読む

レシピ8.16 configファイルを読む

レシピ8.17 ファイルの信頼性をテストする

プログラム8.1 tailwtmp

プログラム8.2 tctee

プログラム8.3 laston

9 章 ディレクトリ

レシピ9.1 タイムスタンプを取得/設定する

Getting and Setting Timestamps

時間の変換がちょっと面倒だ。

import System.Posix.Files
import System.Time
import System.Posix.Types
import System.Environment

getTimes :: FilePath -> IO (ClockTime, ClockTime)
getTimes fp =
    do stat <- getFileStatus fp
       return (toct (accessTime stat),
               toct (modificationTime stat))

toct :: EpochTime -> ClockTime
toct et = TOD (truncate (toRational et)) 0

main :: IO ()
main = do
  (file:_) <- getArgs
  (atime, mtime) <- getTimes file
  toCalendarTime atime >>= print
  toCalendarTime mtime >>= print

参考

レシピ9.2 ファイルを削除する

Deleting a File

System.DiirectoryのremoveFileを使う

removeFile "test.pl"

レシピ9.3 ファイルをコピーまたは移動する

Copying or Moving a File

System.DiirectoryのrenameFileかcopyFileを使う

レシピ9.4 同じファイルに2つの異なる名前をつける

レシピ9.5 ディレクトリ内のすべてのファイルを処理する

Processing All Files in a Directory

System.DiirectorygetDirectoryContentsをつかう

レシピ9.6 パターンにマッチするファイル名のリストを取得する(グロブする)

Globbing, or Getting a List of Filenames Matching a Pattern

Glob packageを使えばいい

レシピ9.7 ディレクトリ内のすべてのファイルを再帰的に処理する

Processing All Files in a Directory Recursively

ライブラリにはみつからないがRWHに書いてある

レシピ9.8 ディレクトリとその中身を削除する

Removing a Directory and Its Contents

System.DiirectoryのremoveDirectoryRecursiveを使う

レシピ9.9 ファイルをリネームする

レシピ9.10 ファイル名をその構成要素に分割する

Splitting a Filename into Its Component Parts

System.FilePath.Posix

プログラム9.1 symirror

プログラム9.2 lst

10章 サブルーチン

レシピ10.1 サブルーチンの引数にアクセスする

レシピ10.2 変数を関数内に局所化する

レシピ10.3 永続的な局所変数を作成する

レシピ10.4 実行中の関数の名前を知る

レシピ10.5 配列やハッシュをリファレンスで渡す

レシピ10.6 戻り値のコンテキストを調べる

レシピ10.7 名前付き引数を渡す

レシピ10.8 いくつかの戻り値を捨てる

レシピ10.9 2つ以上の配列またはハッシュを返す

レシピ10.10 エラーを返す

レシピ10.11 関数の型宣言(プロトタイプ)

レシピ10.12 例外処理

レシピ10.13 グローバル変数を退避する

レシピ10.14 関数を再定義する

レシピ10.15 AUTOLOADを使って未定義関数の呼び出しをトラップする

レシピ10.16 サブルーチンをネスト(入れ子に)する

プログラム10.1 メールをソートする

11章 リファレンスとレコード

省略

12章 パッケージ、ライブラリ、モジュール

省略

13章 クラス、オブジェクト、tie

省略

14章 データベースアクセス

レシピ14.1 DBMファイルを作成/使用する

レシピ14.2 DBMファイルを空にする

レシピ14.3 異なるDBMファイルに変換する

レシピ14.4 DBMファイルをマージする

レシピ14.5 DBMファイルをロックする

レシピ14.6 大規模なDBMファイルをソートする

レシピ14.7 テキストファイルをデータベース配列として扱う

レシピ14.8 DBMファイルに複雑なデータを格納する

レシピ14.9 永続的なデータ

レシピ14.10 DBIおよびDBDを使用してSQLコマンドを実行する

15章 ユーザインタフェース

レシピ15.1 プログラムの引数を解析する

レシピ15.2 プログラムがインタラクティブに動作しているかどうかを判定する

レシピ15.3 画面を消去する

レシピ15.4 端末またはウィンドウのサイズを調べる

レシピ15.5 テキストの色を変える

レシピ15.6 キーボードから読み取る

レシピ15.7 端末でビープ音を鳴らす

レシピ15.8 POSIX termiosを使用する

レシピ15.9 入力待ちをチェックする

レシピ15.10 パスワードを読む

レシピ15.11 入力を編集する

レシピ15.12 画面を制御する

レシピ15.13 Expectモジュールを使って別のプログラムを制御する

レシピ15.14 Tkを使ってメニューを作成する

レシピ15.15 Tkでダイアログボックスを作成する

レシピ15.16 Tkのウィンドウサイズ変更イベントに応答する

レシピ15.17 Windows Perl/Tkを使ってDOS窓が表示されないようにする

プログラム15.1 小さなtermcapプログラム

プログラム15.2 tkshufflepod

16章 プロセス管理とプロセス間通信

レシピ16.1 プログラムの出力を収集する

レシピ16.2 別のプログラムを起動する

レシピ16.3 現在のプログラムを別のプログラムで置換する

レシピ16.4 別のプログラムと読み書きする

レシピ16.5 自分の出力をフィルタリングする

レシピ16.6 入力を前処理する

レシピ16.7 プログラムのSTDERRを読み込む

レシピ16.8 相手プログラムの入力と出力を制御する

レシピ16.9 相手プログラムの入力、出力、エラー出力を制御する

レシピ16.10 関連プロセス間で通信する

レシピ16.11 名前付きパイプを使ってプロセスをファイルのように見せる

レシピ16.12 異なるプロセス間で変数を共有する

レシピ16.13 使用可能なシグナルを一覧表示する

レシピ16.14 シグナルを送信する

レシピ16.15 シグナルハンドラを設定する

レシピ16.16 一時的にシグナルハンドラを上書きする

レシピ16.17 シグナルハンドラを書く

レシピ16.18 Ctrl-Cを捕捉する

レシピ16.19 ゾンビプロセスの蓄積を避ける

レシピ16.20 シグナルをブロックする

レシピ16.21 操作をタイムアウトにする

プログラム16.1 sigrand

17章 ソケット

レシピ17.1 TCPクライアントを書く

レシピ17.2 TCPサーバを書く

レシピ17.3 TCPを介して通信する

レシピ17.4 UDPクライアントをセットアップする

レシピ17.5 UDPサーバをセットアップする

レシピ17.6 UNIXドメインソケットを使う

レシピ17.7 ソケットの他方の端を特定する

レシピ17.8 自分自身の名前とアドレスを知る

レシピ17.9 forkした後、ソケットをクローズする

レシピ17.10 双方向クライアントを書く

レシピ17.11 サーバをforkする

レシピ17.12 サーバをあらかじめforkしておく

レシピ17.13 forkしないサーバ

レシピ17.14 マルチホームドサーバを書く

レシピ17.15 デーモンを作成する

レシピ17.16 要求に応じてサーバを再起動する

プログラム17.1 backsniff

プログラム17.2 fwdport

18章 インターネットサービス

レシピ18.1 単純なDNSルックアップ

Simple DNS Lookups

標準ライブラリがあるのでそれを使えばいい。

Prelude> :m Network.BSD Network.Socket
Prelude Network.Socket Network.BSD> getHostByName "google.co.jp" >>= print . hostAddresses
[3102440778,3085663562,3219881290]
Prelude Network.Socket Network.BSD Control.Monad> mapM inet_ntoa =<< liftM hostAddresses (getHostByName "google.co.jp")
["173.194.38.95","173.194.38.88","173.194.38.87"]
Prelude Network.Socket Network.BSD> inet_addr "74.125.235.184" >>= getHostByAddr AF_INET
HostEntry {hostName = "nrt19s12-in-f24.1e100.net",
           hostAliases = ["184.235.125.74.in-addr.arpa"],
           hostFamily = AF_INET, hostAddresses = [3102440778]
          }

レシピ18.2 FTPクライアントになる

Being an FTP Client

Network.FTP.Clientを使う

Prelude> :m  Network.FTP.Client
Prelude Network.FTP.Client>  loginAnon h
(230,["Login successful."])
Prelude Network.FTP.Client> cwd h "/pub/linux/kernel/Historic"
(250,["Directory successfully changed."])
Prelude Network.FTP.Client> nlst h Nothing >>= putStrLn . unlines
linux-0.01.tar.gz
linux-0.01.tar.gz.sign
linux-0.01.tar.sign
old-versions
v0.99

Prelude Network.FTP.Client> getbinary h "linux-0.01.tar.gz.sign" >>= putStrLn . fst
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.0 (GNU/Linux)
Comment: See http://www.kernel.org/signature.html for info

iD8DBQA54rf0yGugalF9Dw4RAqelAJ9lafFni4f/QyJ2IqDXzW2nz/ZIogCfRPtg
uYpWffOhkyByfhUt8Lcelec=
=KnLA
-----END PGP SIGNATURE-----

レシピ18.3 メールを送信する

レシピ18.4 Usenetニュースメッセージの購読と投稿

レシピ18.5 POP3を使ってメールを読む

レシピ18.6 プログラムからtelnetをシミュレートする

レシピ18.7 マシンをpingする

レシピ18.8 Whoisを使用してInterNICから情報を検索する

プログラム18.1 expnとvrfy

19章 CGIプログラミング

レシピ19.1 CGIスクリプトを書く

レシピ19.2 エラーメッセージをリダイレクトする

レシピ19.3 500 Server Errorに対処する

レシピ19.4 安全なCGIプログラムを書く

レシピ19.5 CGIスクリプトの効率アップを図る

レシピ19.6 シェルエスケープなしでコマンドを実行する

レシピ19.7 HTMLショートカット関数を使ってHTML形式のリストや表を簡単に作成する

レシピ19.8 別のロケーションにリダイレクトする

レシピ19.9 HTTPによるやり取りを直接デバッグする

レシピ19.10 クッキーを管理する

レシピ19.11 Stickyウィジェットを作成する

レシピ19.12 マルチスクリーンCGIスクリプトを書く

レシピ19.13 フォームをファイルまたはメールパイプに保存する

プログラム19.1 chemiserie

20章 Webオートメーション

レシピ20.1 特定のURLからドキュメント抽出する

レシピ20.2 フォームの自動送信

レシピ20.3 URLを取り出す

レシピ20.4 ASCIIテキストをHTMLに変換する

レシピ20.5 HTMLからASCIIテキストに変換する

レシピ20.6 HTMLタグを取り出す/削除する

レシピ20.7 切れたリンクを見つける

レシピ20.8 最新のリンクを見つける

レシピ20.9 HTMLテンプレートを作成する

レシピ20.10 Webページをミラーリングする

レシピ20.11 ロボットを作成する

レシピ20.12 Webサーバのログファイルを解析する

レシピ20.13 サーバログを処理する

プログラム20.1 htmlsub

プログラム20.2 hrefsub

Table Of Contents

Previous topic

Haskellで人工無脳をつくろう

Next topic

Haskellでスタックを利用した加減乗除の計算機を作ってみる