Soyut, üst düzey bir bakış açısı sunmak istiyorum.
Eşzamanlılık ve eşzamanlılık
G / Ç işlemleri çevre ile etkileşime girer. Ortam programınızın bir parçası değildir ve kontrolünüz altında değildir. Ortam, programınızla gerçekten "eşzamanlı" olarak bulunur. Eşzamanlı her şeyde olduğu gibi, "mevcut durum" ile ilgili sorular anlamlı değildir: Eşzamanlı olaylar arasında "eşzamanlılık" kavramı yoktur. Devletin birçok özelliği eşzamanlı olarak mevcut değildir .
Bunu daha açık bir şekilde ifade edeyim: "Daha fazla veriye sahip misiniz?" Bunu eşzamanlı bir kapsayıcıdan veya G / Ç sisteminizden isteyebilirsiniz. Ancak cevap genellikle hareketsiz ve dolayısıyla anlamsızdır. Peki ya konteyner "evet" diyorsa - okumaya çalıştığınız zaman, artık veri olmayabilir. Benzer şekilde, cevap "hayır" ise, okumaya çalıştığınız zaman, veriler gelmiş olabilir. Sonuç şudur ki ,"Verilerim var" gibi bir özellik yoktur, çünkü olası bir yanıta yanıt olarak anlamlı bir şekilde hareket edemezsiniz. (Durum, tamponlu girdi ile biraz daha iyidir, burada bir çeşit garanti oluşturan "evet, verilerim var" olabilir, ancak yine de karşı dava ile başa çıkabilmeniz gerekir. kesinlikle açıkladığım kadar kötü: bu diskin mi yoksa ağ arabelleğinin dolu olup olmadığını asla bilemezsiniz.)
Biz imkansız olduğunu ve aslında un olduğu sonucuna Yani makul bunun olmadığını bir I / O sistemi sormaya, olacak bir I / O işlemi gerçekleştirmek mümkün. Onunla etkileşimde bulunabilmemizin tek yolu (aynı anda bir kapta olduğu gibi) işlemi denemek ve başarılı veya başarısız olup olmadığını kontrol etmektir. Çevre ile etkileşime girdiğiniz anda, o zaman ve ancak o zaman etkileşimin gerçekten mümkün olup olmadığını biliyorsunuz ve bu noktada etkileşimi gerçekleştirmeyi taahhüt etmelisiniz. (İsterseniz bu bir "senkronizasyon noktası" dır.)
EOF
Şimdi EOF'a geçiyoruz. EOF ise tepki bir olsun teşebbüs I / O işlemi. Bu, bir şey okumaya veya yazmaya çalıştığınız anlamına gelir, ancak bunu yaparken herhangi bir veri okuyamaz veya yazamazsınız ve bunun yerine giriş veya çıkışın sonu ile karşılaşılır. Bu, ister C standart kitaplığı, C ++ iostreams veya diğer kitaplıklar olsun, tüm I / O API'leri için geçerlidir. G / Ç işlemleri başarılı olduğu sürece , ilerideki işlemlerin başarılı olup olmayacağını bilemezsiniz . Her zaman önce işlemi denemeli ve sonra başarı veya başarısızlığa cevap vermelisiniz .
Örnekler
Örneklerin her birinde, önce G / Ç işlemini denediğimizi ve ardından geçerliyse sonucu kullandığımızı dikkatlice not edin . Ayrıca , her örnekte farklı şekiller ve formlar alsa da, her zaman G / Ç işleminin sonucunu kullanmamız gerektiğini unutmayın .
C stdio, bir dosyadan okunur:
for (;;) {
size_t n = fread(buf, 1, bufsize, infile);
consume(buf, n);
if (n < bufsize) { break; }
}
Kullanmamız gereken sonuç n
, okunan öğelerin sayısıdır (sıfır kadar az olabilir).
Cı, stdio scanf
:
for (int a, b, c; scanf("%d %d %d", &a, &b, &c) == 3; ) {
consume(a, b, c);
}
Kullanmamız gereken sonuç scanf
, dönüştürülen öğe sayısı olan dönüş değeridir .
C ++, iostreams formatlı çıkarma:
for (int n; std::cin >> n; ) {
consume(n);
}
Kullanmamız gereken sonuç std::cin
, boole bağlamında değerlendirilebilen ve akışın hala good()
devlette olup olmadığını bize söyleyen kendisidir .
C ++, iostreams getline:
for (std::string line; std::getline(std::cin, line); ) {
consume(line);
}
Kullanmamız gereken sonuç, daha std::cin
önce olduğu gibi tekrar .
write(2)
Bir arabelleği temizlemek için POSIX :
char const * p = buf;
ssize_t n = bufsize;
for (ssize_t k = bufsize; (k = write(fd, p, n)) > 0; p += k, n -= k) {}
if (n != 0) { /* error, failed to write complete buffer */ }
Burada kullandığımız sonuç k
, yazılan bayt sayısıdır. Buradaki nokta, sadece yazma işleminden sonra kaç bayt yazıldığını bilmemizdir .
POSIX getline()
char *buffer = NULL;
size_t bufsiz = 0;
ssize_t nbytes;
while ((nbytes = getline(&buffer, &bufsiz, fp)) != -1)
{
/* Use nbytes of data in buffer */
}
free(buffer);
Kullanmamız gereken sonuç nbytes
, satırsonuna kadar (veya dosya satırsonu ile bitmediyse EOF) dahil olmak üzere bayt sayısıdır.
-1
Bir hata oluştuğunda veya EOF'a ulaştığında işlevin açıkça döndüğünü (EOF! Değil) unutmayın.
"EOF" kelimesini nadiren dile getirdiğimizi fark edebilirsiniz. Hata durumunu genellikle bizim için daha ilginç olan başka bir şekilde tespit ederiz (örneğin, istediğimiz kadar G / Ç gerçekleştirememe). Her örnekte, bize açıkça EOF durumuna rastlandığını söyleyebilen bazı API özellikleri vardır, ancak bu aslında çok kullanışlı bir bilgi değildir. Sıklıkla önemsediğimizden çok daha ayrıntılı. Önemli olan, G / Ç'nin başarılı olup olmadığı, başarısız olduğu durumdan daha fazla.
EOF durumunu gerçekten sorgulayan son bir örnek: Bir dizeye sahip olduğunuzu ve bunun bir tamsayıyı temsil ettiğini test etmek istediğinizi varsayalım. C ++ iostreams kullanarak, şu şekilde gider:
std::string input = " 123 "; // example
std::istringstream iss(input);
int value;
if (iss >> value >> std::ws && iss.get() == EOF) {
consume(value);
} else {
// error, "input" is not parsable as an integer
}
Burada iki sonuç kullanıyoruz. Birincisi iss
, biçimlendirilmiş çıkarma işleminin value
başarılı olup olmadığını kontrol etmek için stream nesnesinin kendisidir . Ancak, beyaz alanı da tükettikten sonra, başka bir I / O / işlemi gerçekleştiririz iss.get()
ve EOF olarak başarısız olmasını bekleriz, bu da tüm dizenin biçimlendirilmiş ekstraksiyon tarafından zaten kullanılmış olması durumunda olur.
C standart kütüphanesinde strto*l
, uç işaretçisinin giriş dizesinin sonuna gelip gelmediğini kontrol ederek işlevlere benzer bir şey elde edebilirsiniz .
Cevap
while(!feof)
yanlıştır, çünkü alakasız olan bir şeyi test eder ve bilmeniz gereken bir şeyi test edemez. Sonuç olarak, aslında bu hiç gerçekleşmediğinde, başarıyla okunan verilere eriştiğini varsayan kodu yanlışlıkla yürütüyorsunuz.
feof()
Bir döngüyü kontrol etmek için kullanmak neden kötü