====== Girdi ve Çıktı Akışları ====== 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 başlık dosyası kullanılır. ===== Akış Nedir? ===== 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ışlar ===== 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. * cin -- standart girdiye (tipik olarak klavye) bağlı bir istream nesnesi * cout -- standart çıktıya (tipik olarak monitöre) bağlı bir ostream nesnesi * cerr -- standart hataya (tipik olarak monitöre) bağlı, tamponlanmamış çıktı sağlayan bir ostream nesnesi * clog -- standart hataya (tipik olarak monitöre) bağlı, tamponlanmış çıktı sağlayan bir ostream nesnesi 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. ===== istream ile girdi ===== ''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 [[https://en.cppreference.com/w/cpp/io/basic_istream|basic_istream]] adresine bakabilirsiniz. ==== Çıkarma operatörü ==== 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 char buf[10]; std::cin >> std::setw(10) >> buf; Yukarıdaki örnekte ''setw()'' manipülatörü ile çıkarma operatörünün maksimum 9((Bir adette sonlandırıcı için ayrılmıştır.)) 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'; ===== ostream ve ios ile çıktı ===== ==== Ekleme Operatörü ==== 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 ==== 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. ==== Diğer Biçimlendiriciler ==== Genel olarak karşılaşabileceğiniz bayraklar, manipülatörler ve üye fonksiyonlar aşağıda listelenmiştir. * Bayraklar ''std::ios'' sınıfında barınırlar. * Manipülatörler ''std::'' isim alanında barınırlar. * Üye fonksiyonlar ise ''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ır * ''std::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. * Eğer bayrak belirtilmezse, az basamaklı sayılar için sabit, aksi takdirde bilimsel kullanılır. * ''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. [[tr:cs:cpp:common:basicio|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.