Girdi ve çıktı işlevselliği C++ dilinin bir parçası olarak tanımlanmaz, bunun yerine C++ standart kütüphanesi aracılığıyla sağlanır.
Birçok C++ kitabı bu işlevselliği temel dilin parçası olmadığından anlatmaz. Ancak bu işlevselliğin C++'ın bir parçası olmaması, C++'ın bu işlevselliği desteklemediği anlamına gelmez. C++'ın bu işlevselliği desteklemesi için C++ standart kütüphanesinin bir parçası olan <iostream> başlık dosyası kullanılır.
En basit haliyle, C++'da girdi ve çıktı, akışlarla gerçekleştirilir. Bir akış sırayla erişilebilen bir bayt dizisidir. Zaman içinde, bir akış potansiyel olarak sınırsız miktarda veri üretebilir veya tüketebilir.
Genellikle 2 tür akış ile ilgileniriz.
Girdi akışları klavye, dosya veya ağ gibi bir veri üreticisinden gelen girdileri tutmak için kullanılır. Örnek olarak kullanıcı, program herhangi bir girdi beklemezken bir tuşa basabilir. Kullanıcının bu eylemini tamamen görmezden gelmek yerine C++ bu girdileri bir akış içerisinde tutar, veriler bir giriş akışına konur ve burada program hazır olana kadar bekler.
Çıktı akışları monitör, dosya veya yazıcı gibi belirli bir veri tüketicisinin çıktısını tutmak için kullanılır. Bir çıktı aygıtına veri yazarken, aygıt henüz bu veriyi kabul etmeye hazır olmayabilir. Örnek olarak, program çıktı akışına veri yazdığında yazıcı hala ısınıyor olabilir. Veriler yazıcı tarafından tüketilmeye başlayana kadar çıktı akışında kalacaktır.
Elbette bazı araçlar, girdi ve çıktı kaynağı olabilirler. Örnek olarka dosyalar ve ağlar.
Programcının öğrenmesi gereken sadece bu akışları nasıl kontrol edeceğidir. Arkaplanda akışların cihazlarla ve dosya, ağ gibi kaynaklarla nasıl etkileşime girdiğini bilmesine gerek yoktur. Bu kısım ortama ve işletim sistemine bağlıdır.
istream sınıfı, giriş akışlarıyla uğraşırken kullanılan birincil sınıftır. Giriş akışlarında, değerleri akıştan çıkarmak için çıkarma operatörü (»
) kullanılır.
ostream sınıfı, çıktı akışlarıyla uğraşırken kullanılan birincil sınıftır. Çıktı akışlarında, değerleri akışa yerleştirmek için ekleme operatörü («
) kullanılır.
iostream sınıfı hem girdi hem de çıktı işleyebilir ve çift yönlü girdi/çıktıya izin verir. Hem istream hem de ostream, ios adlı sınıftan türetilmiştir
Standart akış, bir bilgisayar programına çevresi tarafından sağlanan önceden bağlanmış bir akıştır. C++, kullanımınız için önceden tanımlanmış dört standart akış nesnesi ile birlikte gelir.
Tamponlanmamış çıktı genellikle hemen işlenirken, tamponlanmış çıktı genellikle bir blok olarak saklanır ve yazılır. Clog çok sık kullanılmadığından, genellikle standart akışlar listesinden çıkarılır.
iostream
kütüphanesi çok geniş ve çok çeşitli girdi işlevselliği sağlar. Burada sadece giriş için gerekli bilgilere yer verilmiştir. Çok fazla alt istream fonksiyonu bulunmaktadır. Detaylar için basic_istream adresine bakabilirsiniz.
Bir giriş akışından bilgi okumak için çıkarma operatörünü (») kullanabiliriz.
C++ tüm temel veri tipleri için öntanımlı çıkarma operatörüne sahiptir. Ayrıca kendi sınıfsal tiplerimiz için bu operatörü nasıl aşırı yükleyebileceğimizi görmüştük.
Çıkarma operatörü kullanılırken dikkat edilmesi gereken. Girdinin bizim tampon nesnemizi aşması durumudur.
char buf[10]; std::cin >> buf;
Örnek olarak yukarıdaki kodda, eğer kullanıcı 10'dan fazla karakter girerse ne olur? Buffer overflow adı verilen bir durum ortaya çıkar ve tanımsız davranışa sebep olabilir.
Bu gibi durumların önüne geçmek için manipülatör kullanabiliriz.
Manipülatör, çıkarma (») veya ekleme («) operatörleri ile uygulandığında bir akışı değiştirmek için kullanılan bir nesnedir.
#include <iomanip> char buf[10]; std::cin >> std::setw(10) >> buf;
Yukarıdaki örnekte setw()
manipülatörü ile çıkarma operatörünün maksimum 91) karakter almasını sağladık. Diğer kalan girdiler bir sonraki çıkarma işlemine kadar akışta kalacaktır.
Bir diğer bilinmesi gereken ise, çıkarma operatörünün boşlukları göz ardı etmesidir. Bunun önüne geçmek için gene istream sınıfında bulunan get()
fonksiyonunu kullanabiliriz.
char ch; while (std::cin.get(ch)) std::cout << ch;
Aynı şekilde get()
fonksiyonunun stringler içinde alternatifi bulunmaktadır. Aşağıdaki örnek, kullanıcıdan 10 karakter uzunluğunda bir string alır. Aynı şekilde kalanlar akışta kalır.
char strBuf[11]; std::cin.get(strBuf, 11); std::cout << strBuf << '\n';
Bu verilen iki get()
örneğinde bilinmesi gereken önemli bir nokta var. Bu fonksiyon \n
karakterini okumaz. Bu karaktere denk geldiğinde okuma işlemi bitti sayar. Bu durum için getline()
fonksiyonunu kullanabiliriz.
getline()
fonksiyonuda aynı şekilde çalışır ek olarak \n
karakterini de okur.
char strBuf[11]; // 10 karaktere kadar okuma std::cin.getline(strBuf, 11); std::cout << strBuf << '\n'; // 10 karaktere kadar daha okuyun std::cin.getline(strBuf, 11); std::cout << strBuf << '\n';
Ekleme operatörü («), bir çıktı akışına bilgi koymak için kullanılır. C++, tüm yerleşik veri türleri için önceden tanımlanmış ekleme işlemlerine sahiptir
Biçimlendirme değiştirmenin iki yolu vardır bayraklar ve manipülatörler. Bayrakları açılıp kapatılabilen boolean değişkenler olarak düşünebilirsiniz. Manipülatörler, bir akışa yerleştirilen ve nesnelerin giriş ve çıkış şeklini etkileyen nesnelerdir.
Bayrakları değiştirmek için setf()
ve unsetf()
fonksiyonlarını kullanabiliriz. Örnek olarak, matematikte olduğu gibi c++'da da pozitif sayıların önünde bulunan + işaretini varsayılan olarak yazmayız.
std::ios::showpos
bayrağı ile yazdırmayı sağlayabiliriz.
std::cout.setf(std::ios::showpos); // std::ios::showpos bayrağını açtık. std::cout << 27 << '\n';
Birden fazla bayrağı aktifleştirmek için |
oparetörünü kullanabiliriz.
std::cout.setf(std::ios::showpos | std::ios::showpoint); // std::ios::showpos ve std::ios::showpoint bayrağını açtık. std::cout << 27 << '\n';
Aynı şekilde bir bayrağı kapatmak içinde unsetf()
fonksiyonunu kullanabiliriz.
std::cout.setf(std::ios::showpos); // std::ios::showpos bayrağını açın std::cout << 27 << '\n'; std::cout.unsetf(std::ios::showpos); // std::ios::showpos bayrağını kapatın std::cout << 28 << '\n';
Bayraklar hakkında bilmemiz gereken bir diğer nokta bazı bayraklar belirli gruplara aitlerdir. Bu gruplara format grubu denir ve format grupları benzer işleve sahip bayrakları bir arada tutar. setf()
ve unsetf()
fonksiyonları bu gruplar halinde bulunan bayrakları işlemede beklendiği kadar akıllı değillerdir.
Örnek olarak basefield adındaki format grubu oct
, dec
ve hex
bayraklarına sahiptir ki bu bayraklar tamsayısal çıktının hangi tabanda işleneceğini belirler. Varsayılan olarak dec
bayrağı açıktır.
Biz eğer hex
bayrağını açarak hexadecimal olarak yazdırmak istersek. Ayrı olarak dec
bayrağını kapatmamız gerekir. Çünkü setf()
fonksiyonu biz hex bayrağını açtığımızda akıllılık ederek dec
bayrağını kapatmaz. Sonuç olarak dec
bayrağı, hex
bayrağına göre işlem önceliğine sahip olduğundan kodumuz beklenen şekilde çalışmayacaktır.
std::cout.setf(std::ios::hex); // hex çıkışını açmayı denedik. std::cout << 27 << '\n';
Kodu bize;
27
Sonucunu verecektir. Çünkü dec
bayrağı açık olduğundan 27 sayısını ondalık olarak işleyecektir. Asıl yapmamız gereken ise;
std::cout.unsetf(std::ios::dec); // dec çıkışını kapattık. std::cout.setf(std::ios::hex); // hex çıkışını açmayı denedik. std::cout << 27 << '\n';
Bir diğer kolaylık ise setf()
fonksiyonunun ikinci parametresidir. İkinci parametreye grubun adını yazarak açtığımız bayrak dışında o gruptaki diğer bayrakları kapatabiliriz.
std::cout.setf(std::ios::hex, std::ios::basefield); std::cout << 27 << '\n';
Bu şekilde çıktıyı biçimlendirme pek yaygın değildir. C++ bize bize ikinci bir biçimlendirme yöntemi sunar. Bu yöntem manipülatörlerdir. Manipülatörlerin iyi yanı ise ayarlayacağımız bayrağa göre uygun diğer bayrakları kapatmayı akıl edebilmesidir.
std::cout << std::hex << 27 << '\n'; // 27'yi onaltılık olarak yazdır std::cout << 28 << '\n'; // Hala hex'teyiz. std::cout << std::dec << 29 << '\n'; // ondalık sisteme geri döndük
Yukarıdaki kod bize ;
1b 1c 29
Sonucunu verecektir.
Peki neden manipülatörler kullanacaksak, bayrakları açıp kapatmayı öğrendik derseniz. Birçok seçenek genellikle manipülatörle yada bayraklarla ayarlanabilir. Ancak bazı ayarlar sadece manipülatörle yada sadece bayrakla ayarlanır. Bu yüzden ikisinide bilmek gerekir.
Genel olarak karşılaşabileceğiniz bayraklar, manipülatörler ve üye fonksiyonlar aşağıda listelenmiştir.
std::ios
sınıfında barınırlar.std::
isim alanında barınırlar.std::ostream
sınıfında barınırlar.std::ios::boolalpha
bayrağı açıksa bool değerler true
veya false
olarak yazdırılır. Kapalıysa, bool değerler 1
veya 0
olarak yazdırılır. Varsayılan olarak kapalıdır.std::boolalpha
manipülatörü bool değerleri “true” veya “false” yazdırırstd::noboolalpha
manipülatörü bool değerleri 0 veya 1 yazdırır (varsayılan)std::cout << true << ' ' << false << '\n'; std::cout.setf(std::ios::boolalpha); std::cout << true << ' ' << false << '\n'; std::cout << std::noboolalpha << true << ' ' << false << '\n'; std::cout << std::boolalpha << true << ' ' << false << '\n';
std::ios::showpos
bayrağı açıksa pozitif sayıların önüne + koyar. Kapalıysa koymaz. Varsayılan olarak kapalıdır.std::showpos
manipülatörü pozitif sayıların önüne + koyar.std::noshowpos
manipülatörü pozitif sayıların önüne + koymaz. (varsayılan)std::cout << 5 << '\n'; std::cout.setf(std::ios::showpos); std::cout << 5 << '\n'; std::cout << std::noshowpos << 5 << '\n'; std::cout << std::showpos << 5 << '\n';
std::ios::uppercase
bayrağı açıksa büyük harfler kullanılır. Varsayılan olarak kapalıdır.std::uppercase
manipülatörü ile büyük harfler kullanılır.std::nouppercase
manipülatörü ile küçük harfler kullanılır. (varsayılan)std::cout << 123456.8 << '\n'; std::cout.setf(std::ios::uppercase); std::cout << 123456.8 << '\n'; std::cout << std::nouppercase << 123456.8 << '\n'; std::cout << std::uppercase << 123456.8 << '\n';
std::ios::basefield
format grubu;std::ios::dec
bayrağı açıksa, değerleri ondalık sistemde yazdırır. (varsayılan)std::ios::hex
bayrağı açıksa, değerleri onaltılık olarak yazdırır.std::ios::oct
bayrağı açıksa, değerleri sekizlik olarak yazdırır.std::dec
manipülatörü değerleri ondalık sistemde yazdırır. (varsayılan)std::hex
manipülatörü değerleri onaltılık olarak yazdırır.std::oct
manipülatörü değerleri sekizlik olarak yazdırır.std::cout << 27 << '\n'; std::cout.setf(std::ios::dec, std::ios::basefield); std::cout << 27 << '\n'; std::cout.setf(std::ios::oct, std::ios::basefield); std::cout << 27 << '\n'; std::cout.setf(std::ios::hex, std::ios::basefield); std::cout << 27 << '\n'; std::cout << std::dec << 27 << '\n'; std::cout << std::oct << 27 << '\n'; std::cout << std::hex << 27 << '\n';
std::ios::floatfield
format grubu;std::ios::fixed
bayrağı açıksa, kesirli sayılar için ondalık gösterimi kullanır. (varsayılan)std::ios::scientific
bayrağı açıksa, kesirli sayılar için bilimsel gösterimi kullanır.std::ios::showpoint
bayrağı açıksa, kesirli değerler için her zaman bir ondalık nokta ve sondaki 0'ları gösterir.std::fixed
manipülatörü ondalık sistemi kullanır. (varsayılan)std::scientific
manipülatörü bilimsel gösterim kullanır.std::showpoint
manipülatörü kesirli değerler için ondalık noktayı ve sondaki 0'ları gösterir.std::noshowpoint
manipülatörü kesirli değerler için ondalık noktayı ve sondaki 0'ları göstermez.std::setprecision(int)
manipülatörü kesirli sayıların hassasiyetini ayarlar. (iomanip başlığında bulunur).std::ios_base::precision()
üye fonksiyonu, kesirli sayıların geçerli hassasiyetini döndürür.std::ios_base::precision(int)
üye fonksiyonu, kesirli sayıların hassasiyetini ayarlar ve eski hassasiyeti döndürür.Sabit veya bilimsel gösterim kullanılıyorsa, kesirde kaç ondalık basamağın görüntüleneceğini hassasiyet belirler. Hassasiyet, anlamlı basamak sayısından azsa sayının yuvarlanacağını unutmayın.
std::cout << std::fixed << '\n'; std::cout << std::setprecision(3) << 123.456 << '\n'; std::cout << std::setprecision(4) << 123.456 << '\n'; std::cout << std::setprecision(5) << 123.456 << '\n'; std::cout << std::setprecision(6) << 123.456 << '\n'; std::cout << std::setprecision(7) << 123.456 << '\n'; std::cout << std::scientific << '\n'; std::cout << std::setprecision(3) << 123.456 << '\n'; std::cout << std::setprecision(4) << 123.456 << '\n'; std::cout << std::setprecision(5) << 123.456 << '\n'; std::cout << std::setprecision(6) << 123.456 << '\n'; std::cout << std::setprecision(7) << 123.456 << '\n';
Kodu bize;
123.456 123.4560 123.45600 123.456000 123.4560000 1.235e+002 1.2346e+002 1.23456e+002 1.234560e+002 1.2345600e+002
Sonucunu verecektir.
Ne sabit ne de bilimsel bir yöntem kullanılmıyorsa, kaç anlamlı basamağın görüntüleneceğini hassasiyet belirler. Yine hassasiyet anlamlı basamak sayısından az ise sayı yuvarlanacaktır.
std::cout << std::setprecision(3) << 123.456 << '\n'; std::cout << std::setprecision(4) << 123.456 << '\n'; std::cout << std::setprecision(5) << 123.456 << '\n'; std::cout << std::setprecision(6) << 123.456 << '\n'; std::cout << std::setprecision(7) << 123.456 << '\n';
Kodu bize;
123 123.5 123.46 123.456 123.456
Sonucunu verecektir.
showpoint
manipülatörünü veya bayrağını kullanarak akışın bir ondalık nokta ve sondaki sıfırları yazmasını sağlayabilirsiniz.
std::cout << std::showpoint << '\n'; std::cout << std::setprecision(3) << 123.456 << '\n'; std::cout << std::setprecision(4) << 123.456 << '\n'; std::cout << std::setprecision(5) << 123.456 << '\n'; std::cout << std::setprecision(6) << 123.456 << '\n'; std::cout << std::setprecision(7) << 123.456 << '\n';
Kodu bize;
123. 123.5 123.46 123.456 123.4560
Sonucunu verecektir.
UCH Viki'den alınmıştır. https://wiki.ulascemh.com/doku.php?id=tr:cs:cpp:common:basicio Çıkarma operatörü başlığında ikinci satırda aşırı yükleme kısmına link verilecek.