改用 vs2008 來做,vs2008 的 wifstream 可以使用 Unicode 的檔名,但 wifstream 仍不能讀取 Unicode 的文字內容,不過用 wistream 配合以下的 class 卻成功了。
經網友 ice_emissary(燃燒的大地) 提醒是 Unicode BOM 檔頭的問題,修改後果然成功。
另外 wifstream 應該只是用來讀取 ansi 的檔案,只不過會自動轉型成 wchar_t 來使用
以下參考資料的連結
http://www.cplusplus.com/reference/iostream/streambuf/
http://oopweb.com/CPP/Documents/CPPAnnotations/Volume/cplusplus20.html
http://www.diybl.com/course/3_program/c++/cppjs/200822/98427.html
http://www.dreamincode.net/code/snippet2499.htm
以下是 wifstreambuf.h 的內容,只是一個 .h 檔
/*------------------------------------------------------------------------
wifstreambuf.h 1.0.2
Copyright 楊志賢 CxxlMan, 2012
All Rights Reserved
- 可以用 Unicode 檔名讀取 Unicode 文字檔
- 只能處理 win32 平台用的 UTF-16 Unicode
- 供 wistream 使用
- 只能用於 win32 平台,因使用 _wopen() 函數
------------------------------------------------------------------------*/
#ifndef WIFSTREAMBUF_H_INCLUDED
#define WIFSTREAMBUF_H_INCLUDED
#include <istream>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
namespace CxxlMan
{
// 可以用 Unicode 檔名的 streambuf
class wifstreambuf:public std::wstreambuf
{
int fd; // 保存由 _wopen() 開啟的檔案 handle
unsigned m_bufsize;
wchar_t *m_buffer;
virtual int_type underflow()
{
int r = read(fd, m_buffer, m_bufsize * sizeof(*m_buffer));
if(r == 0)
return traits_type::eof();
wchar_t *Start;
if(*m_buffer == 0xfeff) // 若含有 BOM 標識
Start = m_buffer + 1;
else
Start = m_buffer;
wchar_t *End = m_buffer + r / sizeof(*m_buffer);
if(Start >= End)
return traits_type::eof();
setg(Start, Start, End);
return *gptr();
}
// seekg() 會叫用到,因只是做讀入用,所以不做 out 的處理
virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir,
std::ios_base::openmode which = std::ios_base::in)
{
pos_type pos = lseek(
fd, off * sizeof(*m_buffer),
(dir == std::ios::beg) ? SEEK_SET :
(dir == std::ios::cur) ? SEEK_CUR :
SEEK_END
);
if (pos < 0) return -1;
setg(m_buffer, m_buffer + 1, m_buffer + 1); // 這樣可以強迫重讀
return pos;
}
virtual std::streamsize xsgetn(char_type *dest, std::streamsize n)
{
int nread = 0;
while (n > 0)
{
int avail = in_avail();
if (avail == 0)
{
if(underflow() == traits_type::eof())
break;
avail = in_avail();
}
if(avail > n)
avail = n;
memcpy((char*)(dest + nread), (char*)gptr(), avail*sizeof(*dest));
gbump(avail);
nread += avail;
n -= avail;
}
return nread;
}
public:
// Constructor
wifstreambuf(const wchar_t *Filename)
{
fd = _wopen(Filename,_O_RDONLY | _O_BINARY);
m_bufsize = 512;
m_buffer = new wchar_t[512];
// 一開始設 buffer 是空的
setg(m_buffer, m_buffer + m_bufsize, m_buffer + m_bufsize);
}
// Destructor
virtual ~wifstreambuf()
{
if(fd != -1)
close(fd);
delete [] m_buffer;
}
bool isOpen()
{
return fd != -1;
}
};
} /* namespace CxxlMan */
#endif // WIFSTREAMBUF_H_INCLUDED
以下是測試碼
#include <sstream>
#include <iostream>
#include <fstream>
#include "wifstreambuf.h"
using namespace std;
using namespace CxxlMan;
int main()
{
// 指定系統現正使用中的地區語言為 wcout 做轉換
setlocale(LC_CTYPE, "");
// 可以用 Unicode 的檔名,內容只有 "123 中文" Unicode 文字
wifstreambuf wfin_buf(L"測試.txt");
if(wfin_buf.isOpen())
{
wistream wfin(&wfin_buf);
int i; wstring str;
wfin >> i;
wfin >> str;
wcout << i << " " << str << endl;
}
else
cout << "開檔失敗!" << endl;
return 0;
}
沒有留言:
張貼留言