การสร้างโครงสร้างข้อมูลด้วยวิธีการโหลดและบันทึก


ข้อกำหนดเบื้องต้น


ข้อกำหนดสำหรับบทความนี้คือ คุณต้องติดตั้ง Go บนคอมพิวเตอร์ของคุณและเคยเรียนรู้บทความ GOPATH และ Go Workspace มาก่อน

สร้าง Go Workspace พื้นที่ทำงาน ภาษา Go


สร้างโฟลเดอร์ใหม่ชื่อ gowiki ภายใน GOPATH/src ของคุณ


สร้างไฟล์ชื่อ wiki.go และเพิ่มบรรทัดต่อไปนี้:

package main

import (
	"fmt"
	"os"
)


เรานำเข้า fmt และ os แพ็คเกจจากไลบรารีมาตรฐาน Go และถ้าเราต้องการใช้งาน แพ็คเกจเพิ่มเติม เราจะเพิ่มแพ็คเกจเพิ่มเติมใน import การประกาศนี้ภายหลัง


โครงสร้างข้อมูล


เริ่มต้นด้วยการกำหนดโครงสร้างข้อมูลวิกิประกอบด้วยชุดของหน้าที่เชื่อมโยงถึงกัน ซึ่งแต่ละหน้ามีชื่อเรื่องและเนื้อหา (เนื้อหาของหน้า) ในที่นี้ เรากำหนด Page เป็นโครงสร้างที่มีสองฟิลด์แทนชื่อและเนื้อหา

type Page struct {
    Title string
    Body  []byte
}


ประเภท []byte หมายถึง “a byte slice” (ดูที่ Slices: การใช้และ internals สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Slice ) Body องค์ประกอบเป็น a []byte แทนที่จะเป็น string เพราะเป็นประเภทที่ไลบรารี io ที่เราจะใช้คาดหวังดังที่คุณจะเห็นด้านล่าง

การใช้งาน Slice

การใช้งาน Array แบบปกติ เราจะต้องกําหนดขนาดไว้ก่อนล่วงหน้า ซึ่งไม่ค่อยยืดหยุ่น Slice จึงเข้ามาแก้ปัญหานี้ โดยที่ Slice จะสามารถประกาศตัวแปรได้เหมือนกับ Array แต่ไม่ต้องกําหนดขนาดล่วงหน้า การประกาศ slice เราไม่ต้องระบุจำนวนช่อง เพราะเราสามารถเพิ่ม element เข้าไปได้อย่างอิสระภายหลัง

โครงสร้าง Page อธิบายวิธีการจัดเก็บข้อมูลหน้าในหน่วยความจำ แต่สิ่งที่เกี่ยวกับการจัดเก็บแบบถาวร? เราสามารถจัดการกับสิ่งนั้นได้โดยวิธีการสร้าง และ save ลงบน Page:

func (p *Page) save() error {
    filename := p.Title + ".txt"
    return os.WriteFile(filename, p.Body, 0600)
}

ลายเซ็นของเมธอดนี้อ่านว่า: “นี่คือเมธอดที่มีชื่อ save ที่ใช้ p เป็นตัวรับ (receiver) มีตัวชี้ (pointer) ไปยัง Page ไม่ใช้พารามิเตอร์ และส่งกลับค่าของชนิดข้อมูลคือ error”

วิธีนี้จะบันทึก Page’s Body ลงในไฟล์ข้อความ เพื่อความง่าย เราจะใช้ Title เป็นชื่อไฟล์

save และวิธีการส่งกลับค่า error เพราะเห็นว่าเป็นชนิดที่การกลับมาของ WriteFile (ฟังก์ชั่น library มาตรฐานที่เขียน byte slice ไปยังแฟ้ม) save วิธีการส่งกลับค่าความผิดพลาด เพื่อให้การจัดการแอพลิเคชันที่ควรจะเป็นอะไรไปอย่างผิดปกติในขณะที่เขียนไฟล์ หากทุกอย่างเป็นไปด้วยดี Page.save() จะส่งค่ากลับมาเป็น nil (ค่าศูนย์สำหรับพอยน์เตอร์)

ค่าตามตัวอักษรจำนวนเต็มฐานแปด ที่ 0600 ส่งผ่านเป็นพารามิเตอร์ที่สามไปยัง WriteFile บ่งชี้ว่าไฟล์ควรถูกสร้างขึ้นด้วยสิทธิ์ในการอ่าน-เขียนสำหรับผู้ใช้ปัจจุบันเท่านั้น

นอกจากการบันทึกหน้าแล้ว เรายังต้องการโหลดหน้าอีกด้วย:

func loadPage(title string) *Page {
    filename := title + ".txt"
    body, _ := os.ReadFile(filename)
    return &Page{Title: title, Body: body}
}


ฟังก์ชัน loadPage จะสร้างชื่อไฟล์จากพารามิเตอร์ title อ่านเนื้อหาของไฟล์เป็นตัวแปรใหม่ body และส่งคืนตัวชี้ไปยัง Page ตัวอักษรที่สร้างขึ้นด้วยค่า title และค่าเนื้อหาที่เหมาะสม

ฟังก์ชันสามารถคืนค่าได้หลายค่า ฟังก์ชันไลบรารีมาตรฐาน os.ReadFile ส่งคืน []byte และ error. ใน loadPage ข้อผิดพลาดยังไม่ได้รับการจัดการ “blank identifier” (ตัวระบุว่าง) ที่แสดงโดย สัญลักษณ์ขีดล่าง (_) ใช้เพื่อทิ้งค่าส่งคืนข้อผิดพลาด

แต่จะเกิดอะไรขึ้นหาก ReadFile พบข้อผิดพลาด ตัวอย่างเช่น ไฟล์อาจไม่มีอยู่ เราไม่ควรละเลยข้อผิดพลาดดังกล่าว มาแก้ไขฟังก์ชันเพื่อ return *Page และ error.

func loadPage(title string) (*Page, error) {
    filename := title + ".txt"
    body, err := os.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    return &Page{Title: title, Body: body}, nil
}


ผู้เรียกใช้ฟังก์ชันนี้สามารถตรวจสอบพารามิเตอร์ที่สองได้แล้ว ถ้าเป็น nil แสดงว่าโหลดหน้าสำเร็จแล้ว หากไม่เป็นเช่นนั้น error

ณ จุดนี้ เรามีโครงสร้างข้อมูลที่เรียบง่ายและสามารถบันทึกและโหลดจากไฟล์ได้ มาเขียนฟังก์ชัน main พื่อทดสอบสิ่งที่เราเขียนกัน:

func main() {
    p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
    p1.save()
    p2, _ := loadPage("TestPage")
    fmt.Println(string(p2.Body))
}


หลังจากคอมไพล์และรันโค้ดนี้แล้ว ไฟล์ที่ชื่อว่า TestPage.txt จะถูกสร้างขึ้น โดยมีเนื้อหาเป็น p1. ไฟล์จะถูกอ่านใน struct p2 และ องค์ประกอบ Body จะพิมพ์ไปที่หน้าจอ

โค้ดทั้งหมด

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style


package main

import (
	"fmt"
	"os"
)

type Page struct {
	Title string
	Body  []byte
}

func (p *Page) save() error {
	filename := p.Title + ".txt"
	return os.WriteFile(filename, p.Body, 0600)
}

func loadPage(title string) (*Page, error) {
	filename := title + ".txt"
	body, err := os.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return &Page{Title: title, Body: body}, nil
}

func main() {
	p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
	p1.save()
	p2, _ := loadPage("TestPage")
	fmt.Println(string(p2.Body))
}


คุณสามารถคอมไพล์และรันโปรแกรมได้ดังนี้:


คลิกขวาที่ โฟลเดอร์ gowiki -> Open integrated Terminal


สร้าง Go Modules โดยใช้คำสั่ง (ถ้าไม่สร้างจะไม่สามารถใช้คำสั่ง go build ได้) จะได้ ไฟล์ go.mod เพิ่มเข้ามา

go mod init


คอมไพล์ไฟล์ซอร์สโค้ด จะได้ ไฟล์ wiki.exe เพิ่มเข้ามา

go build wiki.go


เรียกใช้งาน wiki.exe

./wiki



ผลลัพธ์ คือสร้างไฟล์ TestPage.txt และ แสดงช้อความ “This is a sample Page.”


แนะนำ แพ็คเกจ net/http


ต่อไปนี้คือตัวอย่างการทำงานเต็มรูปแบบของเว็บเซิร์ฟเวอร์อย่างง่าย:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}


ฟังก์ชั่น main เริ่มต้นด้วยการเรียกร้องให้ http.HandleFunc ซึ่งบอกแพคเกจ http ที่จะจัดการกับคำขอทั้งหมดไปยังรากเว็บ ( “/”) handle rด้วย

จากนั้นเรียกใช้ http.ListenAndServe โดยระบุว่าควรฟังบนพอร์ต 8080 บนอินเทอร์เฟซใด ๆ ( “:8080”) (อย่ากังวลกับพารามิเตอร์ตัวที่สอง nil, ในตอนนี้) ฟังก์ชันนี้จะบล็อกจนกว่าโปรแกรมจะถูกยกเลิก

ListenAndServe ส่งคืนข้อผิดพลาดเสมอ เนื่องจากจะส่งคืนเมื่อเกิดข้อผิดพลาดที่ไม่คาดคิดเท่านั้น เพื่อบันทึกข้อผิดพลาดนั้น เราห่อการเรียกฟังก์ชันด้วย log.Fatal.

ฟังก์ชั่นเป็นชนิด handler http.HandlerFunc ใช้ an http.ResponseWrite rและ an http.Request เป็นอาร์กิวเมนต์

ค่า http.ResponseWriter ประกอบการตอบกลับของเซิร์ฟเวอร์ HTTP; โดยการเขียนลงไป เราจะส่งข้อมูลไปยังไคลเอนต์ HTTP

http.Request เป็นโครงสร้างข้อมูลที่แสดงถึงคำขอ HTTP ของไคลเอ็นต์ r.URL.Path เป็นองค์ประกอบพาธของ URL ที่ร้องขอ ส่วนท้าย [1:] หมายถึง “สร้างส่วนย่อยของเส้นทางจากอักขระที่ 1 ไปจนสุด” สิ่งนี้จะลบ “/” นำหน้าออกจากชื่อพาธ

หากคุณเรียกใช้โปรแกรมนี้ go build wiki.go ตามด้วย ./wiki

เปิดเว็บบราวเซอร์ ป้อน URL http://localhost:8080/monkeys คุณจะเห็นว่า Hi there, I love monkeys! อยู่บนหน้าจอของคุณ

credit : https://go.dev/doc/articles/wiki/

Leave a Reply

Your email address will not be published. Required fields are marked *