【Q&A】getline读取行的行尾处理

来源:互联网 发布:韩版女装淘宝网 编辑:程序博客网 时间:2024/05/20 20:02

windows和linux对文本文件的行尾有不同的约定。在windows系统中,行尾包含了两个字符,回车(carriage return, '\t')和换行(line feed, '\n')。这两个字符来自于从前的电传打字机,分别表示将写位置重新定位在首端,并跳转到下一行。在linux和unix、以及mac系统中,只保留了换行符,而没有回车符。这也是一些mac上的文本文件到windows系统上就无法正确换行的原因。


c++的getline函数作用是读取输入流中的字符,当遇到_Delim(The line delimiter)字符的时候,结束读取。通常_Delim默认为'\n'。看一下getline的源代码:

template<class _Elem,
class _Traits,
class _Alloc> inline
basic_istream<_Elem, _Traits>& __CLRCALL_OR_CDECLgetline(
basic_istream<_Elem, _Traits>& _Istr,
basic_string<_Elem, _Traits, _Alloc>& _Str)
{ // get characters into string, discard newline
return (getline(_Istr, _Str, _Istr.widen('\n')));
}

的确如此。那么在windows系统中,一行是以"\t\n"结尾的,前面的'\t'呢?'\n'前面的'\t'被读取到了字符串中。尝试了如下代码:

while (getline (in, sLine))     // 文件中的这一行只写了一个数字1(半角字符),即内容是 “1”
{

int iLength = sLine.lenght();

cout << iLength << endl;

打印出来的结果是“2”,表示除了文本内容‘1’之外,还有一个字符。debug看内容,字符的数值是13,是回车的ascii编码值。印证了前面的说法。


在文本处理中,尤其是中文处理中,有时候这个多出来的回车字符会带来麻烦,需要被去除掉。代码如下:

while (getline (in, sLine))
{
     // remove '\t' at the end of sLine
     char cLast = sLine.at (sLine.length()-1);

     if (isspace(cLast))
           sLine.erase (sLine.length()-1);

     
     // do something to sLine
}

其中判断字符是否是回车的时候用到了isspace函数。这个函数是当字符为tab, line feed, home, form feed, carriage return的时候,都会返回true。再看一下isspace的实现,代码如下:

extern __inline int (__cdecl isspace) (
        int c
        )
{
    if (__locale_changed == 0)
    {
        return __fast_ch_check(c, _SPACE);
    }
    else
    {
        return (_isspace_l)(c, NULL);
    }
}

是调用 __fast_ch_check函数,将当前字符与_SPACE做“与”操作。_SPACE定义如下:

#define _SPACE          0x8     /* tab, carriage return, newline, */
                                                   /* vertical tab or form feed */

这就又有了个问题,空格的ascii值是32,十六进制表示为0x20,和0x8做与操作的结果是0啊。继续看__fast_ch_check的代码,它后来调用的是_chvalidator_l函数,实现如下:

extern "C" int __cdecl _chvalidator_l(
        _locale_t plocinfo,
        int c,
        int mask
        )
{
    _LocaleUpdate _loc_update(plocinfo);


    _ASSERTE((unsigned)(c + 1) <= 256);
    if (c >= -1 && c <= 255)
    {
        return (_loc_update.GetLocaleT()->locinfo->pctype[c] & mask);
    }
    else
    {
        return (_loc_update.GetLocaleT()->locinfo->pctype[-1] & mask);
    }
}


最终用到了系统locale信息,将字符c映射到pctype数组中的一个位置,这个位置的值是0x48,与0x8这个mask做与操作结果是1。就是说,对于空格,也能够判断出来。