1

我正在尝试使用带有 msvc11 的 Windows 上的 ICU4C 读取 UTF-8 编码的文件。我需要确定缓冲区的大小来构建一个 UnicodeString。由于 ICU4C API 中没有类似 fseek 的函数,我认为我可以使用底层 C 文件:

#include <unicode/ustdio.h>
#include <stdio.h>
/*...*/
UFILE *in = u_fopen("utfICUfseek.txt", "r", NULL, "UTF-8");
FILE* inFile = u_fgetfile(in);
fseek(inFile,  0, SEEK_END); /* Access violation here */
int size = ftell(inFile);
auto uChArr = new UChar[size];

这段代码有两个问题:

  1. 由于某种原因,它在 fseek() 行“抛出”访问冲突(test.exe 中 0x000007FC5451AB00 (ntdll.dll) 处的未处理异常:0xC0000005:访问冲突写入位置 0x0000000000000024。)
  2. ftell 函数返回的大小不是我想要的大小,因为 UTF-8 最多可以使用 4 个字节作为代码点(u8"tю" 字符串的长度为 3)。

所以问题是:

  1. 如果我知道输入文件是 UTF-8 编码的,如何确定 UnicodeString 的缓冲区大小?
  2. 是否有一种可移植的方式来使用 iostream/fstream 来读取和写入 ICU 的 UnicodeStrings?

编辑:这是基于第一个答案和 C++11 标准的可能解决方案(在 msvc11 和 gcc 4.8.1 上测试)。ISO IEC 14882 2011 中的一些内容:

  1. “C++ 内存模型中的基本存储单元是字节。一个字节至少足够大,可以包含基本执行字符集 (2.3) 的任何成员和 Unicode UTF-8编码形式的八位代码单元。 ..”
  2. “基本源字符集由 96 个字符组成……”,- 已经需要 7 位
  3. “基本执行字符集和基本执行宽字符集应分别包含基本源字符集的所有成员......”
  4. “声明为字符 (char) 的对象应足够大以存储实现的基本字符集的任何成员。”

因此,为了使其可移植到实现定义的 char 大小为 1 字节 = 8 位的平台(不知道这不是真的),我们可以使用未格式化的输入操作将 Unicode 字符读入字符:

std::ifstream is;
is.open("utfICUfSeek.txt");
is.seekg(0, is.end);
int strSize = is.tellg();
auto inputCStr = new char[strSize + 1];
inputCStr[strSize] = '\0'; //add null-character at the end
is.seekg(0, is.beg);
is.read(inputCStr, strSize);
is.seekg(0, is.beg);
UnicodeString uStr = UnicodeString::fromUTF8(inputCStr);
is.close();

困扰我的是我必须为字符创建一个额外的缓冲区,然后才将它们转换为所需的 UnicodeString。

4

2 回答 2

2

这是使用 ICU 的替代方法。

使用该标准std::fstream,您可以将文件的整个/部分读入标准std::string,然后使用可识别 unicode 的迭代器对其进行迭代。http://code.google.com/p/utf-iter/

std::string get_file_contents(const char *filename)
{
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    if (in)
    {
        std::string contents;
        in.seekg(0, std::ios::end);
        contents.reserve(in.tellg());
        in.seekg(0, std::ios::beg);
        contents.assign((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
        in.close();
        return(contents);
    }
    throw(errno);
}

然后在你的代码中

std::string myString = get_file_contents( "foobar" );
unicode::iterator< std::string, unicode::utf8 /* or utf16/32 */ > iter = myString.begin();

while ( iter != myString.end() )
{
    ...
    ++iter;
}
于 2013-07-08T03:37:38.430 回答
0

好吧,要么您想一次读取整个文件以进行某种后处理,在这种情况下,这icu::UnicodeString并不是最好的容器......

#include <iostream>
#include <fstream>
#include <sstream>

int main()
{
    std::ifstream in( "utfICUfSeek.txt" );
    std::stringstream buffer;
    buffer << in.rdbuf();
    in.close();
    // ...
    return 0;
}

...或者您真正想要的是icu::UnicodeString像读入任何其他字符串对象一样读入,但走了很长一段路...

#include <iostream>
#include <fstream>

#include <unicode/unistr.h>
#include <unicode/ustream.h>

int main()
{
    std::ifstream in( "utfICUfSeek.txt" );
    icu::UnicodeString uStr;
    in >> uStr;
    // ...
    in.close();
    return 0;
}

...或者我完全错过了你的问题到底是什么。;)

于 2014-08-29T16:50:19.587 回答