VerySource

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 763|回复: 6

C程序读写unicode文件

[复制链接]

1

主题

2

帖子

3.00

积分

新手上路

Rank: 1

积分
3.00
发表于 2020-1-15 18:20:01 | 显示全部楼层 |阅读模式
在一个读写unicode文件(road.txt为该UTF-16文件的一部分)的C程序开发中,我遇到问题。

road.txt

10        3938        CH        屯門        EN        TUEN MUN
10        3939        CH        離島        EN        ISLANDS
10        3942        CH        東區        EN        EASTERN
10        3955        CH        維多利亞港        EN        VICTORIA HARBOUR
10        3956        CH        坪洲        EN        PENG CHAU ISLAND
10        4023-1        CH        九龍灣        EN        KOWLOON BAY
10        14000        CH        和豐街        EN        WO FUNG


程序开发需求..

文件road.txt为UTF-16文件,要用C语言读取该文件,获取ID (例如3938)  语言(例如CH)  地名 (例如 屯門)。 存储在一个结构体中 : id 和语言  数据类型为ascii字符串 ;地名  数据类型为utf-16

我遇到的问题

1. 在成功打开文件之后(使用fopen), 我无法用fgetwc两个字节两个字节的读取stream里的utf-16字符,每次用fgetwc读取都还是读取一个字节。
2. 由于无法一次读取两个字节,我使用两次fgetc来读取一个字符,但是在读取有些字符时,( 比如 ”維多利亞港” 在hex编码为 AD 7D 1A 59 29 52 9E 4E 2F 6E ,但是当读取到1A 时,不知为何读取循环结束,似乎1A被读取为EOF。)

谢谢您给我指点迷津=-)  

回复

使用道具 举报

0

主题

78

帖子

29.00

积分

新手上路

Rank: 1

积分
29.00
发表于 2020-1-26 14:00:01 | 显示全部楼层
1A ......
回复

使用道具 举报

0

主题

78

帖子

29.00

积分

新手上路

Rank: 1

积分
29.00
发表于 2020-1-26 16:09:01 | 显示全部楼层
文件读取(避免读取半个字符):

/*-----------------------Own_lib函数库系列 mbsfgets.cc-------------------------
cy_mbsfgets() /mbsfgets.o /libcyfunc.a

描述: 当处理多字节文本文件(如中文)时, 标准fgets()函数只能按字节读入, 这样很容易
      读入半个字符而使读入的字符串不完整, 为此, 程序中需增加一些额外的处理代码,
      但这样又让代码看起来不够简洁.
      本函数试图封装这一过程, 使对东亚文本的读取操作直接一些, 代码也简洁一些. 函
      数使用了"mbstowcs()/wcstombs()"标准库函数, 所以在您的程序开头应setlocale().
      与"fgets()"标准库函数类似, 文件中行尾的换行符被与其它字符同样对待的读入.
      同样, 字符串的末尾附上了'\0'(空字符)结束符.
      用户调用函数时: 传递有足够大(MB_CUR_MAX * max_chars +1)内存空间的目的字符
      串地址(指针) |  最多字符(非字节)数 | 文件流指针 | 和"LC_CTYPE"区域设置字符
      串(如: "zh_CN.GBK", 默认值为NULL, 表示不改变系统设置. 另当传递了一个无效的
      区域设置时, 函数不改变当前的该设置, 但该"设置"已为真!).
      下一个函数为重载函数, 增加了返回实际读取字串字节数的引用变量和返回实际读取
      字串字符数的引用变量.
dest_str:
      保存读入字符串的内存空间的指针/或NULL值.
max_chars:
      需要从文件流stream中读入的最多字符个数.
stream:
      FILE结构的文件流指针.
[length]:
      返回实际读取字符串的字节数.
[chars]:
      返回实际读取字符串的字符数.
locale_ctype = NULL:
      读入字串所属的LC_CTYPE区域(语系/编码). 一般程序开头已经设定, 故此可不管它.
返回值:
      返回指向dest_str内存空间的字符串指针(末尾已加'\0'). 若有错误返回NULL.

注意! 1. 函数从stream读入字符串直到出现换行或到文件尾或已读了max_chars个字符为止.
      2. 当传递LC_CTYPE区域设置时, 参数dest_str应为NULL, 函数返回指向自由存储区
         的字符串指针(用完后应delete[]掉, 并赋值为NULL), 若参数dest_str不为NULL,
         函数移动文件流指针一行/或指定字符数(不足一行时), 返回NULL.
         当不传递LC_CTYPE设置, 并且设定参数dest_str为NULL时, 其行为与LC_CTYPE为真
         && dest_str为真(可传递空字符串"")时一样(只有文件流指针移动 && 返回NULL).
      3. 传入区域字符串后函数将临时改变系统"LC_CTYPE"的设置. 在退出时会恢复其原
         来的设置.

作者: 任逍 |2002.05.08.
版权: GNU General (Library) Public License (GPL/LGPL)

* 编辑器: vim-6.0 |操作系统: TurboLinux7.0简体中文版 *
------------------------------------------------------------------------------*/

#include <stdio.h>                // usr for fgets()
#include <stdlib.h>                // usr for MB_CUR_MAX --当前多字节环境下每字最大字节长.
                                                // usr for mbs* and others.
#include <locale.h>         // usr for setlocale().--函数提供改变环境"LC_CTYPE"的机会

#include "cyinclude/cyfget.h"


char *  cy_mbsfgets(char * dest_str, int max_chars, FILE * stream,
                    const char * locale_ctype = NULL)
{
        long fpos_before;
        if ( (fpos_before = ftell(stream)) == -1 )        // 获取读取前的文件流指针位置
                return NULL;

        int         num = max_chars * MB_CUR_MAX;
        bool        locale_check = false;
        char*        locale_original = setlocale(LC_CTYPE, NULL);        // LC_CTYPE原来的值
        if (locale_ctype)
        {
                if (! dest_str)
                {
                        setlocale(LC_CTYPE, locale_ctype);
                        num = max_chars * MB_CUR_MAX;
                        locale_check = true;
                }
                else
                        dest_str = NULL;
        }

        size_t         test_length = 0;        // 用于测试输入字符数是否超过需求
        char*        tmp_chars = new char[num + 1];        // 字串所需最大可能空间, 临时存放用
        wchar_t*        tmp_wchars = new wchar_t[max_chars + 1];

        if ( (! tmp_chars) || (! tmp_wchars) )
        {        // 其中可能已经有一个申请成功!
                if (locale_check)
                        setlocale(LC_CTYPE, locale_original);
                delete[] tmp_chars;
                tmp_chars = NULL;
                delete[] tmp_wchars;
                tmp_wchars = NULL;
                return NULL;
        }

        if (! fgets(tmp_chars, num + 1, stream))        // 接收最大限定数量的输入字符串
        {
                if (locale_check)
                        setlocale(LC_CTYPE, locale_original);
                delete[] tmp_chars;
                tmp_chars = NULL;
                delete[] tmp_wchars;
                tmp_wchars = NULL;
                return NULL;
        }

        test_length = mbstowcs(tmp_wchars, tmp_chars, max_chars + 1);
        if ( (test_length == (size_t)-1) || (test_length == max_chars + 1) )
                tmp_wchars[max_chars] = L'\0';        // 多出来的字符或非法字节被截断或覆盖

        size_t        chars_length;        // 实际读取字串字节长(用于设置文件流指针偏移量)
        char *        return_chars = NULL;
        if (locale_check)
        {
                chars_length = wcstombs(NULL, tmp_wchars, num + 1);
                return_chars = new char[chars_length + 1];
                if (! return_chars)
                {
                        setlocale(LC_CTYPE, locale_original);
                        delete[] tmp_chars;
                        tmp_chars = NULL;
                        delete[] tmp_wchars;
                        tmp_wchars = NULL;
                        return NULL;
                }
                wcstombs(return_chars, tmp_wchars, chars_length + 1);
        }
        else
                chars_length = wcstombs(dest_str, tmp_wchars, num + 1);
       
        if (locale_check)
                setlocale(LC_CTYPE, locale_original);

        delete[] tmp_chars;
        tmp_chars = NULL;
        delete[] tmp_wchars;
        tmp_wchars = NULL;
       
        if (fseek(stream, fpos_before + chars_length, SEEK_SET))
                return NULL;        // 重置文件流指针, 当有错误时, 返回NULL

        if (locale_check)
                return return_chars;
        else
                return dest_str;
}
//-----------------------------------------------------------------------------


// 上一函数的重载函数.

char *  cy_mbsfgets(char * dest_str, int max_chars, FILE * stream,
                    int & length, int & chars, const char * locale_ctype = NULL)
{
        long fpos_before;
        if ( (fpos_before = ftell(stream)) == -1 )        // 获取读取前的文件流指针位置
                return NULL;

        int         num = max_chars * MB_CUR_MAX;
        bool        locale_check = false;
        char*        locale_original = setlocale(LC_CTYPE, NULL);        // LC_CTYPE原来的值
        if (locale_ctype)
        {
                if (! dest_str)
                {
                        setlocale(LC_CTYPE, locale_ctype);
                        num = max_chars * MB_CUR_MAX;
                        locale_check = true;
                }
                else
                        dest_str = NULL;
        }

        size_t         test_length = 0;        // 用于测试输入字符数是否超过需求
        char*        tmp_chars = new char[num + 1];        // 字串所需最大可能空间, 临时存放用
        wchar_t*        tmp_wchars = new wchar_t[max_chars + 1];

        if ( (! tmp_chars) || (! tmp_wchars) )
        {        // 其中可能已经有一个申请成功!
                if (locale_check)
                        setlocale(LC_CTYPE, locale_original);
                delete[] tmp_chars;
                tmp_chars = NULL;
                delete[] tmp_wchars;
                tmp_wchars = NULL;
                return NULL;
        }

        if (! fgets(tmp_chars, num + 1, stream))        // 接收最大限定数量的输入字符串
        {
                if (locale_check)
                        setlocale(LC_CTYPE, locale_original);
                delete[] tmp_chars;
                tmp_chars = NULL;
                delete[] tmp_wchars;
                tmp_wchars = NULL;
                return NULL;
        }

        test_length = mbstowcs(tmp_wchars, tmp_chars, max_chars + 1);
        if ( (test_length == (size_t)-1) || (test_length == max_chars + 1) )
        {
                tmp_wchars[max_chars] = L'\0';        // 多出来的字符或非法字节被截断或覆盖
                chars = max_chars;        //!.引用传递返回实际字串字符数
        }
        else
                chars = test_length;        //!.引用传递返回实际字串字符数

        size_t        chars_length;        // 实际读取字串字节长, 用于设置文件流指针偏移量和返回值
        char *        return_chars = NULL;
        if (locale_check)
        {
                chars_length = wcstombs(NULL, tmp_wchars, num + 1);
                return_chars = new char[chars_length + 1];
                if (! return_chars)
                {
                        setlocale(LC_CTYPE, locale_original);
                        delete[] tmp_chars;
                        tmp_chars = NULL;
                        delete[] tmp_wchars;
                        tmp_wchars = NULL;
                        return NULL;
                }
                wcstombs(return_chars, tmp_wchars, chars_length + 1);
                length = chars_length;        //!.引用传递返回实际字串字节数
        }
        else
        {
                chars_length = wcstombs(dest_str, tmp_wchars, num + 1);
                length = chars_length;        //!.引用传递返回实际字串字节数
        }
       
        if (locale_check)
                setlocale(LC_CTYPE, locale_original);

        delete[] tmp_chars;
        tmp_chars = NULL;
        delete[] tmp_wchars;
        tmp_wchars = NULL;
       
        if (fseek(stream, fpos_before + chars_length, SEEK_SET))
                return NULL;        // 重置文件流指针, 当有错误时, 返回-1
       
        if (locale_check)
                return return_chars;
        else
                return dest_str;
}
回复

使用道具 举报

0

主题

78

帖子

29.00

积分

新手上路

Rank: 1

积分
29.00
发表于 2020-1-26 16:45:01 | 显示全部楼层
/*-------------------------Own_lib函数库系列 utf8to.cc------------------------
cy_utf8to() /utf8to.o /libcyfunc.a

描述: 通过iconv()函数将UTF-8编码字节序列转换为系统支持的任何编码字符串.
      下一函数为重载函数, 增加了用于返回实际转换字节数的两个参数引用.
out_buf:
      转换的目标字符串空间指针.
buf_len:
      保存目标字符串的内存空间的大小(包括末尾的'\0'字符空间), 按字节计算.
out_code:
      转换的目的字符串的编码.
in_str:
      被转换的源字符串指针(应为末尾有正常结束符'\0'的字符串).
[in_len]:
      用于返回已经被转换的源字符串(UTF-8编码)的字节数(不包括结尾符'\0').
conv_begin = false:
      对于有"状态改变"的编码系统, 在处理连续分段字符串时, 第一次请置其为true.
返回值:
      返回转换后的目标编码字符串的字节数. 若遇到错误返回-1, 若遇到非法字节序列
      或buf_len不足(in_str中还有非空字符待转换), 返回0.

注意! 1. 用iconv()函数转换不同编码字符串时, 与系统LC_CTYPE/locale无关.
      2. 当遇到非法字节序列或buf_len小于转换字符串需要的长度而返回0时, dest_str
         中保存有已经正常转换的字符串(在串的正确位置已有'\0'结束符).

作者: 任逍 |2002.05.26.
版权: GNU General (Library) Public License (GPL/LGPL)

* 编辑器: vim-6.0 |操作系统: TurboLinux7.0简体中文版 *
------------------------------------------------------------------------------*/

#include <string.h>          // use for strlen()
#include <iconv.h>           // use for iconv_open()/iconv()iconv_close()

#include "cyinclude/cyutf.h"


size_t  cy_utf8to(char * out_buf,
                  size_t buf_len,
                  const char * out_code,
                  const char * in_str,
                  bool conv_begin = false)
{
        static iconv_t        its_conv;
        if ( (its_conv = iconv_open(out_code, "UTF-8")) == (iconv_t)-1 )
                return (size_t)-1;
        if (!in_str)
                return (size_t)-1;
        if (conv_begin)
                iconv(its_conv, NULL, NULL, NULL, NULL);

        const char *        instr = in_str;
        size_t        inlen = strlen(in_str) + 1;
        char *        outstr = out_buf;
        size_t        outlen = buf_len - 1;
        size_t        ret_conv = 0;
        ret_conv = iconv(its_conv, &instr, &inlen, &outstr, &outlen);
                // 当outlen长度不足时, iconv()返回-1
        iconv_close(its_conv);

        if ( (ret_conv == (size_t)-1) || (inlen == 1) )
                outstr[0] = '\0';        // 字符串末尾补充结束符'\0'

        if (inlen == 1)        // 此句必须在下句前
                return (buf_len - 1 - outlen);
        if (ret_conv == (size_t)-1)
                return 0;

        return (buf_len - 2 - outlen);
}
//------------------------------------------------------------------------------


// 上一函数的重载函数

size_t  cy_utf8to(char * out_buf,
                  size_t buf_len,
                  const char * out_code,
                  const char * in_str,
                  size_t & in_len,        //!
                  bool conv_begin = false)
{
        static iconv_t        its_conv;
        if ( (its_conv = iconv_open(out_code, "UTF-8")) == (iconv_t)-1 )
                return (size_t)-1;
        if (!in_str)
                return (size_t)-1;
        if (conv_begin)
                iconv(its_conv, NULL, NULL, NULL, NULL);

        const char *        instr = in_str;
        size_t        inlen, save_len;
        inlen = save_len = strlen(in_str) + 1;
        char *        outstr = out_buf;
        size_t        outlen = buf_len - 1;
        size_t        ret_conv = 0;
        ret_conv = iconv(its_conv, &instr, &inlen, &outstr, &outlen);

        iconv_close(its_conv);

        if (inlen == 0)
                in_len = (save_len - 1) - inlen;
        else
                in_len = save_len - inlen;

        if ( (ret_conv == (size_t)-1) || (inlen == 1) )
                outstr[0] = '\0';        // 字符串末尾补充结束符'\0'

        if (inlen == 1)        // 此句必须在下句前
                return (buf_len - 1 - outlen);
        if (ret_conv == (size_t)-1)
                return 0;

        return (buf_len - 2 - outlen);
}

回复

使用道具 举报

1

主题

2

帖子

3.00

积分

新手上路

Rank: 1

积分
3.00
 楼主| 发表于 2020-2-9 18:00:02 | 显示全部楼层
1. 多谢!!

但是我需要自己封装一个用户函数。
先读出MULTI BYTE,然后再转成WIDE CHARACTER吗?但是用fgetwc我不能读出MULTI BYTE。


2.  0X 1A 的问题好像不是我一个人遇到。

http://community.csdn.net/Expert/topic/5214/5214784.xml?temp=.6545221
主  题:  Unicode文本文件读取?  
作  者:  guochun (yingc)      

axx1611(long long *&ago) ( ) 信誉:100    Blog  2006-12-8 12:08:58  
  今天开VC6看了一下~~ “不”字的ASCII编码中由于含有0x1a
  fgetc()就认为文件结束了,原因不详
  gcc没有这个问题(VC6 bug??)
  用fread应该不会有这个问题

axx1611(long long *&ago) ( ) 信誉:100    Blog  2006-12-8 12:38:21  
  说错了 是“会”字
  另外试了VC8 也有此问题
  个人认为这里还是用二进制处理
  PS. fgetwc也是读ANSI,只不过它自动把读入的ANSI转成Unicode返回,所以没用。



很奇怪,但是我用setmode为binary之后又可以读过去了。
回复

使用道具 举报

0

主题

3

帖子

4.00

积分

新手上路

Rank: 1

积分
4.00
发表于 2020-3-7 22:15:01 | 显示全部楼层
0x1A是ASCII字符文件的结束标记,你一个字节一个字节的读,遇到0x1A它当然以为文件结束了.
回复

使用道具 举报

0

主题

3

帖子

4.00

积分

新手上路

Rank: 1

积分
4.00
发表于 2020-3-11 16:00:01 | 显示全部楼层
int i=0;
    wchar_t buf[100];
   
    FILE* input=fopen("c:\\unicode.txt","rb");
    while(!feof(input))
    {
        buf[i++]=fgetwc(input);
    }   
    buf[i-1]=0;
    wcout.imbue(locale("chs"));
    wcout<<(buf+1)<<L"中国\n";
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|CopyRight © 2008-2023|verysource.com ( 京ICP备17048824号-1 )

快速回复 返回顶部 返回列表