工作中有个需求需要采集每个服每天用户的登录信息、道具使用情况等(用来做数据分析),这些信息、数据

是通过技术那边的Http接口来获取,为了提高效率,节省流量,没有用XML或JSON格式的输出(数据量比较

大,用XML格式或JSON格式或增加许多冗余的信息,优劣暂且不讨论),而是用简单的文本形式,Http接口

数据的输出的格式如下:

 

1:不同记录以"\r\n"分隔

2:不同字段以“|||”分隔

 举个例子(假设),用户每天的登录信息:

字段排列顺序为: GameID|||UserID|||UserName|||ServerID|||ServerName|||IP|||RegistedFrom

|||FromSiteUrl|||LoginTimes|||CreateDate

下面是其中一个函数,大家如果不想看,完全可以先跳过,只是为了说明下面的问题

 

public bool ParseDataFromUrl(string url, string serverName, string dateTime)
{

    HttpWebResponse httpResponse = RequestUtil.GetHttpResponse(url);


    if (httpResponse == null)
    {
        log.Error(url + ": 解析URL内容出错,请检查配置表");

        SendMessage.MessageSending(serverName + ": 解析URL内容出错,请检查配置表");

        return false;
    }

    StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream(), Encoding.GetEncoding("UTF-8"));

    string[] columns = { "ID", "GameID", "UserID", "UserName", "ServerID", "ServerName", "IP", 
            "RegistedFrom", "FromSiteUrl", "LoginTimes", "CreateDate", "GatherDate" };

    Type[] dataTypes = {                                 
                            typeof(Int32),  typeof(Int32),  typeof(string), 
                             typeof(string), typeof(string), typeof(string),                                 
                             typeof(string), typeof(string), typeof(string), 
                             typeof(Int32),  typeof(Int32),  typeof(string)                            
                       };
    DataTable dtDataSource = DataTableUtil.DataTableStruct(columns, dataTypes);

    try
    {
        string line = "";
        string tableName = ConfigurationSettings.AppSettings["UserLoginInfoTable"].ToString().Trim();

        //while ((line = streamReader.ReadLine()) != null)       
        while (streamReader.Peek() >= 0)
        {
            line = streamReader.ReadLine();
            if (line == "<?php exit;?>" || line == "")
                continue;

            if (!line.Contains("|||"))
            {
                log.Fatal("ParseDataFromUrl: " + serverName + url + "----没有权限获取日志内容 ");

                SendMessage.MessageSending(serverName + "无权限访问日志内容,请检查配置表");
                continue;

            }

            string[] logText = line.Split(new string[] { "|||" }, StringSplitOptions.None);

            DataRow row = dtDataSource.NewRow();

            row["ID"] = 0;

            if (RegexUtil.IsNumeric(logText[2]))
            {
                row["GameID"] = Int32.Parse(logText[2]);
            }
            else
            {
                row["GameID"] = 0;
            }
            row["ServerID"] = logText[3].Trim();
            row["ServerName"] = serverName;
            row["IP"] = logText[7].Trim();
            row["UserID"] = logText[0].Trim();

            row["UserName"] = logText[1].Trim();
            row["RegistedFrom"] = logText[5].Trim();
            row["FromSiteUrl"] = logText[4].Trim();

            if (RegexUtil.IsNumeric(logText[8].Trim()))
            {
                row["LoginTimes"] = Int32.Parse(logText[8]);
            }
            else
            {
                row["LoginTimes"] = 0;
            }

            if (RegexUtil.IsNumeric(logText[9]))
            {
                row["CreateDate"] = Int32.Parse(logText[9]);
            }
            else
            {
                row["CreateDate"] = 0;
            }
            row["GatherDate"] = dateTime;
            dtDataSource.Rows.Add(row);

            if (dtDataSource.Rows.Count == 5000)
            {
                ImportToDataBase(dtDataSource, tableName);
                dtDataSource.Rows.Clear();
            }
        }

        httpResponse.Close();
        streamReader.Close();


        if (dtDataSource.Rows.Count > 0 && dtDataSource.Rows.Count < 5000)
        {
            ImportToDataBase(dtDataSource, tableName);
        }

    }
    catch (Exception exc)
    {

        log.Fatal("函数ParseDataFromUrl出错:" + exc.Message);

        SendMessage.MessageSending("函数ParseDataFromUrl出错, 详情请见日志!");

    }

    return true;

}



public static HttpWebResponse GetHttpResponse(string url)
{
    try
    {
        HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);

        httpRequest.Timeout = 6000000; //1000 * 60 * 10; 用后面的效率低下,会有多余的计算步骤

        httpRequest.ContentType = "application/x-www-form-urlencoded";
        httpRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; ccdotnet ;.NET CLR 1.1.4322; .NET CLR 2.0.50727)";
        httpRequest.Accept = "*/*";
        httpRequest.KeepAlive = true;
        httpRequest.Headers.Add("Accept-Language", "zh-cn, en-us; q=0.5");
        httpRequest.AllowAutoRedirect = true;

        HttpWebResponse response = (HttpWebResponse)httpRequest.GetResponse();
        return response;
    }
    catch (Exception exc)
    {
        return null;
    }


}

 

 

 

在测试过程中,遇到了一个让我比较郁闷的问题:采集下来的数据跟接口提供的数据条数不一致,调

试过程居然发现每次运行取得的数据居然都不一致,刚开始还以为是HttpWebResponse 

httpResponse = RequestUtil.GetHttpResponse(url);里面HttpWebRequest对象的Timeout

设置过短的超时引起的数据异常,等到把这个Timeout设置很长后,定位错误情况的时候,居然发现

罪魁祸首是:while (streamReader.Peek() >= 0),往往还没有获取所有记录,Peek就返回了-1

, 查了下MSDN,关于Peek的结束如下:

 

 

Peek 方法返回一个整数值以便确定是否到达文件末尾,或发生其他错误。这样一来,用户在将返回值

强制转换为 Char 类型之前就可以首先检查该值是否为 -1。换句话说,它不需要先转换字符,即可返

回是否达到文件末尾。

http://msdn.microsoft.com/zh-cn/library/system.io.streamreader.peek.aspx


现在情况是StreamReader对象还没读写到文件末尾,那肯定是发生了其它错误,但是又没有什么异

常抛出,我只好改用while ((line = streamReader.ReadLine()) != null)来读写,测试结果完全

正确,于是网上搜索了下,发现邀月的博客的博客里面有涉及这方面的内容

http://www.cnblogs.com/downmoon/archive/2010/08/18/1802095.html

不过他里面所涉及的是StringReader.ReadLine出现的异常的情况(具体情况大家可以去他博客

看看):

StringReader.ReadLine 方法将行定义为后面跟有下列符号的字符序列:换行符(“\n”)、回车符

(“\r”)或后跟换行符的回车符(“\r\n”)。 所产生的字符串不包含终止回车符和/或换行符 如果

到达基础字符串的结尾,则返回值为 null  我的理解:如果由于编码的问题,导致读取异常,也

就是无法读取行标志时,可能会认为已到文件结尾而断下行的读取。这也解释了为什么会有时读取

不完整的原因。

搜索了很久发现了有一些网友也碰到了类似的问题,不过也没发现有价值的解释,我列举下搜索到的

一些资料

http://www.netframeworkdev.com/net-framework-networking-communication

/streamreader--peek-null-61609.shtml

http://bbs.bccn.net/viewthread.php?tid=88673