簡體   English   中英

用setw閱讀:eof還是不eof?

[英]Reading with setw: to eof or not to eof?

請考慮以下簡單示例

#include <string>
#include <sstream>
#include <iomanip>

using namespace std;

int main() {
  string str = "string";
  istringstream is(str);
  is >> setw(6) >> str;
  return is.eof();
}

乍一看,由於顯式寬度由setw操縱器指定,我希望>>操作符在從輸入流成功提取所請求的字符數后完​​成讀取字符串。 我沒有看到它立即嘗試提取第七個字符的原因,這意味着我不希望流進入eof狀態。

當我在MSVC ++下運行這個例子時,它按照我的預期工作:讀取后流保持良好狀態。 但是,在GCC中,行為是不同的:流最終處於eof狀態。

語言標准,它為此版本的>>運算符提供以下完成條件列表

  • 存儲n個字符;
  • 文件結束發生在輸入序列上;
  • 對於下一個可用的輸入字符c,isspace(c,is.getloc())為true。

鑒於上述情況,我沒有看到>>運算符在上述代碼中將流驅動到eof狀態的任何原因。

但是,這就是GCC庫中>>運算符實現

...
__int_type __c = __in.rdbuf()->sgetc();

while (__extracted < __n
       && !_Traits::eq_int_type(__c, __eof)
       && !__ct.is(__ctype_base::space,
                   _Traits::to_char_type(__c)))
{
  if (__len == sizeof(__buf) / sizeof(_CharT))
  {
    __str.append(__buf, sizeof(__buf) / sizeof(_CharT));
    __len = 0;
  }
  __buf[__len++] = _Traits::to_char_type(__c);
  ++__extracted;
  __c = __in.rdbuf()->snextc();
}
__str.append(__buf, __len);

if (_Traits::eq_int_type(__c, __eof))
  __err |= __ios_base::eofbit;
__in.width(0);
...

如您所見,在每次成功迭代結束時,它會嘗試為下一次迭代准備下一個__c字符,即使下一次迭代可能永遠不會發生。 在循環之后,它分析該__c字符的最后一個值並相應地設置eofbit

所以,我的問題是:在上述情況下觸發eof流狀態,就像GCC那樣 - 從標准的角度來看它是合法的嗎? 我沒有在文檔中明確指出它。 MSVC和GCC的行為是否合規? 或者只是其中一個表現正常?

該特定operator>>的定義與eofbit的設置無關,因為它僅描述操作何時終止,而不描述觸發特定位的內容。

標准(草案)中eofbit的描述說:

eofbit - 表示輸入操作到達輸入序列的末尾;

我想在這里取決於你想要解釋“達到”的方式。 請注意,gcc實現正確無法設置failbit ,其定義為

failbit - 表示輸入操作無法讀取預期的字符,或者輸出操作無​​法生成所需的字符。

所以我認為eofbit並不一定意味着文件的結尾阻礙了任何新字符的提取,只是文件的結尾已經“到達”了。

我似乎無法找到更准確的“達到”描述,所以我想這將是實現定義。 如果此邏輯正確,則MSVC和gcc行為都是正確的。


編輯:特別是,當sgetc()返回eof時,似乎eofbit被設置。 這在istreambuf_iterator部分和basic_istream::sentry部分中都有描述。 所以現在問題是:什么時候流的當前位置允許前進?


最終編輯:事實證明,g ++可能具有正確的行為。

每個字符掃描都通過<locale> ,以允許解析不同的字符集,貨幣格式,時間描述和數字格式。 雖然似乎沒有關於operator>>如何為字符串工作的直通描述,但是有關於數字,時間和金錢的do_get函數應如何do_get非常具體的描述。 您可以從草案的第687頁找到它們。

所有這些都是從istreambuf_iterator讀取ctype (字符的“全局”版本,通過locales讀取)開始的(對於數字,您可以在草稿的第1018頁找到調用定義)。 然后處理ctype,最后迭代器被提前。

因此,一般來說,這需要內部迭代器始終指向最后一個讀取后的下一個字符; 如果不是這樣的話你理論上可以提取超出你想要的東西:

string str = "strin1";
istringstream is(str);
is >> setw(6) >> str;
int x;
is >> x;

如果當前字符is后提取str並不在eof ,那么標准就需要x得到值1,因為對於數字提取標准明確要求迭代器第一次讀取后前進。

由於這沒有多大意義,並且鑒於標准中描述的所有復雜提取都以相同的方式運行,因此對於字符串來說同樣會發生這種情況是有意義的。 因此,當指針為is讀取6個字符之后落在eof時, eofbit需要被設置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM