Output bawaan std::cout sering kali belum siap dipakai: 3.14159265 tercetak enam digit, angka tidak sejajar di kolom tabel, dan nominal uang kehilangan dua angka di belakang koma. C++ menyediakan dua gaya untuk mengatasi hal ini. Gaya klasik bekerja lewat member function pada stream seperti width(), fill(), dan precision(), sedangkan gaya modern memakai std::format (C++20) dan std::print (C++23) dengan format string yang ringkas seperti "{:.2f}". Pada Chapter ini kita mencoba keduanya, membaca anatomi format spec, dan menetapkan kapan masing-masing jadi pilihan terbaik.
Lebar Kolom, Karakter Pengisi, dan Presisi #
Tiga member function berikut mengatur tampilan pada std::ostream mana pun. width(n) menjamin output minimal n karakter, fill(c) menentukan karakter pengisi ruang kosong, dan precision(n) membatasi jumlah digit untuk floating-point.
#include <iostream>
int main() {
std::cout << '[';
std::cout.width(8); // lebar kolom minimum 8 karakter
std::cout << 42 << "]\n"; // rata kanan (bawaan untuk angka)
std::cout << '[';
std::cout.width(8);
std::cout.fill('0'); // ruang kosong diisi karakter '0'
std::cout << 42 << "]\n";
std::cout << '[' << 42 << "]\n"; // width() otomatis kembali ke 0
double pi = 3.14159265;
std::cout << "Pi lengkap : " << pi << '\n';
std::cout.precision(3); // batasi menjadi 3 digit signifikan
std::cout << "Pi presisi : " << pi << '\n';
// Jebakan: fill('0') masih aktif, width() harus di-set ulang
std::cout << "Total : Rp";
std::cout.width(10);
std::cout << 12500 << '\n';
std::cout.fill(' '); // kembalikan pengisi ke spasi
std::cout << "Total : Rp";
std::cout.width(10);
std::cout << 12500 << '\n';
return 0;
}Output yang Dihasilkan:
[ 42]
[00000042]
[42]
Pi lengkap : 3.14159
Pi presisi : 3.14
Total : Rp0000012500
Total : Rp 12500Mari kita bedah perilaku yang tampak di output:
- Baris 5-7 (
std::cout.width(8)): angka42hanya dua karakter, sehingga enam ruang kosong ditambahkan di depannya — angka secara bawaan rata kanan — dan hasilnya[ 42]. - Baris 9-12 (
std::cout.fill('0')): lebar yang sama, tetapi ruang kosong kini diisi'0'sehingga menghasilkan[00000042]. Pola ini persis seperti penomoran di struk:000001bukan1. - Baris 14:
width()berlaku sekali saja dan otomatis kembali ke0setelah satu output, sehingga42berikutnya tercetak polos[42]. - Baris 16-19 (
std::cout.precision(3)): presisi dalam mode bawaan berarti jumlah digit signifikan.3.14159265yang bawaannya enam digit (3.14159) dipangkas menjadi3.14. - Baris 22-24: jebakan yang sering mengejutkan —
fill('0')tidak di-reset sepertiwidth(). Pengisi nol masih aktif, sehingga nominal tercetak sebagaiRp0000012500. - Baris 26-28: setelah
fill(' ')dikembalikan, baris kedua tampil wajar:Rp 12500.
std::format dan std::print: Cara Modern #
C++20 menghadirkan <format> dengan fungsi std::format, dan C++23 melengkapinya dengan std::print serta std::println dari header <print> — keduanya sudah tersedia di g++ 14.2 dengan flag -std=c++23. Gaya ini meniru keringkasan printf di bahasa C, tetapi type-safe: format string diperiksa compiler, dan jumlah argumen yang salah langsung ditolak pada saat kompilasi.
#include <format>
#include <print>
int main() {
double harga = 1234.5;
int stok = 7;
// std::print mencetak langsung ke stdout (C++23)
std::print("Harga : Rp{:.2f}\n", harga); // 2 angka di belakang koma
std::print("Stok : {:>10}\n", stok); // rata kanan, lebar 10
std::print("Stok : {:<10}|\n", stok); // rata kiri, lebar 10
std::print("Kode : {:05d}\n", stok); // isi nol, lebar 5
std::print("Ilmiah: {:.2e}\n", 299792458.0); // notasi ilmiah
// std::format mengembalikan string agar bisa dipakai ulang
std::string kolom = std::format("|{:^9}|{:^9}|", "Kopi", "Teh");
std::print("{}\n", kolom);
std::println("Jumlah item: {}", stok); // println menambah '\n'
return 0;
}Output yang Dihasilkan:
Harga : Rp1234.50
Stok : 7
Stok : 7 |
Kode : 00007
Ilmiah: 3.00e+08
| Kopi | Teh |
Jumlah item: 7Bedah barisnya:
- Baris 7 (
{:.2f}): typefmemaksa notasi fixed dengan 2 digit di belakang koma;1234.5tampil sebagai1234.50. - Baris 8-9 (
{:>10}dan{:<10}): simbol>meratakan kanan,<meratakan kiri, keduanya dalam lebar 10 — perhatikan tanda|di baris kiri untuk melihat sisa ruangnya. - Baris 10 (
{:05d}):0di depan lebar berarti pengisi nol; typedmenegaskan desimal, hasilnya00007. - Baris 11 (
{:.2e}): typeemenghasilkan notasi ilmiah dengan dua digit desimal;299792458.0menjadi3.00e+08. - Baris 14-15 (
std::format): fungsi ini tidak mencetak apa pun, melainkan mengembalikanstd::string— berguna saat hasil format perlu disimpan ke variabel, dikirim ke file, atau diubah lagi. Pengatur{:^9}meratakan tengah; sisa pembagian ganjil diberikan ke sisi kanan (Kopimendapat 2 spasi kiri, 3 kanan). - Baris 18 (
std::println): identik denganstd::print, tetapi otomatis menambahkan newline di akhir sehingga kita tidak menulis\nmanual.
Memahami Mini-Format-Spec #
Semua kekuatan gaya modern tersimpan dalam satu pola di dalam kurung kurawal: {[[fill]align][sign][#][0][width][.precision][type]}. Kita tidak perlu menghafal semuanya — tabel berikut memetakan bagian yang paling sering dipakai:
| Spesifikasi | Arti | Contoh hasil |
|---|---|---|
{:.2f} | fixed, 2 digit desimal | 1234.50 |
{:>10} | rata kanan, lebar 10 | 7 |
{:<10} | rata kiri, lebar 10 | 7 |
{:^9} | rata tengah, lebar 9 | Kopi |
{:05d} | lebar 5, pengisi nol | 00007 |
{:.2e} | notasi ilmiah, 2 digit | 3.00e+08 |
{:x} / {:b} | heksadesimal / biner | ff / 10101111 |
Kolom fill dan align ditulis berpasangan: untuk mengisi dengan . dan meratakan tengah, kita menulis {:^9.2f} bergaya {:*^9} — karakter pertama menjadi pengisi, karakter kedua arah rata. Detail lengkapnya terdokumentasi pada halaman format spec cppreference di bagian referensi.
Klasik atau Modern? #
Untuk kode baru, std::format dan std::print adalah pilihan utama: lebih ringkas, lebih aman, dan keinginan format terbaca sekali jelas di dalam string. Member function klasik tetap penting untuk dua alasan — kode lama masih memakainya di mana-mana, dan beberapa pengaturan stream (seperti flag yang dijelaskan pada Chapter berikutnya) hanya tersedia lewat jalur tersebut.
| Aspek | Gaya klasik (cout.width) | Gaya modern (std::print) |
|---|---|---|
| Usulan standar | Sejak C++ awal | C++20 / C++23 |
| Keterbacaan format | Tersebar di banyak pemanggilan | Terkumpul dalam satu string |
| Keamanan tipe | Diperiksa saat runtime | Diperiksa saat kompilasi |
| Hasil bisa disimpan | Tidak langsung | std::format mengembalikan string |
Gaya klasik sebenarnya masih menyimpan satu keunggulan yang belum kita sentuh: manipulator seperti std::setw dan std::fixed yang disisipkan langsung di rantai <<. Chapter berikutnya membahasnya satu per satu — dan membandingkan std::endl dengan karakter '\n' biasa.