Pada praktik kali ini kita akan membuat program sederhana untuk menulis dan membaca data dari sebuah file. Untuk memudahkan menggunakan data dari file yang di simpan, kita menggunakan struktur struct dan format data yang disimpan berupa data json.

Kerangka Program

Kerangka program yang akan kita gunakan terdiri dari 4 fungsi sperti ditampilkan pada kerangka program berikut:

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
)

// Struktur Data Penduduk
type Penduduk struct {
	ID           int
	Nama, Alamat string
	Umur         int
}

// array Daftar data penduduk
type DataPenduduk []Penduduk

//nama file untuk menyimpan data penduduk
const namaFile string = "penduduk.json"

// Fungsi utama program
func main() {

}

// Untuk memuat data penduduk dari file
func (d *DataPenduduk) MuatData() error {
	return nil
}

//Menambahkan data penduduk kedalam file
func (d DataPenduduk) Tambah(p Penduduk) error {
	return nil
}

// menampilkan daftar data penduduk
func (d DataPenduduk) Daftar() {
}

Pada program yang akan kita buat, kita akan menggunakan struktur dengan nama (type) Penduduk dan data yang disimpan ke dalam file bernama (type) DataPenduduk.

Karena data yang akan kita simpan berupa json file maka kita memerlukan paket io/ioutil untuk membaca dan menyimpan file dengan namaFile yang telah di deklarasikan daam bentuk variabel, serta encoding/json untuk memformat type Penduduk kedalam format json dan sebaliknya.

Tambahan paket yang dibutuhkan adalah paket os untuk menerima input dari pengguna.

Fungsi Muat Data

Fungsi MuatData memuat data dari file, fungsi ini akan kita panggil pada fungsi main() setiap kali program dijalankan sehingga kita bisa memodifikasi data dalam file atau membacanya.

Data yang dimuat dari file akan di format kedalam type DataPenduduk, seperti yang kita lihat argumen sebelah kiri fungsi ini type DataPenduduk berupa pointer.

Ini akan memudahkan untuk mengubah nilai varibel type DataPenduduk dimanapun variabel tersebut dideklarasikan tanpa harus mengembalikan nilai dan menambahkan nilai dengan operator assigment (=).

// Untuk memuat data penduduk dari file
func (d *DataPenduduk) MuatData() error {
	//muat data dari file
	data, err := ioutil.ReadFile(namaFile)
	if err != nil {
		return err
	}

	// parse data ke dalam bentuk data struct
	err = json.Unmarshal([]byte(data), &d)
	return err
}

Fungsi Menambahkan Data

Seperti halnya fungsi MuatData(), fungsi ini juga merupakan fungsi pada type DataPenduduk.

Bedanya disini DataPenduduk akan disalin dan dimodifikasi untuk disimpan kedalam file. Dengan argumen type Penduduk yang akan di tambahkan kedalam DataPenduduk.

//Menambahkan data penduduk kedalam file
func (d DataPenduduk) Tambah(p Penduduk) error {
	id := 1         // id jika belum ada data
	if len(d) > 0 { // id jika sudah ada data
		id = d[len(d)-1].ID + 1 //increment dari id data sebelumnya
	}
	p.ID = id //memberi id pada data baru yang akan disimpan

	//menggabungkan data baru di Daftar DataPenduduk
	d = append(d, p)

	// Mengubah data struct Data Penduduk ke dalam format json
	json, err := json.Marshal(d)
	if err != nil {
		return err
	}

	// menyimpan/menulis ulang data dalam file
	err = ioutil.WriteFile(namaFile, json, 0644)
	return err
}

Fungsi Untuk Melihat Daftar Data Yang Disimpan

Pada fungsi ini kita hanya menyalin nilai type DataPenduduk dan menampilkan data pada terminal menggunakan fungsi Println.

// menampilkan daftar data penduduk
func (d DataPenduduk) Daftar() {
	fmt.Println("== Daftar Data Penduduk ==")

	// list semua data menggunnakan perulangan
	for _, v := range d {
		fmt.Println("ID: ", v.ID)
		fmt.Println("Nama: ", v.Nama)
		fmt.Println("Alamat: ", v.Alamat)
		fmt.Println("Umur: ", v.Umur, "\n")
	}
}

Fungsi Main

Fungsi main adalah fungsi pertama yang akan dijalankan ketika program di eksekusi. Disini kita menginisialisasi variable data dengan type DataPenduduk sebagai wadah data Penduduk dan variabel input yang akan menampung setiap inputan pengguna.

// Fungsi utama program
func main() {
	input := os.Args
	data := DataPenduduk{}

	//Memuat data dari file
	err := data.MuatData()
	if err != nil {
		fmt.Println("File belum dibuat atau tidak ditemukan")
	}

	// Cek input pengguna
	if len(input) > 1 {
		if len(input) < 4 { 
			// tampilkan pesan jika inputan tidak lengkap
			fmt.Println("Data yang anda isi kurang lengkap\n" +
				"format input: \"Nama\" \"Alamat\" umur ")
			os.Exit(0)
		}

		// Jika inputan lengkap maka bisa di proses
		umur, _ := strconv.Atoi(input[3])
		penduduk := Penduduk{
			Nama:   input[1],
			Alamat: input[2],
			Umur:   umur,
		}

		// Menyimpan data kedalam file
		data.Tambah(penduduk)

	} else {
		// Jika tidak ada input argumen
		// Maka cukup tampilkan data penduduk 
		data.Daftar()
	}
}

Menjalankan Program

Bagian yang paling menyenangkan adalah mencari tahu hasil program yang telah kita tulis. Untuk menjalankan program gunakan perintah yang biasa digunakan yaitu go run:

>$ go run main.go
File belum dibuat atau tidak ditemukan
== Daftar Data Penduduk ==

Seperti yang kita lihat, kita memiliki error File belum dibuat atau tidak ditemukan hal ini karena fungsi MuatData() tidak menemukan file yang akan dibaca datanya dan data yang ditampilkan oleh fungsi Daftar() adalah kosong.

Pesan error ini akan hilang ketika kita menambahan data menggunakan argumen seperti berikut:

go run main.go "Yaka" "Yogyakarta" 19

Setelah menjalankan perintah diatas pesan error tetap muncul, hal ini dikarenakan program akan menjalankan fungsi MuatData() pertama kali setiap program dijalankan.

Pesan error akan hilang ketika kita menjalankan go run main.go lagi, Karena kita sudah memiliki file yang secara otomatis dibuat untuk menampung data yang kita inputkan.

Memodifikasi Program

Program yang kita buat disini tentunya jauh dari sempurna, kamu bisa mengubahnya sesuai keiginan. Misalnya tampilan pesan error yang mengganggu bisa kita hilangkan dengan menghapus kode yang di highligh dan nama variabel err diganti underscore _:

	//Memuat data dari file
	_ := data.MuatData()
-	err := data.MuatData()
-	if err != nil {
-		fmt.Println("File belum dibuat atau tidak ditemukan")
-	}

Underscore _ sebagai nama variabel digunakan untuk mengabaikan data yang di kembalikan oleh sebuah fungsi. Dalam kasus ini kamu juga bisa menulis hanya fungsinya saja:

	//Memuat data dari file
	data.MuatData()
-	err := data.MuatData()
-	if err != nil {
-		fmt.Println("File belum dibuat atau tidak ditemukan")
-	}