勉強部屋‎ > ‎Go 1‎ > ‎

演習a5

日時とタイトルと本文を保存できるメモアプリケーションを作成せよ.

(1)
  • タイトル,日付,内容を格納するデータ型Memoと,Memo型を要素とするスライスMemoSliceを宣言せよ.
(2)
  • Memosをファイルに格納する関数Saveとファイルから読み込む関数LoadMemoを作成せよ.
  • ただし,メモファイルのファイル名は memo.txtとし,ファイルの1行目には"Memo File メモ数"という文字列があるものとする.また,ファイルの2行目から,タイトル行,日時行,内容行が,新しい順に並ぶものとする.
(3)
  • (1)(2)を使ってメモアプリを作成せよ.

解答例(1)

ソースコード
  1. package main
  2. package main
  3. import (
  4.     "text/template"
  5.     "net/http"
  6.     "time"
  7.     "fmt"
  8.     "bufio"
  9.     "strconv"
  10.     "strings"
  11.     "os"
  12.     "log"
  13. )

  14. type Memo struct {
  15.     Title    string
  16.     Date    string
  17.     Body    string
  18. }

  19. type MemoSlice []Memo

  20. const EXPAND = 5

  21. func LoadMemo() (memoArray MemoSlice, num int, err error) {
  22.     f, err := os.Open("memo.txt")
  23.     if err != nil { log.Fatal(err) }
  24.     defer func () {
  25.         err := f.Close()
  26.         if err != nil { log.Fatal(err) }
  27.     }()

  28.     input := bufio.NewReader(f)
  29.     line, _ := input.ReadString('\n')
  30.     if line[0:10] != "Memo File " { log.Fatal("LoadMemo: memo.txt の形式が違います.") }
  31.     num, _ = strconv.Atoi(line[10:len(line)-1])

  32.     memoArray = make([]Memo, num+EXPAND)
  33.     memos := memoArray[EXPAND:]
  34.     for i:=0; i < num; i++ {
  35.         line, err = input.ReadString('\n')
  36.         memos[i].Title = line[:len(line)-1]
  37.         line, err = input.ReadString('\n')
  38.         memos[i].Date = line[:len(line)-1]
  39.         line, err = input.ReadString('\n')
  40.         memos[i].Body = line[:len(line)-1]
  41.     }
  42.     return
  43. }

  44. func (memos MemoSlice) Save() error {
  45.     f, err := os.OpenFile("memo.txt",os.O_WRONLY,0)
  46.     if err != nil { log.Fatal(err) }
  47.     defer func() {
  48.         err = f.Close()
  49.         if err != nil { log.Fatal(err) }
  50.     }()

  51.     output := bufio.NewWriter(f)
  52.     _, err = output.WriteString("Memo File "+strconv.Itoa(len(memos))+"\n")
  53.     for i :=0; i < len(memos); i++ {
  54.         _, err = output.WriteString(memos[i].Title+"\n")
  55.         _, err = output.WriteString(memos[i].Date+"\n")
  56.         _, err = output.WriteString(memos[i].Body+"\n")
  57.     }
  58.     output.Flush()
  59.     return err
  60. }

  61. func main() {
  62.     fmt.Println("メモサーバ スタート")
  63.     memoArray, num, err := LoadMemo()
  64.     if err != nil { log.Fatal(err) }
  65.     memos := memoArray[len(memoArray)-num:]
  66.     fmt.Println("メモファイル読み込み完了")

  67.     http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
  68.         w.Header().Set("Content-Type", "text/html")
  69.         t := template.Must(template.ParseFiles("memo.html"))
  70.         err := t.Execute(w,memos)
  71.         if err != nil {
  72.             fmt.Println(err.Error())
  73.         }
  74.     })

  75.     http.HandleFunc("/memo", func (w http.ResponseWriter, r *http.Request) {
  76.         fmt.Println("メモした")
  77.         title := r.FormValue("title")
  78.         date := time.Now().Format(time.RFC3339)
  79.         body := strings.Replace(r.FormValue("body"),"\n","<br />", -1)
  80.         memos = memoArray[len(memoArray)-len(memos)-1:]
  81.         memos[0] = Memo{title, date, body}
  82.         if len(memoArray) == len(memos) {
  83.             newMemoArray := make([]Memo,len(memoArray)+EXPAND)
  84.             copy(newMemoArray[EXPAND:], memos)
  85.             memoArray = newMemoArray
  86.             err = memos.Save()
  87.             if err != nil { log.Fatal(err) }
  88.             fmt.Println("セーブした")
  89.         }
  90.         http.Redirect(w, r, "/", http.StatusFound)
  91.     })

  92.     http.HandleFunc("/quit", func (w http.ResponseWriter, r *http.Request) {
  93.         err = memos.Save()
  94.         if err != nil { log.Fatal(err) }
  95.         fmt.Println("セーブした")
  96.         fmt.Println("サービス終了")
  97.         os.Exit(0)
  98.     })

  99.     fmt.Println("サービス開始")
  100.     http.ListenAndServe(":10080",nil)
  101. }
HTMLテンプレート
  1. <!DOCTYPE htm>
  2. <head>
  3. <meta charset="utf-8" />
  4. <title>memo</title>
  5. </head>
  6. <body>
  7. <h1>メモ</h1>
  8. <form action="/memo" method="POST">
  9.     <div>タイトル<br><textarea name="title" rows="1" cols="40"></textarea></div>
  10.     <div>内容<br><textarea name="body" rows="3" cols="40"></textarea></div>
  11.     <div><input type="submit" value="メモする"></div>
  12. </form>
  13. {{range .}}
  14.     <hr>
  15.     <p>タイトル:{{.Title}}</p>
  16.     <p>日時:{{.Date}}</p>
  17.     <p>内容:</p>
  18.     {{.Body}}
  19. {{end}}
  20. </body>
  21. </html>

解説

(1)
  • 14行目から18行目:データ型Memoの宣言
  • 20行目:データ型MemoSliceの宣言
(2)
LoadMemo
  • 25行目から30行目:ファイルのオープンとクローズ.
  • 33行目:ファイルから1行読み込み.ReadString('\n')で'\n'まで読み込む.
  • 34行目:ファイルの最初の11バイトが"Memo File "か否かで,メモファイルか否かを判定.
  • 35行目:数を読み込み,strconv.Atoiで整数に変換.これにより,numにメモ数が代入される.
  • 37行目から38行目:メモ数+EXPANDの長さのスライスを作成し,スライスの後方に(EXPAND以降に右詰めで)データを読み込むため,その部分のスライスをmemosとする.
  • 39行目から46行目:タイトル行,日時行,内容行の順に,ファイルからメモ数分だけメモをmemosに読み込む
Save
  • 51行目から56行目:ファイルのオープンとクローズ.
  • 59行目:1行目に"Memo File "とメモ数を出力.strconv.Itoaで整数を文字列に変換.
  • 60行目から64行目:メモ数分だけ,メモを出力.
  • 65行目:出力バッファをフラッシュ.
(3)
main
  • 71行目:メモファイルからメモをmemoArrayに読み込む.この時点で,memoArrayにはEXPAND個(0からEXPAND-1)の空きがある.
  • 73行目:memoArrayのメモが入っている部分(スライス)をmemosとする.
  • 76行目以降で,ハンドルを登録している.ハンドルから,memosにアクセスするために,関数リテラルを用いる.
  • 76行目から83行目:パスが"/"のとき,メモの一覧(memos)を表示する.
  • 85行目から101行目:パスが"/memo"のとき,テンプレートのフォームに入力されたメモデータを,その時の日時とともに,memosに追加する.
  • 92行目から99行目:memoArrayがいっぱいになったら,大きなスライスを用意し,そちらにデータをコピーする.また,メモデータをセーブする.
  • 100行目:"/"にリダイレクトする(メモリストを表示する)
  • 103行目から109行目:"/quit"にアクセスしたら,メモデータをセーブして,サーバを停止する.
  • 112行目:サービス開始.
  • メモファイルが大きくなるとsaveに時間がかかる,Ctl-Cで終わらせると最大5つのメモが消えてしまう,websocketを使ってデータのやり取りを減らした方がいい,などなど,改良の余地は多数あります.
テンプレート
  • 8行目から12行目:メモの入力フォーム.
  • 13行目から19行目:メモのリストの表示.
  • 13行目の{{range .}}により,ソースコードの79行目で指定したmemosというスライスの要素に順にアクセスし,要素数だけ14行目から18行目を繰り返し表示する.
  • スライスの要素はMemo型であるので,そのフィールドには,.Title,.Date,.Bodyでアクセスする.
ċ
a5-template.go
(3k)
Tetsuo Tanaka,
2012/04/30 2:51
ċ
memo.html
(1k)
Tetsuo Tanaka,
2012/04/30 2:51
ċ
memo.txt
(0k)
Tetsuo Tanaka,
2012/04/30 2:52
Comments