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

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。就是说,对于空格,也能够判断出来。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值