Modul 3 Struktur Data: I/O, CodeChef

Modul 3 Struktur Data: I/O, CodeChef

Kembali ke Struktur Data (dengan Python)

Di praktikum kali ini, kita akan belajar tentang I/O (input/output), yaitu cara berurusan dengan input dan output di Python. Bukan hanya sekadar print dan input, tetapi juga cara berurusan dengan text file.

Selain itu, kita akan belajar tentang Graphviz, yang bisa kita gunakan untuk membuat berbagai gambar “graf”, dan bisa digunakan untuk menggambar berbagai jenis struktur data nantinya. Pembahasan tentang Graphviz ditunda ke praktikum yang akan datang.

Terakhir, untuk melatih dan mendalami urusan input/output, kita akan berkenalan dengan CodeChef (https://www.codechef.com/), suatu situs “competitive programming”. Kesannya seolah-olah untuk persiapan lomba, tetapi maksudnya situs tersebut punya “bank soal”, lebih tepatnya di link berikut (jangan lupa membuat akun terlebih dahulu):

Ada banyak latihan soal yang bisa kita coba untuk melatih kemampuan pemrograman kita. Siapa tahu, kalian akan mengambil soal dari situ dan menyesuaikan untuk proyek akhir Struktur Data :)

I/O (input/output)

Seputar print

Kegunaan utama print adalah untuk menampilkan string (str).

print("Hello, world!")
Hello, world!
teks1 = "Selamat sore!"
print(teks1)
Selamat sore!

Kita bisa menampilkan beberapa string sekaligus di dalam satu print, memisakan tiap string dengan koma.

print("Saya", "sudah", "makan", "siang")
Saya sudah makan siang

Sebenarnya, kita bisa menggunakan print untuk menampilkan tipe data apapun.

angka = -45
harga = 10.6
print(angka)
print(harga)
-45
10.6

Sehingga, kita bisa menuliskan seperti ini:

print("Suhu:", angka)
Suhu: -45

Kalau mau, kita juga bisa menyiapkan suatu string yang utuh terlebih dahulu (mengubah tipe data lain menjadi string dengan str), baru menampilkan string yang utuh tersebut:

string_utuh = "Suhu: " + str(angka)
print(string_utuh)
Suhu: -45

Apabila kita print suatu list begitu saja, maka akan ditampilkan sebagai list.

beberapa_buah = ["pisang", 42, -5.1, "apel", "jeruk"]
print(beberapa_buah)
['pisang', 42, -5.1, 'apel', 'jeruk']

Namun, kita bisa saja menggunakan for loop untuk menampilkan tiap elemen.

for elemen in beberapa_buah:
    print(elemen)
pisang
42
-5.1
apel
jeruk

Begitu juga untuk set (tentu saja urutannya tidak menentu):

beberapa_warna = {"merah", "hijau", "biru", "kuning"}
print(beberapa_warna)
{'merah', 'biru', 'kuning', 'hijau'}
for warna in beberapa_warna:
    print(warna)
merah
biru
kuning
hijau

Untuk suatu dict, kita bisa menampilkan dict seutuhnya:

harga_toko = {"kopi": 6000, "teh": 5000, "susu": 7000}
print(harga_toko)
{'kopi': 6000, 'teh': 5000, 'susu': 7000}

Kita bisa memperoleh set yang berisi key nya saja dengan dict.keys(), baru menampilkan set tersebut:

yang_dijual = set(harga_toko.keys())
print(yang_dijual)
{'kopi', 'teh', 'susu'}

Serupa, kita bisa memperoleh set yang berisi value nya saja menggunakan dict.values():

semua_harga = set(harga_toko.values())
print(semua_harga)
{6000, 7000, 5000}

Kalau mau, kita bisa melakukan for loop untuk tiap key:

for key in harga_toko.keys():
    print(key, harga_toko[key])
kopi 6000
teh 5000
susu 7000

Bahkan, kita bisa melakukan for loop untuk tiap key dan value sekaligus, dengan dict.items():

for key, value in harga_toko.items():
    print(key, "harganya", value)
kopi harganya 6000
teh harganya 5000
susu harganya 7000

Umumnya, tiap kali kita menggunakan print, baris baru selalu ditambahkan secara otomatis, sehingga print yang selanjutnya akan ditampilkan di baris berikutnya. Sebenarnya, hal ini bisa diatur dengan setting end= seperti berikut:

print("Hari", end="\n")
print("ini", end="\n")
print("Jum'at", end="\n")
Hari
ini
Jum'at

end= bisa berupa apa saja:

print("Hari", end="|")
print("ini", end="|")
print("Jum'at", end="|")
Hari|ini|Jum'at|

Bahkan, kita bisa mengkosongkan end= (membuatnya menjadi string kosong atau "" atau '') apabila kita mengharapkan tidak ada “pemisah” antara tiap output:

print("Hari", end="")
print("ini", end="")
print("Jum'at", end="")
HariiniJum'at

Seputar input

Mneggunakan input, kita bisa menerima masukkan data berupa string.

nama = input()
print("Halo", nama)
Halo Bisma

Kita bisa menggunakan prompt berupa string dalam input, yaitu semacam “pertanyaan” agar jelas data apa yang diperlukan.

nama = input("Masukkan nama: ")
print("Halo", nama)
Masukkan nama: Bisma
Halo Bisma

Apabila input yang kita inginkan adalah selain string, kita harus mengakali. Contohnya, bisa saja kita langsung mengkonversi string yang masuk menjadi tipe data lain:

umur = int(input("Masukkan umur: "))
print("Tahun depan, Anda akan berumur", umur+1, "tahun")
Masukkan umur: 19
Tahun depan, Anda akan berumur 20 tahun

Bahkan menjadi list juga bisa, menggunakan split untuk memecah suatu string menjadi beberapa bagian (dalam suatu list) berdasarkan suatu pemisah (di sini ,):

beberapa_angka = input("Masukkan beberapa angka: ").split(",")
print("Input yang masuk:", beberapa_angka)
sum = 0
for angka in beberapa_angka:
    sum += float(angka)
print("Totalnya adalah", sum)
Masukkan beberapa angka: -10, 5.6, 3, -7, 82
Input yang masuk: ['-10', ' 5.6', ' 3', ' -7', ' 82']
Totalnya adalah 73.6

Berurusan dengan text file

Di Python, kita bisa membuka, mengedit, dan menutup text file, yaitu file yang berakhiran .txt

Ketika membuka suatu text file, ada beberapa pilihan “mode”:

  • r: read-only, jika kita hanya ingin membaca isinya. Kalau file nya tidak ada, error.
  • a: append-only, jika kita hanya ingin menambahkan isi di akhir text file (sehingga tidak bisa melihat isi yang sudah ada). Kalau file nya belum ada, akan dibuat.
  • w: write-only, jika kita hanya ingin menulis (tanpa bisa membaca isi yang sudah ada) dan menimpa apapun tulisan yang sudah ada. Kalau file nya belum ada, akan dibuat.
  • r+: read and write. Kalau file nya tidak ada, error.
  • a+: append and read. Kalau file nya belum ada, akan dibuat.
  • w+: write and read. Kalau file nya belum ada, akan dibuat.

Untuk fitur yang paling lengkap (tetapi bisa berbahaya apabila kita tidak berhati-hati), bisa digunakan mode w+.

Kita bisa membuka suatu file dengan open. Dengan begitu, kita akan memperoleh suatu objek file. Objek ini memiliki beberapa atribut seperti .mode, dan beberapa method seperti:

  • .write() untuk menulis
  • .read() untuk membaca (memperoleh isinya sebagai string)
  • .seek() agar “cursor” lompat ke posisi tertentu (misalnya .seek(0) untuk kembali ke awal file)
  • .close() untuk menutup file setelah selesai digunakan

(Apabila kita ingin mengubah mode, kita bisa melakukan .close() terlebih dahulu, baru open lagi dengan mode yang baru.)

Di kode di bawah ini, kita akan membuka suatu file, menuliskan Hello, world! di dalamnya, lalu menutup file nya.

teks = open("test.txt", 'w+')
teks.write("Hello, world!")
teks.close()

Setelah running kode di atas, coba cek folder kalian yang menyimpan file .ipynb yang sedang kalian gunakan. Harusnya, ada file baru yang muncul bernama test.txt dan isinya Hello, world!

  • Di Jupyter Notebook, kalian bisa kembali ke tab yang terbuka di browser kalian di mana kalian tadinya sudah membuat new notebook. Coba double-click test.txt
  • Di Google Colaboratory, kalian bisa menekan tombol folder yang ada di sebelah kiri. Coba download test.txt lalu buka isinya

Mari kita coba gunakan .write() untuk menuliskan sesuatu di dalamnya, lalu .seek(0) untuk kembali ke awal file, kemudian .read() untuk membaca isinya (mulai dari awal file sesuai yang ditentukan oleh .seek())

teks = open("test.txt", 'w+')
teks.write("Selamat pagi")
teks.seek(0)
print(teks.read())
teks.close()
Selamat pagi

Setelah running kode di atas, kalau mau, silakan dibuka kembali.

  • Menggunakan Jupyter Notebook: kalau tadinya sudah dibuka, ditutup dulu, baru dibuka lagi.
  • Menggunakan Google Colaboratory: tunggu sebentar, download lagi, lalu buka isinya.

Pasti ada isinya, yaitu tulisan Selamat pagi

Tulisan Selamat pagi yang tadi tertimpa, karena kita menggunakan mode w, bukan a.

Sebenarnya, kalaupun tidak diperiksa isinya, kita bisa yakin bahwa isinya sudah berubah sesuai yang kita inginkan, karena kita sudah melihat isi yang baru dengan .read()

Mari kita coba menuliskan hal lain.

teks = open("test.txt", 'w+')
teks.write("Selamat siang")
teks.seek(0)
print(teks.read())
teks.close()
Selamat siang

Kalau kita buka kembali, sekarang tulisannya adalah Selamat siang

Ada cara penulisan lain agar Python akan menutup file secara otomatis, yaitu menggunakan yang namanya context manager atau with statement. Syntax nya sebagai berikut:

with open("test.txt", 'w+') as teks:
    teks.write("Selamat sore")
    teks.seek(0)
    print(teks.read())
Selamat sore

Yang tadinya kita tulis teks = open("test.txt", 'w+'), sekarang kita tulis dengan with ... as ... dengan titik dua di akhir. Lalu, semua hal yang mau kita lakukan dengan file tersebut (yang di sini sekarang namanya teks) itu kita lakukan di dalam with statement tersebut, dengan indentasi, seperti dalam for loop misalnya.

Begitu keluar dari with statement, file akan ditutup secara otomatis. Dengan demikian, kita tidak perlu lagi melakukan .close()

Anyway, boleh diperiksa lagi file nya, sekarang tulisannya menjadi Selamat sore

Kalau mau, kita bisa saja melakukan .write() berkali-kali untuk menambahkan tulisan berbaris-baris:

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi")
    teks.write("Selamat siang")
    teks.write("Selamat sore")
    teks.write("Selamat malam")
    teks.seek(0)
    print(teks.read())
Selamat pagiSelamat siangSelamat soreSelamat malam

Oops, jangan lupa tambahkan \n di akhir, ya!

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")
    teks.seek(0)
    print(teks.read())
Selamat pagi
Selamat siang
Selamat sore
Selamat malam

Kalau iseng, kita bisa melakukan misalnya teks.seek(21) untuk melompat 21 karakter dari awal file, sebelum melakukan .read():

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")
    teks.seek(21)
    print(teks.read())
siang
Selamat sore
Selamat malam

Dengan demikian, .read() hanya akan membaca tulisan yang ada mulai dari posisi ke-21 tersebut.

Kalau mau, kita bisa saja hanya membaca beberapa karakter, misalnya hanya 5 karakter:

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")
    teks.seek(21)
    print(teks.read(5))
siang

Kita bahkan bisa .seek() lagi setelah .read(), lalu .read() lagi, .seek() lagi, dan seterusnya sesuka hati.

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")
    teks.seek(21)
    print(teks.read(5))
    teks.seek(48)
    print(teks.read(5))
    teks.seek(35)
    print(teks.read(4))
siang
malam
sore

Setelah melakukan read, sebenarnya cursor akan ikut maju! Seandainya kita melakukan read dua kali berturut-turut, perhatikan:

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")
    teks.seek(21)
    print(teks.read(5), end="")
    print(teks.read(7), end="")
siang
Selama

Di sini, kita menggunakan end="" agar print tidak menambahkan baris baru. Lho, tapi ada baris baru? Baris baru itu sebenarnya dari file itu sendiri :)

Perhatikan juga bahwa baris baru \n itu terhitung sebagai satu karakter dengan sendirinya, sehingga yang tadinya kita mau membaca Selamat (7 karakter) itu malah hanya menjadi Selama (6 karakter), karena jatah satu karakter sudah digunakan untuk baris baru.

Seandainya kita tidak menggunakan .seek() sama sekali, maka setelah selesai .write(), cursor akan terletak di akhir file (karena tadinya sudah selesai menulis sampai situ) sehingga .read() tidak akan memberikan output apapun:

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")
    print(teks.read())

Kita bisa melihat posisi cursor saat ini dengan .tell()

with open("test.txt", 'w+') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")
    print(teks.tell())
53

Wow, jauh ya! Tidak heran, tidak ada lagi yang perlu dibaca.

Kalau misalnya kita muak dengan .seek(), kita bisa saja menutup file setelah menulis, barulah kemudian membuka file lagi untuk melihat isinya. Kita bisa melakukan itu dengan dua kali with statement. (Ada baiknya kita menggunakan mode yang sesuai, misalnya w saja untuk menulis saja, lalu r saja untuk membaca saja.)

with open("test.txt", 'w') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")

with open("test.txt", 'r') as teks:
    print(teks.read())
Selamat pagi
Selamat siang
Selamat sore
Selamat malam

Kok bisa? Karena, setelah file ditutup, ketika dibuka lagi, cursor akan kembali ke posisi awal. Sehingga, kita tidak perlu menggunakan .seek() untuk meminta agar kembali ke posisi awal. Lihat saja:

with open("test.txt", 'w') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")

with open("test.txt", 'r') as teks:
    print(teks.tell()) # output 0 karena berada di awal file
    print(teks.read())
0
Selamat pagi
Selamat siang
Selamat sore
Selamat malam

Selain .read() beberapa karakter, kita juga bisa membaca satu baris (yaitu berhenti di baris baru) menggunakan .readline()

with open("test.txt", 'w') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")

with open("test.txt", 'r') as teks:
    print(teks.readline(), end="")
Selamat pagi

Kalau kita lakukan berkali-kali, akan terbaca beberapa baris:

with open("test.txt", 'w') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")

with open("test.txt", 'r') as teks:
    print(teks.readline(), end="")
    print(teks.readline(), end="")
    print(teks.readline(), end="")
Selamat pagi
Selamat siang
Selamat sore

Bahkan, kita bisa menggunakan for loop untuk mengiterasi pada tiap baris:

with open("test.txt", 'w') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")

with open("test.txt", 'r') as teks:
    for baris in teks:
        print(baris, end="")
Selamat pagi
Selamat siang
Selamat sore
Selamat malam

Kita juga bisa memperoleh suatu list yang terdiri dari tiap baris, menggunakan .readlines():

with open("test.txt", 'w') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")
    teks.write("Selamat sore\n")
    teks.write("Selamat malam")

with open("test.txt", 'r') as teks:
    semua_baris = teks.readlines()

print(semua_baris)
['Selamat pagi\n', 'Selamat siang\n', 'Selamat sore\n', 'Selamat malam']

Bagaimana kalau kita coba mode a? Kita coba .tell() juga di awal mode a agar bisa melihat posisi cursor kita membuka file dalam mode a:

with open("test.txt", 'w') as teks:
    teks.write("Selamat pagi\n")
    teks.write("Selamat siang\n")

with open("test.txt", 'a') as teks:
    print(teks.tell())
    teks.write("Selamat sore\n")

with open("test.txt", 'r') as teks:
    semua_baris = teks.readlines()

print(semua_baris)
27
['Selamat pagi\n', 'Selamat siang\n', 'Selamat sore\n']

Ternyata, ketika membuka file dengan mode a, cursor langsung diposisikan di akhir file, sehingga kita bisa langsung menulis untuk menambahkan sesuatu di akhir file.

Terakhir, kita akan mencoba untuk meng-copy suatu text file, dengan cara membuat text file baru dan mengisi isinya dari text file yang lama. Caranya:

  1. Buka file yang lama dengan mode r, simpan semua isinya dalam suatu variabel, tutup
  2. Buka file yang baru dengan mode w, isi dengan variabel tersebut, tutup

Setelah itu, kita bisa membuka lagi file yang baru dengan mode r hanya untuk melihat isinya, memastikan sama :)

# Memperoleh isi file yang lama
with open("test.txt", 'r') as file_lama:
    isi_lama = file_lama.read()

print("Isi file yang lama: ")
print(isi_lama, end="")

# Menuliskan ke file yang baru
with open("testcopy.txt", 'w') as file_baru:
    file_baru.write(isi_lama)

# Memperoleh isi file yang baru, mau liat aja
with open("testcopy.txt", 'r') as file_baru:
    isi_baru = file_baru.read()

print("Isi file yang baru:")
print(isi_baru, end="")
Isi file yang lama: 
Selamat pagi
Selamat siang
Selamat sore
Isi file yang baru:
Selamat pagi
Selamat siang
Selamat sore

Berhasil ya!

CodeChef

Kita akan latihan beberapa soal pemrograman di CodeChef. Sebenarnya, yang ditekankan di sini adalah latihan cara menerima input dan memberikan output sesuai permintaan soal di CodeChef, agar kalian sudah paham nantinya ketika mencari soal di CodeChef untuk proyek akhir Struktur Data.

Pertama-tama, silakan buat akun terlebih dahulu di https://www.codechef.com/

Lalu, silakan coba menyelesaikan soal-soal berikut:

  1. https://www.codechef.com/problems/START01

  2. https://www.codechef.com/problems/FLOW001

  3. (tidak jadi dibahas) https://www.codechef.com/problems/GDTURN

N = input()
print(N)
T = int(input())
for i in range(T):
    baris = input().split(" ")
    A = int(baris[0])
    B = int(baris[1])
    hasil = A + B
    print(hasil)

(ditunda ke praktikum yang akan datang) Graphviz

Sebelum bisa menggunakan Graphviz, perlu di-install terlebih dahulu.

Di Google Colaboratory, kalian tinggal mengetik:

pip install graphviz

Sedangkan, apabila menggunakan Anaconda, buka Anaconda Prompt atau Command Prompt (cmd) lalu ketik:

conda install graphviz

Tunggu instalasi selesai, barulah buka Jupyter Notebook dan ketik

pip install graphviz

Kemudian, kita bisa import:

import graphviz as gv

(tunggu praktikum yang akan datang yaa)