【问题标题】:fgetwc EOF loop test fails, but 65535 OKfgetwc EOF 循环测试失败,但 65535 OK
【发布时间】:2016-01-23 13:42:43
【问题描述】:

VS10 和 MCBS:

为此,我创建了一个名为 c:\eoftest 的文件,其中包含文本 "test"。下面代码中第5遍ch的值是fgetwc返回的65535,但不等于EOF,我们都知道在stdio.h中定义为(-1):

#include <stdio.h>
#include <windows.h>

int main()
{
    int ch;
    FILE *stream = NULL;
    wchar_t buf[5];
    memset (buf, '\0', sizeof(buf));
    stream = _wfopen(L"C:\\eoftest.txt", L"r");

            for (int i = 0; (i  < (sizeof(buf) - 1) && ((ch = fgetwc(stream)) != EOF) && (ch != '\0')); i++) //we are reading so last null condition mandatory
            {
            ungetwc(ch, stream);
            buf[i] = (wchar_t)(ch = fgetwc(stream));
            }
}

(ch = fgetwc(stream)) != 65535) 替换条件(原文如此)在这种情况下有效,但是没有做些什么来确保 EOF 测试能够成功?

【问题讨论】:

    标签: c++ c loops testing eof


    【解决方案1】:

    来自 fgetc, fgetwc 的 MSDN 文档

    fgetc 将读取的字符返回为 int 或返回 EOF 以指示错误或文件结束。 fgetwc 返回,作为 wint_t,对应读取的字符的宽字符或 返回 WEOF 表示错误或文件结束。

    WEOF 被定义为 0xFFFF,这是您之前替换的 65535

    #define WEOF ((wint_t)(0xFFFF))
    

    所以对于宽字符的EOF 测试应该改为

    if ((ch = fgetwc(stream)) != WEOF) ...
    

    编辑

    int main()
    {
        wchar_t buf[5];
        memset(buf, '\0', sizeof(buf));
        wcscpy(buf, L"1234");
    
        FILE *stream = _wfopen(L"C:\\eoftest.txt", L"rb");
        if (!stream)
        {
            stream = _wfopen(L"C:\\eoftest.txt", L"w+b");
            if (!stream)
            {
                printf("cannot create file\n");
                return 0;
            }
    
            fwrite((char*)buf, sizeof(buf[0]), wcslen(buf), stream);
            fseek(stream, 0, 0);
        }
    
        int len = sizeof(buf) / sizeof(buf[0]);
        for (int i = 0; i < len; i++) 
        {
            wchar_t ch = fgetwc(stream);
            if (ch == WEOF) break;
            buf[i] = ch;
        }
    
        wprintf(L"result = %s\n", buf);
    
        return 0;
    }
    

    编辑 2:这将逐行打印 unicode 文件的内容:

    int main()
    {
        FILE *stream = _wfopen(L"c:\\test\\test.txt", L"rb");
        if (!stream) return 0;
    
        int buflen = 256;
        wchar_t* buf = (wchar_t*)malloc(buflen * sizeof(wchar_t));
    
        if (fread(buf, 2, 1, stream))
        {
            if (buf[0] != 0xFEFF)
            {
                //BOM not detected, go back to start of file
                rewind(stream);
            }//else, skip the first 2 bytes
        }
    
        int i = 0, line = 0;
        wint_t ch = 0;
        while (ch != WEOF)
        {
            ch = fgetwc(stream);
            if (ch == L'\n' || ch == WEOF)
            {
                //null-terminate the buffer at i
                buf[i] = L'\0';
    
                //trim the '\r' at the end, if any
                if (i > 0 && buf[i - 1] == '\r') buf[i - 1] = L'\0';
    
                wprintf(L"%3d %s\n", ++line, buf);
    
                //start a new line for the next pass
                i = 0;
            }
            else
            {
                buf[i] = ch;
                i++;
                if (i == buflen)
                {
                    //increase buffer:
                    buflen += 256;
                    buf = (wchar_t*)realloc(buf, buflen * sizeof(wchar_t));
                }
            }
        }
    
        free(buf);
        return 0;
    }
    

    【讨论】:

    • 很高兴知道。谢谢。必须是 MSDN 库的“最近”添加,因为它仅在前 10 个页面中的 one 上搜索“MSDN”和“EOF”。
    • 顺便说一句,我刚刚注意到buf 被声明为宽字符,sizeof(buf) 是 10 个字节,而 buf 有 5 个元素。您可能想要sizeof(buf)/sizeof(buf[0]),即5 ---您也可能想要将unicode 文件作为二进制文件打开。见编辑。
    • t 会很有趣,找出对大文件更有效的方法,在 OP 中使用 ungewc 或在循环中每次定义一个 char 的建议替代方法后退一步。也更喜欢做二进制,但只看到有关在文本文件中使用标记或分隔符的示例(我使用了 fwprintf) - 尽管是另一个 SO 问题的材料。
    • 我明白你在做什么。但不要那样做。磁盘操作很慢,应尽量减少。使用 3 次磁盘操作读取一个字符会慢 3 倍。理想情况下,您一次应该读取多个字节。您必须将 unicode 文件作为二进制文件打开(您仍然可以使用 fwprintf)或者设置特殊的 unicode 标志和标记,但不要将 Unicode 文件作为文本文件 (ANSI) 打开
    • 但是默认的NTFS块大小不是4kb左右吗?根据这个ancient post,操作系统总是使用预读缓存。哎呀,这涉及到我知之甚少的东西,但同意一个字符在物理磁盘上的三个读取不是要走的路。单次读取的推荐大小会大于 4kb 吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多