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



#include<iostream> #include<iomanip> #define OK 1 #define ERROR 0 #define OVERFLOW -2 using namespace std; typedef struct {//图书信息定义     char no[20];    //图书ISBN     char name[50];   //图书名字     float price;   //图书价格 }Book; typedef struct LNode {//图书信息表的链式存储结构     Book data;         //结点的数据域     int length;       //链表的表长,即图书表中图书个数     struct LNode *next; //指针域 }LNode,*LinkList; int InitList_L(LinkList &L) {//构造一个空的单链表L     L=new LNode;     L->next=NULL;     return OK; } int Input_L(LinkList &L) {//链表的输入     LinkList p=L;      //初始化p指向链表的头结点     int n;     cin>>n;     while(n--)                   //后插法创建链表     {         LinkList q=new LNode;        //生成新结点*q         cin>>q->data.no>>q->data.name>>q->data.price;//输入数据         q->next=NULL;                                 //尾指针置为NULL         p->next=q;                   //将新结点*q插入尾结点*p之后         p=q;                         //更新指针p,将p指向新的尾结点*q     }     return OK; } int  Length_L(LinkList &L) {//求链表的表长,即图书表中图书个数     LinkList p=L;                 //初始化p指向链表的头结点     L->length=0;                  //初始化链表的表长L->length为0     while(p->next)                //下一个结点存在时     {         L->length++;              //链表的表长+1         p=p->next;                //更新指针p,指向下一个结点     }     return OK; } int Delete_L(LinkList &L) {//出库旧图书并输出 /**************begin************/     } int main() {     LinkList L;                         //定义一个LinkList类型的变量L     InitList_L(L);                      //初始化一个空的链表L     Input_L(L);                         //输入链表数据     Length_L(L);                        //求链表的表长     Delete_L(L);                        //出库旧图书并输出     return 0; } 写出出库就图书并输出的这个代码。
最新发布
03-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值