Pembuatan file dapat cukup mudah dilakukan di Go yaitu menggunakan package dari os, dengan fungsi os.Create()
Langsung saja praktik, buat sebuah folder baru dengan nama file, agar lebih rapih buatlah folder baru didalamnya dengan nama data kemudian buat file baru didalam data dengan nama create_file.go
yang isinya adalah sebagai berikut.
package data
import (
"fmt"
"os"
"enigmacamp.com/basic-go/18-file/utils"
)
func CreateFile(fileName string) {
// deteksi apakah file sudah ada
// ini ganti aja sesuaikan (cek menggunakan pwd)
path := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/"
fileName := "example.txt"
filePath := path + fileName
_, err := os.Stat(filePath)
// buat file baru
if os.IsNotExist(err) {
file, err := os.Create(filePath)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
}
fmt.Println("File dibuat...", fileName)
}
Penjelasan:
os.IsNotExist()
-> untuk mendeteksi apakah file sudah dibuat atau belum.os.Stat()
-> mengembalikan 2 data, yaitu informasi tetang path yang dicari, dan error (jika ada).os.Create()
-> digunakan untuk membuat file pada path tertentu.
File yang baru terbuat statusnya adalah otomatis open, maka dari itu perlu untuk di-close menggunakan method file.Close()
setelah file tidak digunakan lagi.
Read File (os.ReadFile
)
File pada Go menggunakan sebuah pointer of object dari os.File
Ketika pada materi sebelumnya (input) untuk menulis dan membaca adalah menggunakan os.Stdin
dan os.Stdout
.
Isi dari db.dat adalah sebagai berikut:
Andi
Budi
Candra
Dodi
Erna
Selanjutnya buat file baru di data dengan nama get_file_os_open.go
package data
import (
"bufio"
"fmt"
"io"
"os"
)
func OpenFileWithOsOpen(fileName string) {
inputFile, inputError := os.Open(fileName) // *os.File, error
if inputError != nil {
fmt.Printf("Terjadi masalah ketika membaca file\n" +
"Apakah file ada?\n" +
"Ada akses ?\n")
return // keluar jika error
}
defer inputFile.Close()
inputReader := bufio.NewReader(inputFile)
for {
inputString, readError := inputReader.ReadString('\n')
if readError == io.EOF {
return
}
fmt.Printf("Nama: %s", inputString)
}
}
Penjelasan:
io.EOF
menandakan bahwa file yang sedang dibaca adalah baris terakhir isi atau end of file.
Buat file index.go
yang isinya adalah sebagai berikut
package main
import "enigmacamp.com/basic-go/18-file/data"
// set global var
var fileLocation = "db.dat"
func main() {
// data.CreateFile(fileLocation)
data.OpenFileWithOsOpen(fileLocation)
}
Jalankan program dan hasilnya adalah
Solusinya adalah, kita harus tahu benar letak atau posisi file itu dimana, untuk cek bisa menggunakan perintah pwd
Jika sudah silahkan modifikasi menjadi
package main
import "enigmacamp.com/basic-go/18-file/data"
// set global var
var fileLocation = "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/basic-programming-go/18-file/db.dat"
func main() {
// data.CreateFile(fileLocation)
data.OpenFileWithOsOpen(fileLocation)
}
Jalankan kembali
Selain menggunakan os.Open dari os Package dan io, kita bisa juga menggunakan package dari io/ioutil, buat file baru dengan nama get_file_io_readfile.go.
package data
import (
"fmt"
"io/ioutil"
"os"
)
func OpenFileWithReadFile(fileName string) {
buf, err := ioutil.ReadFile(fileName)
if err != nil {
fmt.Fprintf(os.Stderr, "File error: %s\n", err)
}
fmt.Printf("%s\n", string(buf))
}
Jangan gunakan ReadFile ketika mengakses sebuah file dengan ukuran besar, karena akan membutuhkan banyak memori ReadFile (os.OpenFile) os.OpenFile adalah fungsi yang memungkinkan kontrol lebih besar atas mode pembukaan file. Fungsi OpenFile mengambil nama file, satu atau lebih flag (secara logis OR-d bersama-sama menggunakan | bitwise OR operator jika lebih dari satu) dan izin file untuk digunakan. Beberapa flag yang dapat digunakan:
os.O_RDONLY
: the read flag for read-only accessos.O_WRONLY
: the write flag for write-only accessos.O_RDWR
: the flag that provides both read and write accessos.O_CREATE
: the create flag to create the file if it doesn’t existos.O_TRUNC
: the truncate flag to truncate to size 0 if the file already exists
Saat membaca, izin file diabaikan sehingga kami dapat menggunakan nilai 0. Saat menulis, kami menggunakan izin file Unix standar 0666 (bahkan di Windows).
Berikut contoh kodenya, buat file baru di data dengan nama get_file_os_open_file.go
package data
import (
"fmt"
"io"
"os"
)
func OpenFileWithOpenFile(fileName string) {
file, err := os.OpenFile(fileName, os.O_RDONLY, 0644)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
// Mendapatkan informasi ukuran file
fileInfo, err := file.Stat()
if err != nil {
fmt.Println(err)
return
}
fileSize := fileInfo.Size()
// Membuat buffer sesuai dengan ukuran file
buffer := make([]byte, fileSize)
// Membaca isi file ke dalam buffer
_, err = file.Read(buffer)
if err != nil {
fmt.Println(err)
return
}
// Menampilkan isi file
fmt.Println(string(buffer))
}
Panggil fungsi diatas pada main()
package main
import "enigmacamp.com/basic-go/18-file/data"
// set global var
var fileLocation = "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/basic-programming-go/18-file/db.dat"
func main() {
// data.CreateFile(fileLocation)
// data.OpenFileWithOsOpen(fileLocation)
// data.OpenFileWithReadFile(fileLocation)
data.OpenFileWithOpenFile(fileLocation)
}
Write (WriteString)
Buat file baru di folder data dengan nama writing_to_file.go
package data
import (
"fmt"
"os"
)
func WriteToFileWithString(filePath string, data string) {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Gagal membuka file: %v\n", err)
os.Exit(1)
}
_, err = file.WriteString(data + "\n")
if err != nil {
fmt.Printf("Gagal menulis ke file: %v\n", err)
os.Exit(1)
}
err = file.Sync()
if err != nil {
fmt.Printf("Gagal sinkronisasi ke file: %v\n", err)
os.Exit(1)
}
}
Pada main()
package main
import "enigmacamp.com/basic-go/18-file/data"
// set global var
var fileLocation = "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/basic-programming-go/18-file/db.dat"
func main() {
// data.CreateFile(fileLocation)
// data.OpenFileWithOsOpen(fileLocation)
// data.OpenFileWithReadFile(fileLocation)
// data.OpenFileWithOpenFile(fileLocation)
data.WriteToFileWithString(fileLocation, "Jution")
}
Kode diatas hanya untuk inputan sekali, jika mencoba untuk input lagi isi file lama maka akan ter-replace Agar tidak ter-replace maka rubah pada bagian ini outputFile, outputError := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0644) Jenis dan Tipe Menulis ke File Disini akan mencontohkan beberapa tipe penulisan ke sebuah file yaitu plain text, csv dan json. Contoh data dalam bentuk CSV
nama,usia,alamat
Jution,25,Jakarta
Fadli,28,Bandung
Contoh data dalam bentuk JSON.
[
{
"id": "001",
"nama": "Jution Candra Kirana"
}
]
// untuk [][]string bisa ganti dengan Struct (bisa berikan struct tag csv
)
func WriteToFileCSV(filePath string, data [][]string) {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Gagal membuka file: %v\n", err)
os.Exit(1)
}
writer := csv.NewWriter(file)
writer.WriteAll(data)
writer.Flush()
fmt.Println("Data berhasil disimpan dalam file example.csv")
}
Sehingga pada main.
// CSV
data := [][]string{
{"nama", "usia", "alamat"},
{"Jution", "25", "Jakarta"},
{"Fadli", "28", "Bandung"},
}
fileCSV := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.csv"
handler.WriteToFileCSV(fileCSV, data)
Terdapat kesalahan saat penambahan data, harusnya ketika kita membuat data baru kita tidak perlu menuliskan header seperti nama, usia, alamat. Kode sebelumnya juga ketika data ada yang baru lagi maka akan ter-replace. Perbaikan kode adalah sebagai berikut.
func WriteToFileCSV(filePath string, data [][]string) {
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Gagal membuka file: %v\n", err)
os.Exit(1)
}
defer file.Close()
writer := csv.NewWriter(file)
if fileStat, _ := file.Stat(); fileStat.Size() == 0 {
writer.Write([]string{"nama", "usia", "alamat"})
}
writer.WriteAll(data)
writer.Flush()
fmt.Println("Data berhasil disimpan dalam file", filePath)
}
Pada main menjadi berikut.
package main
import (
"github.com/jutionck/golang-fundamental-sandbox/day-9/file/handler"
)
func main() {
fileCSV := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.csv"
newPenduduk := [][]string{
{"Jution", "25", "Bandung"},
{"Fadli", "22", "Jakarta"},
}
handler.WriteToFileCSV(fileCSV, newPenduduk)
}
Jalankan program.
// untuk []string bisa ganti dengan Struct (bisa berikan struct tag `json`)
func WriteToFileJSON(filePath string, data []map[string]string) {
jsonData, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Println(err)
return
}
err = ioutil.WriteFile(filePath, jsonData, 0644)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Data berhasil disimpan dalam file", filePath)
}
Sehingga pada main.
dataJSON := []map[string]string{
{"nama": "Jution", "usia": "25", "alamat": "Jakarta"},
{"nama": "Fadli", "usia": "28", "alamat": "Bandung"},
}
fileJSON := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.json"
handler.WriteToFileJSON(fileJSON, dataJSON)
Terdapat kesalahan pada saat menambahkan data JSON, karena ketika kita mencoba memasukkan data baru maka data lama akan ter-replace. Kenapa seperti itu karena kita tidak melakukan pengecekan terlebih dahulu apakah data sudah ada atau tidak. Perbaikan yang dapat dilakukan adalah sebagai berikut.
func WriteToFileJSON(filePath string, data map[string]string) {
var ps []map[string]string
penduduk, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Printf("Gagal membaca file: %v\n", err)
os.Exit(1)
}
if len(penduduk) != 0 {
err = json.Unmarshal(penduduk, &ps)
if err != nil {
fmt.Printf("Gagal membaca file: %v\n", err)
os.Exit(1)
}
}
ps = append(ps, data)
jsonData, err := json.MarshalIndent(ps, "", " ")
if err != nil {
fmt.Println(err)
return
}
err = ioutil.WriteFile(filePath, jsonData, 0644)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Data berhasil disimpan dalam file", filePath)
}
Sekarang pada main menjadi berikut.
package main
import (
"github.com/jutionck/golang-fundamental-sandbox/day-9/file/handler"
)
func main() {
fileJSON := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.json"
newPenduduk := map[string]string{
"nama": " Jution",
"usia": " 25",
"alamat": " Jakarta",
}
handler.WriteToFileJSON(fileJSON, newPenduduk)
}
Jalankan lagi program.
Untuk membaca file .csv
func ReadFileCSV(filePath string) {
file, err := os.OpenFile(filePath, os.O_RDONLY, os.ModePerm)
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
fmt.Println("Error:", err)
return
}
// Ambil baris pertama sebagai header
header := records[0]
// Buat slice untuk menampung data
var data []map[string]string
// Loop untuk setiap baris data (mulai dari baris kedua)
for i := 1; i < len(records); i++ {
row := records[i]
m := make(map[string]string)
for j := 0; j < len(header); j++ {
m[header[j]] = row[j]
}
data = append(data, m)
}
fmt.Println(data)
}
Pastikan file .csv ada. Disini masih menggunakan file yang sebelumnya di atas. Jika kita lihat isi dari file .csv adalah seperti berikut.
nama,usia,alamat
Jution,25,Jakarta
Fadli,28,Bandung
Jika sudah kita panggil funciton read csv yang sudah dibuat pada func main
fileCSV := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.csv"
// Read file CSV
handler.ReadFileCSV(fileCSV)
Sekarang kita akan membaca file dari .json Pastikan file sudah ada dan ada isinya, disini file .json isinya seperti berikut.
[
{
"alamat": "Jakarta",
"nama": "Jution",
"usia": "25"
},
{
"alamat": "Bandung",
"nama": "Fadli",
"usia": "28"
}
]
Untuk kode membaca data json seperti berikut.
func ReadFileJSON(filePath string) {
file, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Println("Error reading JSON file:", err)
return
}
// Dekode file JSON ke dalam map[string]interface{}
var data []map[string]interface{}
if err := json.Unmarshal(file, &data); err != nil {
fmt.Println("Error decoding JSON file:", err)
return
}
// Tampilkan data dari map
for _, item := range data {
fmt.Printf("Nama: %s\n", item["nama"])
fmt.Printf("Usia: %s\n", item["usia"])
fmt.Printf("Alamat: %s\n", item["alamat"])
}
}
Setelah itukita panggil di main.
// JSOn
fileJSON := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.json"
// Read file JSON
handler.ReadFileJSON(fileJSON)
Jalankan program.
Praktik ini melanjutkan untuk praktik write json dan read json. Kebutuhan update sangat diperlukan ketika kita sudah mempunyai data pada json dan ingin merubah. Kode berikut adalah cara untuk melakukan perubah data, tetapi sebelum itu kita akan mencoba sedikit modifikasi, sebelumnya kita menggunakan sebuah map sekarang kita coba ubah menjadi sebuah struct. Buat struct baru dengan nama Penduduk misalnya.
type Penduduk struct {
Nama string
Usia string
Alamat string
}
Pada struct kita bisa memberikan sebuah tag, karena kita akan membaca sebuah file JSON maka bisa kita ubah struct di atas dengan menambahkan struct tag json. Jaidnya seperti ini.
type Penduduk struct {
Nama string `json:"nama"`
Usia string `json:"usia"`
Alamat string `json:"alamat"`
}
Sekarang ubah function ReadJSON sebelumnya dengan ini.
func ReadFileJSON(filePath string) {
file, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Println("Error reading JSON file:", err)
return
}
// Dekode file JSON ke dalam map[string]interface{}
var data []model.Penduduk
if err := json.Unmarshal(file, &data); err != nil {
fmt.Println("Error decoding JSON file:", err)
return
}
for _, item := range data {
fmt.Printf("Nama: %s\n", item.Nama)
fmt.Printf("Usia: %s\n", item.Usia)
fmt.Printf("Alamat: %s\n", item.Alamat)
fmt.Println()
}
}
Kita coba dahulu pada main.
package main
import "github.com/jutionck/golang-fundamental-sandbox/day-9/file/handler"
func main() {
fileJSON := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.json"
// Read file JSON
handler.ReadFileJSON(fileJSON)
}
Kita jalankan program.
Lanjut, sekarang kita mencoba untuk merubah data yang sudah ada, misalnya kita akan merubah data dengan data Jution. Disini menggunakan nama karena di struct kita tidak terdapat id, sebaiknya kita memberikan id sebagai unique value. Kode nya adalah sebagai berikut.
func UpdateDataJSON(filePath string, newData interface{}) error {
// Baca file JSON
jsonFile, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
if err != nil {
return err
}
defer jsonFile.Close()
byteValue, err := ioutil.ReadAll(jsonFile)
if err != nil {
return err
}
// Parse data JSON ke dalam slice of struct
var people []model.Penduduk
err = json.Unmarshal(byteValue, &people)
if err != nil {
return err
}
// convert interface{} ke struct
newDataConvert := newData.(model.Penduduk)
// Ubah data di dalam slice of struct
for i, v := range people {
if v.Nama == newDataConvert.Nama {
people[i] = newDataConvert
break
}
}
// Tulis kembali data ke dalam file JSON
updatedData, err := json.MarshalIndent(people, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(filePath, updatedData, 0644)
if err != nil {
return err
}
fmt.Println("Data berhasil diupdate")
return nil
}
Pada main untuk merubah data seperti ini.
package main
import (
"fmt"
"github.com/jutionck/golang-fundamental-sandbox/day-9/file/handler"
"github.com/jutionck/golang-fundamental-sandbox/day-9/file/model"
)
func main() {
fileJSON := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.json"
// Read file JSON
handler.ReadFileJSON(fileJSON)
// Ubah Data
updateData := model.Penduduk{
Nama: "Jution",
Usia: "25",
Alamat: "Lampung",
}
err := handler.UpdateDataJSON(fileJSON, updateData)
if err != nil {
fmt.Println(err)
return
}
}
Sekarang data jution berubah.
Setelah berhasil merubah data, sekarang kita akan mencoba menghapus data pada file JSON. Kode nya adalah sebagai berikut.
func RemoveDataJSON(filePath string, nama string) {
file, err := os.OpenFile(filePath, os.O_RDWR, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// membaca isi file JSON
data, err := ioutil.ReadAll(file)
if err != nil {
log.Fatal(err)
}
// mengubah data JSON menjadi slice of struct
var people []model.Penduduk
err = json.Unmarshal(data, &people)
if err != nil {
log.Fatal(err)
}
// hapus data dengan ID 2
for i, person := range people {
if person.Nama == nama {
people = append(people[:i], people[i+1:]...)
break
}
}
// encode data ke JSON
newData, err := json.MarshalIndent(people, "", " ")
if err != nil {
log.Fatal(err)
}
// menulis data ke file JSON
err = ioutil.WriteFile(filePath, newData, 0644)
if err != nil {
log.Fatal(err)
}
fmt.Println("Data berhasil dihapus dari file JSON")
}
Karena sekali lagi kita tidak ada Id pada struct maka kita gunakan sebuah nama. Pada main jadi seperti ini.
package main
import (
"fmt"
"github.com/jutionck/golang-fundamental-sandbox/day-9/file/handler"
"github.com/jutionck/golang-fundamental-sandbox/day-9/file/model"
)
func main() {
fileJSON := "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/golang-fundamental-sandbox/day-9/file/example.json"
// Hapus Data
nama := "Jution"
handler.RemoveDataJSON(fileJSON, nama)
}
Jalankan dan data berhasil di hapus.
Buat file baru di dalam folder data bernama delete_file.go
package data
import (
"fmt"
"os"
"enigmacamp.com/basic-go/18-file/utils"
)
func DeleteFile(fileName string) {
err := os.Remove(fileName)
if utils.IsError(err) {
return
}
fmt.Println("File berhasil di hapus")
}
Pada main()
package main
import "enigmacamp.com/basic-go/18-file/data"
// set global var
var fileLocation = "/Users/jutioncandrakirana/Documents/GitHub/enigma/GOLANG/basic-programming-go/18-file/db.dat"
func main() {
// data.CreateFile(fileLocation)
// data.OpenFileWithOsOpen(fileLocation)
// data.OpenFileWithReadFile(fileLocation)
// data.OpenFileWithOpenFile(fileLocation)
// data.WriteToFile(fileLocation, "Jution")
data.DeleteFile(fileLocation)
}
Digunakan untuk kebutuhan membuat aplikasi sederhana menggunakan file. Kode ini bisa disimpan kedalam folder utils kemudian beri nama file_handler.go.
package utils
import (
"fmt"
"io/ioutil"
"os"
)
func OpenFile(filepath string) (*os.File, error) {
file, err := os.OpenFile(filepath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return nil, fmt.Errorf("failed to open file: %v", err)
}
return file, nil
}
func CloseFile(file *os.File) error {
err := file.Close()
if err != nil {
return fmt.Errorf("failed to close file: %v", err)
}
return nil
}
func ReadFile(filepath string) ([]byte, error) {
fileInfo, err := os.Stat(filepath)
if err != nil {
return nil, fmt.Errorf("failed to read file info: %v", err)
}
var data []byte
// Mengecek ukuran file menggunakan fileInfo.Size()
if fileInfo.Size() == 0 {
// Jika ukuran file sama dengan 0,
// maka fungsi akan mengembalikan byte slice kosong ([]byte{}).
data = []byte{}
} else {
data, err = ioutil.ReadFile(filepath)
if err != nil {
return nil, fmt.Errorf("failed to read file: %v", err)
}
}
return data, nil
}
func WriteToFile(file *os.File, data []byte) error {
// Perbedaan utama antara file.Write dan
// ioutil.WriteFile adalah pada cara penggunaannya.
// Jika kita ingin menambahkan data ke akhir file tanpa menimpa data yang sudah ada, kita harus menggunakan file.Write.
// Namun, jika kita ingin menulis data baru ke file dan menimpa data yang sudah ada, kita harus menggunakan ioutil.WriteFile.
_, err := file.Write(data)
if err != nil {
return fmt.Errorf("failed to write to file: %v", err)
}
return nil
}