Keluaran yang rapi mempermudah pembacaan tabel, log, dan laporan. C++ menyediakan dua generasi alat pemformatan, yaitu manipulator iostream yang dipakai bersama operator << sejak lama, dan std::format dari C++20 yang menata tampilan lewat format string. Topik ini mempraktikkan keduanya pada data yang sama, sehingga perbedaan gaya dan letak pengaturannya terlihat jelas.
Mengatur tampilan dengan manipulator#
Manipulator adalah nilai yang disisipkan ke stream untuk mengubah tampilan data berikutnya. Manipulator lebar dan presisi berasal dari header <iomanip>, sedangkan yang mengatur arah perataan seperti std::left diatur langsung pada stream. Aturan umumnya, std::setw hanya berlaku untuk satu data berikutnya, sedangkan std::fixed, std::setprecision, dan std::setfill menempel sampai diubah lagi.
#include <iomanip>
#include <iostream>
#include <string>
int main() {
const std::string waktu[] = {"07:00", "08:00", "09:00"};
const double suhu[] = {22.5, 23.1, 24.8};
std::cout << std::left << std::setw(8) << "waktu"
<< std::right << std::setw(8) << "suhu" << "\n";
for (int i = 0; i < 3; ++i) {
std::cout << std::left << std::setw(8) << waktu[i]
<< std::right << std::fixed << std::setprecision(1)
<< std::setw(8) << suhu[i] << "\n";
}
std::cout << std::setfill('*') << std::setw(16) << "" << "\n";
return 0;
}waktu suhu
07:00 22.5
08:00 23.1
09:00 24.8
****************Kolom waktu diratakan kiri dengan std::left, sedangkan kolom suhu diratakan kanan dengan std::right dan ditampilkan dengan satu angka di belakang koma melalui kombinasi std::fixed dan std::setprecision(1). Baris terakhir memakai std::setfill('*') sehingga lebar 16 karakter terisi karakter bintang. Kombinasi ini memadai untuk tabel kecil, tetapi pengaturan yang menempel perlu diingat karena berlaku untuk semua cetakan setelahnya.
Dua perilaku manipulator patut dicatat. Data yang lebih panjang daripada lebar std::setw tetap dicetak utuh, sehingga kolom bergeser apabila isinya melebihi lebar yang ditetapkan dan tidak pernah terpotong. Di sisi lain, std::setprecision tanpa std::fixed berarti jumlah angka signifikan, sehingga std::setprecision(3) menampilkan 23.4567 sebagai 23.5. Dengan std::fixed, nilai yang sama tampil sebagai 23.457 karena presisinya berpindah ke belakang koma.
Pemformatan modern dengan std::format#
std::format memindahkan seluruh pengaturan ke dalam format string, yaitu teks pola yang memuat pengganti {} beserta spesifikasinya. Pengganti diisi oleh argumen secara berurutan, dan spesifikasinya ditulis setelah tanda titik dua, misalnya {:<8} untuk rata kiri dalam lebar 8 karakter atau {:.1f} untuk satu angka di belakang koma. Hasilnya berupa std::string yang tinggal dicetak, sehingga keadaan stream tidak tersentuh sama sekali.
// tidak-dijalankan (butuh -std=c++23; perintah build ada di bawah)
#include <format>
#include <iostream>
#include <string>
int main() {
const std::string waktu[] = {"07:00", "08:00", "09:00"};
const double suhu[] = {22.5, 23.1, 24.8};
std::cout << std::format("{:<8}{:>8}\n", "waktu", "suhu");
for (int i = 0; i < 3; ++i) {
std::cout << std::format("{:<8}{:>8.1f}\n", waktu[i], suhu[i]);
}
std::cout << std::format("{:*^16}\n", "");
return 0;
}# kompilasi dengan standar C++23
g++ -std=c++23 main.cpp -o main
# jalankan program
./mainwaktu suhu
07:00 22.5
08:00 23.1
09:00 24.8
****************Format string {:*^16} menata teks di tengah lebar 16 karakter dengan karakter pengisi bintang, yang setara dengan kombinasi std::setfill dan std::setw pada versi manipulator. Keduanya menghasilkan tabel yang persis sama, tetapi std::format menyatukan lebar, perataan, dan presisi pada satu tempat yang mudah dibaca. Tanda { dan } yang dimaksud sebagai karakter biasa perlu ditulis ganda, yaitu {{ dan }}.
Tipe tampilan pada std::format#
Spesifikasi di dalam pengganti {} mengikuti urutan tetap, yaitu pengisi dan perataan, tanda, bentuk alternatif #, lebar, presisi, dan tipe tampilan (presentation type) di ujungnya. Tanda + memaksa tanda muncul pula pada bilangan positif, sedangkan # mengaktifkan bentuk alternatif seperti awalan 0x pada heksadesimal. Tipe tampilan menentukan notasi bilangannya, misalnya x untuk heksadesimal, b untuk biner, e untuk notasi ilmiah, dan g untuk pilihan umum yang memilih antara tetap dan ilmiah.
// tidak-dijalankan (butuh -std=c++23; perintah build ada di bawah)
#include <format>
#include <iostream>
int main() {
std::cout << std::format("tanda : {:+d}\n", 42);
std::cout << std::format("heksa : {:#x}\n", 255);
std::cout << std::format("biner : {:b}\n", 10);
std::cout << std::format("ilmiah : {:+.2e}\n", 12345.678);
std::cout << std::format("umum : {:g}\n", 0.000123);
return 0;
}# kompilasi dengan standar C++23
g++ -std=c++23 format-tipe.cpp -o format-tipe
# jalankan program
./format-tipetanda : +42
heksa : 0xff
biner : 1010
ilmiah : +1.23e+04
umum : 0.000123Notasi {:*^16} pada bagian sebelumnya dapat dibaca ulang dengan urutan di atas, yaitu pengisi bintang, perataan tengah, dan lebar 16 karakter. Presisi yang ditulis tanpa tipe tampilan pada bilangan pecahan berarti jumlah angka signifikan, sedangkan presisi pada tipe f berarti jumlah angka di belakang koma. Keunggulan lainnya bersifat pencegahan, karena format string diperiksa saat kompilasi terhadap jumlah dan tipe argumennya, sehingga ketidakcocokan terdeteksi sebelum program dijalankan.
Membandingkan manipulator dan std::format#
| Aspek | Manipulator iostream | std::format |
|---|---|---|
| Letak pengaturan | keadaan stream yang menempel | format string pada tiap panggilan |
| Lingkup berlaku | std::setw sekali, sisanya menempel | satu panggilan std::format |
| Keterbacaan pola | berpencah di antara operator << | terpusat dalam satu teks pola |
| Hasil | langsung ke stream | string yang dapat dipakai ulang |
| Standar | tersedia sejak C++ awal | C++20, membutuhkan compiler modern |
Keduanya sah digunakan, dan kode lama yang besar umumnya tetap memakai manipulator. Untuk kode baru, std::format mengurangi risiko lupa mengembalikan keadaan stream karena pengaturannya tidak menempel. Spesifikasi formatnya juga mencakup tipe tampilan untuk basis bilangan, karakter, dan boolean, sehingga kebutuhan tabel seperti contoh di atas tidak lagi memerlukan rantai manipulator yang panjang.
Kesalahan umum#
Kekeliruan berikut sering muncul saat pemformatan pertama kali diterapkan.
- Menganggap
std::setwmenempel. Lebar hanya berlaku pada satu data berikutnya, sehingga tabel dengan banyak kolom memerlukanstd::setwpada setiap sisipan. Perbaikannya menuliskannya ulang di setiap kolom atau berpindah kestd::format. - Lupa mengembalikan keadaan stream.
std::fixeddanstd::setprecisiontetap aktif untuk seluruh cetakan setelahnya, termasuk yang tidak membutuhkannya. Perbaikannya mengatur ulang presisi sebelum cetakan lain atau memindahkan pengaturannya kestd::format. - Menyusun kode
std::formattanpa standar yang memadai. Kompilasi dengan standar lama gagal karena header<format>belum tersedia. Perbaikannya menyertakan-std=c++20atau-std=c++23dan memakai GCC 13 ke atas. - Memakai kurung kurawal tunggal untuk karakter harfiah. Tanda
{dan}pada format string harus ditulis ganda, yaitu{{dan}}, apabila keduanya bukan pengganti argumen. - Menyusun format string yang tidak cocok dengan argumennya.
std::formatmemeriksa format string saat kompilasi, sehingga program gagal dikompilasi ketika jumlah pengganti tidak sesuai dengan jumlah argumennya. Perbaikannya menyamakan jumlah keduanya atau menyebut indeks secara eksplisit, misalnya{0}dan{1}.
Ringkasan#
- Manipulator seperti
std::setw,std::fixed,std::setprecision, danstd::setfillmengatur tampilan lewat keadaan stream, dan hanyastd::setwyang berlaku sekali. std::formatdari C++20 menata lebar, perataan, presisi, dan pengisi dalam satu format string, lalu mengembalikan hasilnya sebagai string.- Kompilasi program dengan
<format>memerlukan-std=c++20atau lebih baru serta GCC 13 ke atas. - Pengaturan yang menempel pada stream berlaku untuk semua cetakan setelahnya, sehingga perlu dikembalikan apabila tidak lagi diinginkan.
Referensi#
- Input/output manipulators — cppreference — Daftar manipulator standar beserta efeknya pada stream.
- std::format — cppreference — Dokumentasi pemformatan string modern dari C++20.
- Standard format specification — cppreference — Rincian lengkap sintaks spesifikasi format string.
- GCC 13 changes — GCC — Catatan perubahan yang mencantumkan dukungan std::format pada libstdc++.
- Output with ostream — LearnCpp — Pengenalan keluaran ostream dan pengaturan tampilannya.