Bu neden oluyor?
Bunun, kendi sağladığınız girdiyle çok az ilgisi vardır, bunun yerine varsayılan davranış std::getline()
sergileriyle ilgilidir. Ad ( std::cin >> name
) için girişinizi sağladığınızda, yalnızca aşağıdaki karakterleri göndermediniz, aynı zamanda akışa örtük bir satırsonu da eklendi:
"John\n"
Bir terminalden seçim yaptığınızda Enterveya Returngönderirken girişinize her zaman yeni satır eklenir . Ayrıca, bir sonraki satıra geçmek için dosyalarda da kullanılır. Satırsonu, çıkarıldıktan sonra, name
atıldığı veya tüketildiği bir sonraki G / Ç işlemine kadar arabellekte bırakılır . Kontrol akışı ulaştığında std::getline()
, satırsonu atılacak, ancak girdi hemen kesilecektir. Bunun olmasının nedeni, bu işlevin varsayılan işlevselliğinin bunu yapması gerektiğini belirtmesidir (bir satırı okumaya çalışır ve bir satırsonu bulduğunda durur).
Bu öncü satırsonu, programınızın beklenen işlevselliğini engellediğinden, bir şekilde göz ardı edildiğimizde onun atlanması gerekir. Bir seçenek, std::cin.ignore()
ilk çıkarma işleminden sonra aramaktır. Bir sonraki mevcut karakteri atacak, böylece yeni satır artık yolda olmayacak.
std::getline(std::cin.ignore(), state)
Derinlemesine Açıklama:
Bu std::getline()
aradığınız aşırı yük :
template<class charT>
std::basic_istream<charT>& getline( std::basic_istream<charT>& input,
std::basic_string<charT>& str )
Bu işlevin başka bir aşırı yüklemesi, bir tür sınırlayıcı alır charT
. Sınırlayıcı karakter, girdi dizileri arasındaki sınırı temsil eden bir karakterdir. Bu özel aşırı yükleme, sınırlayıcıyı sağlanmadığı için input.widen('\n')
varsayılan olarak yeni satır karakterine ayarlar .
Şimdi, bunlar std::getline()
girdiyi sonlandıran koşullardan birkaçı :
- Akış,
std::basic_string<charT>
tutabileceği maksimum karakter miktarını çıkardıysa
- Dosya sonu (EOF) karakteri bulunursa
- Sınırlayıcı bulunmuşsa
Üçüncü koşul, uğraştığımız durumdur. İçine girdi state
DİR thusly temsil:
"John\nNew Hampshire"
^
|
next_pointer
next_pointer
ayrıştırılacak sonraki karakter nerede . Giriş sırasındaki bir sonraki konumda saklanan karakter sınırlayıcı olduğundan, std::getline()
bu karakteri sessizce atacak next_pointer
, bir sonraki kullanılabilir karaktere yükselecek ve girişi durduracaktır. Bu, sağladığınız karakterlerin geri kalanının bir sonraki G / Ç işlemi için hala arabellekte kaldığı anlamına gelir. Hattan içine başka bir okuma yaparsanız state
, ayırma std::getline()
işleminizin sınırlayıcıyı atmak için son çağrı olarak doğru sonucu vereceğini fark edeceksiniz .
Biçimlendirilmiş girdi operatörü ( operator>>()
) ile ayıklarken tipik olarak bu problemle karşılaşmadığınızı fark etmiş olabilirsiniz . Bunun nedeni, giriş akışlarının giriş için sınırlayıcılar olarak beyaz boşlukları kullanması ve std::skipws
1 manipülatörünün varsayılan olarak açık olmasıdır. Akışlar, biçimlendirilmiş girdi gerçekleştirmeye başladığında akıştan baştaki boşlukları çıkarır. 2
Biçimlendirilmiş giriş operatörler farklı olarak, std::getline()
bir bir biçimlendirilmemiş giriş işlev. Ve tüm biçimlendirilmemiş giriş işlevlerinin ortak bir şekilde aşağıdaki kodu vardır:
typename std::basic_istream<charT>::sentry ok(istream_object, true);
Yukarıdakiler, standart bir C ++ uygulamasında tüm biçimlendirilmiş / biçimlendirilmemiş G / Ç işlevlerinde somutlaştırılmış bir nöbetçi nesnesidir. Sentry nesneleri, akışı G / Ç için hazırlamak ve hata durumunda olup olmadığını belirlemek için kullanılır. Sadece biçimlendirilmemiş girdi işlevlerinde, sentry kurucusunun ikinci argümanının olduğunu göreceksiniz true
. Bu iddia aracı boşluk gelen olacak olup , giriş dizinin başlangıcından itibaren göz ardı edilmesi. Standarttan [§27.7.2.1.3 / 2] ilgili alıntı:
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
[...] noskipws
Sıfırsa ve sıfır değilse , is.flags() & ios_base::skipws
işlev, bir sonraki kullanılabilir giriş karakteri c
bir boşluk karakteri olduğu sürece her karakteri çıkarır ve atar . [...]
Yukarıdaki koşul yanlış olduğundan, nöbetçi nesne boşlukları atmayacaktır. Bunun nedeni noskipws
, true
bu işlev tarafından belirlenir , çünkü std::getline()
amacı, ham, formatlanmamış karakterleri bir std::basic_string<charT>
nesneye okumaktır .
Çözüm:
Bu davranışı durdurmanın bir yolu yok std::getline()
. Yapmanız gereken şey, yeni satırı std::getline()
çalıştırmadan önce kendiniz atmaktır (ancak bunu biçimlendirilmiş ayıklamadan sonra yapın ). Bu, ignore()
yeni bir satıra ulaşana kadar girdinin geri kalanını atmak için kullanılarak yapılabilir :
if (std::cin >> name &&
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') &&
std::getline(std::cin, state))
{ ... }
Sen dahil etmek gerekir <limits>
kullanımına std::numeric_limits
. std::basic_istream<...>::ignore()
bir sınırlayıcı bulana veya akışın sonuna ulaşana kadar belirtilen miktarda karakteri atan bir işlevdir ( ignore()
ayrıca bulursa sınırlayıcıyı da atar). max()
İşlevi, bir akım kabul karakter büyük miktarda döner.
Boşluğu atmanın başka bir yolu std::ws
, bir giriş akışının başlangıcından önde gelen beyaz boşlukları çıkarmak ve atmak için tasarlanmış bir manipülatör olan işlevi kullanmaktır :
if (std::cin >> name && std::getline(std::cin >> std::ws, state))
{ ... }
Fark ne?
Aradaki fark, ignore(std::streamsize count = 1, int_type delim = Traits::eof())
3'ün karakterleri atana count
, sınırlayıcıyı bulana (ikinci bağımsız değişken tarafından belirtilen delim
) veya akışın sonuna ulaşana kadar karakterleri ayrım gözetmeden atmasıdır .std::ws
yalnızca akışın başından itibaren boşluk karakterlerini atmak için kullanılır.
Biçimlendirilmiş girdiyi biçimlendirilmemiş girdiyle karıştırıyorsanız ve kalan beyaz boşlukları atmanız gerekiyorsa, kullanın std::ws
. Aksi takdirde, geçersiz girişi ne olduğuna bakılmaksızın silmeniz gerekirse, kullanın ignore()
. Örneğimizde, akış değişken "John"
için girdinizi tükettiğinden, yalnızca boşlukları temizlememiz gerekiyor name
. Geriye kalan tek şey satırsonu karakteriydi.
1: std::skipws
giriş akışına biçimlendirilmiş girdi gerçekleştirirken baştaki boşluğu atmasını söyleyen manipülatördür. Bu, std::noskipws
manipülatör ile kapatılabilir .
2: Giriş akışları, boşluk karakteri, yeni satır karakteri, form beslemesi, satır başı vb. Gibi belirli karakterleri varsayılan olarak boşluk olarak kabul eder.
3: Bu, imzasıdır std::basic_istream<...>::ignore()
. Akıştan tek bir karakteri atmak için sıfır bağımsız değişken, belirli sayıda karakteri atmak için bir bağımsız değişken veya count
karakterleri atmak için iki bağımsız değişken veya delim
hangisi önce gelirse ulaşana kadar bunu çağırabilirsiniz . Normalde kullanmak std::numeric_limits<std::streamsize>::max()
değeri olarak count
size sınırlayıcı önce kaç tane karakter bilmiyorsanız, ancak yine de onları atmak istiyorum.
std::cin >> name && std::cin >> std::skipws && std::getline(std::cin, state)
de beklendiği gibi çalışması gerektiğine inanıyorum . (Aşağıdaki cevaplara ek olarak).