Bir String İçinde Yaşayan Akış
cin ve cout'u zaten biliyorsunuz - klavyeye ve ekrana bağlı akışlar. Bir string akışı aynı fikirdir, yalnızca veri bunun yerine bellekteki bir std::string içinde yaşar. İçine << ile yazar ve >> ile değer çekersiniz; tam olarak konsol akışlarındaki gibi, ama hiçbir şey terminale dokunmaz.
Bu tek fikir, günlük iki sorunu çözer: metni türlenmiş değerlere ayrıştırmak ve karışık parçalardan biçimlendirilmiş bir dize oluşturmak. Her şey <sstream> başlığında yaşar ve üç çeşitte gelir:
istringstream- yalnızca okuma, girdi ayrıştırmak için.ostringstream- yalnızca yazma, çıktı oluşturmak için.stringstream- her iki yön.
ss.str(), o ana kadar yazılan her şeyi düz bir string olarak döndürür. << operatörleri, cout'un yaptığı int-ten metne biçimlendirmenin aynısını yaptı - siz yalnızca sonucu yazdırmak yerine yakaladınız.
Ayrıştırma: Türlenmiş Değerleri Çekme
Asıl güç >> ile ortaya çıkar. Bir akışa biraz metin verin; çıkarma işlemi her belirteci istediğiniz türe dönüştürür ve aradaki boşlukları atlar. Bu, bir satırı birkaç türlenmiş alana bölmenin temiz yoludur:
Her >> bir sonraki boşluğa kadar okur ve dönüştürür: "Ada"'yı bir string'e, "36"'yı bir int'e, "5.5"'i bir double'a. Buradaki istringstream'e dikkat edin - yalnızca girdi türü, yalnızca ayrıştırma yaptığınızı açıkça gösterir.
String'leri Sayılara Dönüştürme (Güvenli Şekilde)
Sık karşılaşılan bir iş, "42" gibi tek bir dizeyi bir int'e çevirmektir. Bir stringstream bunu yapar ve metnin gerçekten geçerli bir sayı olup olmadığını söyler - atoi'nin asla yapmadığı bir şey:
Burada ss >> value, 123'ü okur, a'da durur ve başarılı olur - bu yüzden önemliyse her zaman dizenin tamamını doğrulayın. Sağlam bir kontrol, sayıyı okuyup ardından anlamlı hiçbir şeyin kalmadığını doğrulamaktır. Düz tek değerli dönüşümler için std::stoi, std::stod ve benzerleri (tür dönüştürme ile birlikte ele alındı) daha kısadır; tek bir dize birkaç karışık değer taşıdığında stringstream'e başvurun.
Bir String'i Ayırıcıya Göre Bölme
Metni bölmek, Google'da en çok aranan string akışı görevidir. Bir akışı, yalnızca satır sonu değil, herhangi bir ayırıcı karaktere kadar okuyabilen std::getline ile birleştirin:
while (getline(...)) deseni, akış tükenene kadar döngüye girer; çünkü getline akışı döndürür ve geriye bir şey kalmadığında akış false olarak değerlendirilir. Üçüncü argümanı kaldırırsanız, bunun yerine >> ile boşlukla ayrılmış belirteçlere göre bölersiniz - verinizin kullandığı ayırıcıyı seçin.
Bellekte String Oluşturma
Diğer yönde, ostringstream, sprintf'in tampon taşması riski ya da kırılgan manuel birleştirme olmadan biçimlendirilmiş bir dize bir araya getirir. Sayılardan ve metinden derlenmiş günlük satırları, dosya adları veya mesajlar için mükemmeldir:
cout ile kullanacağınız tüm <iomanip> biçimlendirmesi - setw, setfill, setprecision, hex - bir string akışında aynı şekilde çalışır; böylece doğrudan bir string içine yakalanmış, dolgulu, sabit genişlikli veya onaltılık çıktı elde edersiniz.
Tuzak: Yeniden Kullanmadan Önce Sıfırlayın
Herkesi ısıran tuzak, bir akışı sıfırlamadan yeniden kullanmaktır. Bir akış verisinin sonuna ulaştığında, eof ve fail bayrakları takılı kalır ve sonraki her >> sessizce hiçbir şey yapmaz - hata yok, yalnızca eski değerler:
stringstream ss("10");
int a, b;
ss >> a; // a = 10, akış şimdi sonda -> eof bayrağı ayarlandı
ss.str("20"); // yeni metni yükle...
ss >> b; // SESSİZCE BAŞARISIZ OLUR - hata bayrakları hâlâ ayarlı, b değişmedi
Hem hata durumunu hem de içeriği temizlemelisiniz. Doğru sıra önce clear(), sonra str()'dir:
İlgili iki hata: ss.clear() bayrakları sıfırlar ama tamponu boşaltmaz (bunun için ss.str("") kullanın) ve << ile eklemeye devam ettiğiniz bir stringstream büyümeye devam eder - her yineleme için kirli bir tane yeniden kullanmak yerine döngünün içinde yeni bir tane oluşturun.
Sonraki: Diziler
String akışları, metni türlenmiş değerlere ve tekrar geriye dönüştürmenin temiz bir yolunu verdi - genellikle CSV'yi bölerken doldurduğunuz vector gibi bunların tüm bir listesini. Bu değerlerin sabit koleksiyonlarını saklamak ve indekslemek için dildeki en temel kapsayıcıya ihtiyacınız var. Sonra dizileri ele alacağız: nasıl bildirileceğini, nasıl indeksleneceğini, üzerlerinde nasıl döngü kurulacağını ve C++'ta bu kadar çok tanımsız davranışa yol açan sınır dışı erişimden nasıl kaçınılacağını.
Sıkça Sorulan Sorular
C++'ta stringstream nedir?
std::stringstream, klavye veya dosya yerine bir string ile desteklenen bir akıştır. Tıpkı cout ve cin gibi, içine << ile yazar ve >> ile okursunuz; bu da onu metni ayrıştırmak ve bellekte dize oluşturmak için standart araç yapar. <sstream> başlığında yer alır.
C++'ta stringstream ile bir string'i int'e nasıl dönüştürürsünüz?
Metni bir akışa koyup bir sayıya çıkarın: stringstream ss("42"); int n; ss >> n;. Dönüşümün gerçekten başarılı olduğunu doğrulamak için if (ss) (ya da if (ss >> n)) ile kontrol edin. Basit durumlarda std::stoi daha kısadır, ancak tek bir dize birden çok karışık değer barındırdığında stringstream parlar.
Bir stringstream üzerinde neden clear() çağırmam gerekiyor?
Bir akış verisinin sonuna ulaştığında, eof/fail bitleri takılı kalır ve sonraki her >> sessizce hiçbir şey yapmaz. Aynı stringstream'i yeni içerikle yeniden kullanıyorsanız, bu hata bayraklarını sıfırlamak için ss.clear(), ardından yeni veri yüklemek için ss.str(newText) çağırın; aksi halde okumalar sessizce başarısız olur.