由项目需要,需要扫描1200万行的文本文件。经网友的指点与测试,发现C#与Delphi之间的差距并不大。不多说,列代码测试:

下面是Delphi的代码:

 

//遍历文件查找回车出现的次数
function ScanEnterFile(const FileName:string):TInt64Array;
var
  MyFile:TMemoryStream;//文件内存
  rArray:TInt64Array;       //行索引结果集
  size,curIndex:int64;//文件大小,当前流位置
  enterCount:int64;//回车数量
  DoLoop:Boolean;//是否继续循环
  pc: PChar;
  arrayCount:int64;//当前索引数组大小
  addStep:integer;//检测到回车字符串时需要添加的步进
begin
  if fileName = '' then
    Exit;
  if not FileExists(fileName) then
    Exit;
  MyFile:=TMemoryStream.Create;//创建流
  MyFile.LoadFromFile(fileName);//把流入口映射到MyFile对象
  size:=MyFile.Size;
  pc:=MyFile.Memory; //把字符指针指向内存流
  curIndex:=RowLeast;
  DoLoop:=true;
  enterCount:=0;
  setlength(rArray,perArray);
  arrayCount:=perArray;
  enterCount:=0;
  rArray[enterCount]:=0;
  while DoLoop do
  begin
    addStep:=0;
    if (ord(pc[curIndex])=13then
      addStep:=2;
    if (ord(pc[curIndex])=10then
      addStep:=1;
    //处理有回车的
    if (addStep<>0then
    begin
      Application.ProcessMessages;
      //增加一行记录
      inc(enterCount);
      //判断是否需要增大数组
      if (enterCount mod perArray=0then
      begin
        arrayCount:=arrayCount+perArray;
        setlength(rArray,arrayCount);
      end;
      rArray[enterCount]:=curIndex+addStep;
      curIndex:=curIndex+addStep+RowLeast;
    end
    else
      curIndex:=curIndex+2;
    if curIndex> size then
      DoLoop:=false
    else
      DoLoop:=true;
  end;
  result:=rArray;
  freeandnil(MyFile);
end;

执行代码:

 

procedure TMainForm.btn2Click(Sender: TObject);
var
  datasIndex:TInt64Array;//数据文件索引
begin

  t1:=GetTickCount;
  datasIndex:=ScanEnterFile('R:\201201_dataFile.txt');
  Caption:=Caption+'::'+inttostr(GetTickCount-t1); 
end;

执行结果是:16782 ms

 

下面是C#的代码:

 

        /// <summary>
        
/// 扫描文本文件,进行行数的统计,并返回每一行的开始指针数组(1.2KW数据速度比使用数组的快10秒)
        
/// </summary>
        
/// <param name="fileName">文件名</param>
        
/// <param name="rowCount">行数</param>
        
/// <param name="rowLeast">一行最小长度</param>
        
/// <param name="incCount">递增索引数组数量</param>
        
/// <param name="initCount">首次初始化行索引数量</param>
        
/// <returns>索引列表</returns>
        public static IList<long> ScanEnterFile(string fileName, out int rowCount, int rowLeast,ThreadProgress progress)
        {
            rowCount = 0;
            if (string.IsNullOrEmpty(fileName))
                return null;
            if (!System.IO.File.Exists(fileName))
                return null;
            FileStream myFile = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8);//把文件读入流
            IList<long> rList=new List<long>();
            int enterCount = 0;//回车数量
            int checkValue;
            int addStep;
            myFile.Position = rowLeast;
            checkValue = myFile.ReadByte();
            while (checkValue != -1)
            {
                //Application.DoEvents();
                addStep = -1;
                //由于文件ReadByte之后,其当前位置已经往后推移了移位。
                
//因此,如果是回车的第一个字符,则要推移一位。
                
//而如果是回车的第二个字符,则不用推移一位
                if (checkValue == 13)
                    addStep = 1;
                else if (checkValue == 10)
                    addStep = 0;
                if (addStep >= 0)
                {
                    enterCount++;
                    rList.Add(myFile.Position + addStep);
                    myFile.Seek(rowLeast + addStep, SeekOrigin.Current);
                    progress(enterCount);
                }
                else myFile.Seek(2, SeekOrigin.Current);
                checkValue = myFile.ReadByte();
            }
            rowCount = enterCount + 1;
            return rList;
        }

执行的代码:

 

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            int rowCount;
            FileHelper.ScanEnterFile(@"R:\201201_dataFile.txt"out rowCount, 35, outputProgress);
            useTime = stopwatch.ElapsedMilliseconds;

执行结果是:

124925  ms

(经过众多网友的批评与指点,该方法并没有把文件读取内存中,而是逐个字节地读取,速度比Delphi字节读进内存的方法要慢很多。这种方法只适合于老机器,内存不够的情况下,当今内存已经很便宜了,所以,该方法目前已经过时了,下面经过网友的指点,使用了readline的方法,速度大概是6秒左右。)

 

        public static IList<long> ScanEnterFile(string fileName, ThreadProgress progress)
        {
            if (string.IsNullOrEmpty(fileName))
                return null;
            if (!System.IO.File.Exists(fileName))
                return null;
            IList<long> rList = new List<long>();
            rList.Add(0);
            StreamReader sr = File.OpenText(fileName);
            string rStr = sr.ReadLine();
            while (null != rStr)
            {
                rList.Add(rList[rList.Count-1] + rStr.Length + 2);
                rStr = sr.ReadLine();
                progress(rList.Count);
            }
            sr.Close();
            return rList;
        }

 

经过测试,该方法如果存在中文字符编码的时候,其位置是错误的。日后找到解决方法后,再上来更新。

经过测试,C#的使用IList<T>比数组的要快。

总结:任何事物都有其存在的价值,至于看官门选什么,就根据自己的需要,来选择,这里,本人不会有任何偏向于哪一方。反正,能成事,什么都不重要了。

posted @ 2012-01-12 15:39 努力偷懒 阅读(3268) 评论(37) 编辑

(Spark)openfire是一个非常不错的IM服务器,而且是纯Java实现,具有多个平台的版本,他的数据存储可以采用多种数据库,如MySQL,Oracle等。

在实际使用时大家遇到最多的就是采用MySQL数据库后的中文乱码问题,这个问题十分有趣,而且从现象上可以看出openfire内部的一些机制。

实际问题是这样的:首先启动openfire服务器,然后利用客户端或直接登录到后台新建一个帐户,为该帐户指定一些中文的属性,如姓名等。如果不重启服务器,你永远不会觉得有什么不对的地方,因为所有的中文显示都是正常的。接下来重启一下openfire,再用建立的帐号登录客户端或进入后台管理端查看,会发现所有的中文全都变成了问号。登录到数据库中进行查看,发现所有的中文字符也均为问号,这说明了两个问题:

  1. openfire具有应用层缓存
  2. 数据库编码存在问题

解决办法其实也很简单,首先要保证你为openfire创建的数据库编码是utf8的,建表语句如下:

create database openfire default character set utf8 default collate utf8_general_ci

 

当你原来就创建好数据库时,你可以用:

 alter database openfire default character set utf8 default collate utf8_general_ci;

 

其次,在初始化openfire数据库,即第一次配置openfire服务器时,在连接数据库那里的连接串要加入字符编码格式,必须在连接里增加UTF8的编码要求,连接字符串设置如下:

jdbc:mysql://127.0.0.1:3306/openfire?useUnicode=true&characterEncoding=utf8

如果已经安装完成,这个配置也是可以改动的,直接到openfire的安装目录下,找到conf/openfire.xml这样一个文件,打开找到如下的XML节,修改其中的serverURL即可

<database>
<defaultProvider>
<driver>com.mysql.jdbc.Driver</driver>
<serverURL>jdbc:mysql://127.0.0.1:3306/openfire?useUnicode=true&amp;characterEncoding=utf8</serverURL>

注意:由于&具有特殊含义,因此原&符号必须被转义为&amp;

posted @ 2011-11-16 16:46 努力偷懒 阅读(107) 评论(0) 编辑

我们来先看看WinAction的有关数据操作的流程:(包括更新数据操作和取消更新的操作)

具体的参考代码:

更新数据操作:

 

        /// <summary>
        
/// 实体更新时执行
        
/// </summary>
        
/// <param name="entity">当前操作的实体</param>
        
/// <param name="caller">修改对象(窗体)</param>
        
/// <param name="sender">修改对象(窗体)的按钮</param>
        
/// <param name="e">唤出窗体的事件参数</param>
        public virtual void SaveEntity(object entity, object caller, object sender, EventArgs e)
        {
            
try
            {
                
object mResult = useBll.Update((T)entity);
                
//卸载修改窗体的方法
                if (caller is IEditDataForm)
                    UnloadEditForm(caller 
as IEditDataForm);
                
//隐藏执行该方法的窗体(修改窗体)
                if (caller is Form)
                    (caller 
as Form).Hide();
                
if (AfterUpdateEntity != null)
                    AfterUpdateEntity(entity, caller, 
this, e);
            }
            
catch (Exception ex)
            {
                
/*
                if (entity is Entity)
                {
                    if (!(entity as Entity).IsNewEntity)
                    {//出错时把数据实体的数据进行恢复
                        if (caller is IBindEntityAware)
                            (caller as IBindEntityAware).RecoverEntity(entity);
                    }
                }
*/
                
if (AfterUpdateEntityError != null)
                    AfterUpdateEntityError(entity, caller, 
this, e);
                
else
                    
throw new Exception(ex.Message, ex);
            }
        }

        
/// <summary>
        
/// 更新实体对象列表时执行
        
/// </summary>
        
/// <param name="entity"></param>
        
/// <param name="caller">修改对象(窗体)</param>
        
/// <param name="sender">修改对象(窗体)的按钮</param>
        
/// <param name="e">唤出窗体的事件参数</param>
        public virtual void UpdateEntitys(object entity, object caller, object sender, EventArgs e)
        {
            
try
            {
                
if (caller is IDataListEdit)
                {
                    IEnumerable deletedList 
= (caller as IDataListEdit).GetDeletedDataList();

                    
foreach (object delData in deletedList)
                        useBll.Delete(TransformToEntity(delData));

                    IEnumerable updateList 
= (caller as IDataListEdit).GetCurrentDataList();

                    
foreach (object curData in updateList)
                        useBll.Update(TransformToEntity(curData));
                }
                
//卸载修改窗体的方法
                if (caller is IEditDataForm)
                    UnloadEditForm(caller 
as IEditDataForm);
                
if (caller is Form)
                    (caller 
as Form).Hide();
                
if (AfterUpdateEntity != null)
                    AfterUpdateEntity(entity, caller, 
this, e);
            }
            
catch (Exception ex)
            {
                
if (AfterUpdateEntityError != null)
                    AfterUpdateEntityError(entity, caller, 
this, e);
                
else
                    
throw new Exception(ex.Message, ex);
            }
        }

注意,有两种更新的方式:一种是单个数据实体更新,另外一个就是批量更新。

取消操作:

 

        /// <summary>
        
/// editform执行的取消实体更新时执行
        
/// </summary>
        
/// <param name="entity">当前操作的实体</param>
        
/// <param name="caller">修改对象(窗体)</param>
        
/// <param name="sender">修改对象(窗体)的按钮</param>
        
/// <param name="e">唤出窗体的事件参数</param>
        public virtual void CancelUpdateEntity(object entity, object caller, object sender, EventArgs e)
        {
            
//editform执行的取消更新过程
            if (AfterUpdateEntityCancel != null)
                AfterUpdateEntityCancel(entity, caller, 
this, e);
            
if (caller is IEditDataForm)
                UnloadEditForm(caller 
as IEditDataForm);
            
if (caller is Form)
                (caller 
as Form).Hide();
        }

原创作品出自努力偷懒,转载请说明文章出处http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/

posted @ 2011-09-09 11:29 努力偷懒 阅读(349) 评论(0) 编辑

我们来先看看WinAction的删除数据的流程:

具体的参考代码:

 

        public virtual void DeleteEntity(object entity, object caller, object sender, EventArgs e)
        {
            
if (caller is IDataListView)
            {
                IDataListView listForm 
= caller as IDataListView;
                
if (null == listForm.DataList || (listForm.DataList is ICollection && (listForm.DataList as ICollection).Count == 0))
                    
return;
            }
            
if (MessageBox.Show("确实要删除当前记录?""提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                
return;
            
//判断当前删除的是否是新增的记录。(如果是新增的那条,其id为-1)
            if (entity is Entity && (entity as Entity).IsNewEntity)
            {
                Form eForm 
= CallEditForm(caller);
                
if (eForm.Visible)
                    eForm.Close();
                
return;
            }
            useBll.Delete((T)entity);
            
if (updateFreshByDB && caller is IDataListView && (caller as IDataListView).RefreshEntityList != null)
                (caller 
as IDataListView).RefreshEntityList((caller as IDataListView).SearchEntity, caller, sender, e);
        }

 

原创作品出自努力偷懒,转载请说明文章出处http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/
posted @ 2011-09-09 11:23 努力偷懒 阅读(52) 评论(0) 编辑

我们来先看看WinAction的修改数据的流程:

具体的参考代码:

 

        public virtual void EditEntityHandler(object entity, object caller, object sender, EventArgs e)
        {
            
if (caller is IDataListView)
            {
                IDataListView listForm 
= caller as IDataListView;
                
if (null == listForm.DataList || (listForm.DataList is ICollection && (listForm.DataList as ICollection).Count == 0))
                    
return;
            }

            Form eForm 
= CallEditForm(caller);
            
if (eForm is IEditDataForm)
            {
                IEditDataForm mEditForm 
= eForm as IEditDataForm;
                
if (eForm.Visible)
                {
                    
if (mEditForm.Entity is Entity && (mEditForm.Entity as Entity).IsNewEntity)//新增时进行修改
                    {
                        
if (caller is IDataControlActionView)
                        {
                            
//修改最后新增的记录
                            if ((entity as Entity).IsNewEntity)
                            {
                                CommonFunctions.SetFormTop(eForm);
                                
return;
                            }
                        }
                        
if (caller is IDataControlActionView && caller is IDataListView && (caller as IDataListView).DataList is IList)
                            (caller 
as IDataControlActionView).RemoveLastNewRow();
                        
if (caller is IDataControlActionView)//已经执行过修改
                            UnloadEditEntityEvent(caller as IDataControlActionView);
                    }
                    
else
                    {
                        
if (mEditForm is IBindEntityAware)
                            (mEditForm 
as IBindEntityAware).BindEntity(entity);
                        CommonFunctions.SetFormTop(eForm);
                        
return;
                    }
                }
                LoadEditForm(EditForm);
            }
            
if (eForm is IBindEntityAware)
                (eForm 
as IBindEntityAware).BindEntity(entity);
            
if (caller is IDataControlActionView)
                LoadEditEntityEvent(caller 
as IDataControlActionView);
            
if (eForm.Visible)
                CommonFunctions.SetFormTop(eForm);
            
else
                eForm.Show();
        }

 

原创作品出自努力偷懒,转载请说明文章出处http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/
posted @ 2011-09-09 11:21 努力偷懒 阅读(49) 评论(0) 编辑

我们来先看看WinAction的新增数据的流程:

源码参考:

 

        public virtual void NewEntityHandler(object entity, object caller, object sender, EventArgs e)
        {
            
//判断编辑窗体是否已打开
            Form eForm = CallEditForm(caller);
            
if (eForm is IEditDataForm)
            {
                IEditDataForm mEditForm 
= eForm as IEditDataForm;
                
if (eForm.Visible)
                {
                    
if (mEditForm.Entity is Entity && (mEditForm.Entity as Entity).IsNewEntity)//新增时再次进行新增
                    {
                        CommonFunctions.SetFormTop(eForm);
                        
return;
                    }
                    
if (caller is IDataControlActionView)//修改时进行新增
                        UnloadEditEntityEvent(caller as IDataControlActionView);
                }
                LoadEditForm(EditForm);
            }

            T mEntity 
= GetNewEntity();
            
if (mEntity is Entity)
                (mEntity 
as Entity).IsNewEntity = true;
            
if (eForm is IBindEntityAware)
                (eForm 
as IBindEntityAware).BindEntity(mEntity);
            
if (caller is IDataListView && (caller as IDataListView).DataList is IList)
                ((caller 
as IDataListView).DataList as IList).Add(mEntity);

            
if (caller is IDataControlActionView)
                LoadEditEntityEvent(caller 
as IDataControlActionView);

            
if (eForm.Visible)
                CommonFunctions.SetFormTop(eForm);
            
else
                eForm.Show();
        }

原创作品出自努力偷懒,转载请说明文章出处http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/

posted @ 2011-09-09 11:20 努力偷懒 阅读(233) 评论(0) 编辑

我们来先看看WinAction的显示列表窗体的流程:


具体代码参考:

        public void InitListForm(Form listForm, object entity)
        {
            
if (listForm is IDataListView)
            {
                IDataListView mForm 
= listForm as IDataListView;
                IList
<T> dataList;
                
if (null==entity)
                    dataList 
= useBll.SelectAll<T>();
                
else
                    dataList 
= useBll.QueryForList<T>(entity);
                mForm.setDataList(TransformToDataListObject(dataList));
                
if (null == mForm.RefreshEntityList)
                    mForm.RefreshEntityList 
= RefreshEntity;
                
//初始化搜索对象
                if (entity != null)
                    mForm.SearchEntity 
= entity;
            }
            
//
            if (listForm is IDataListGridView && !(listForm as IDataListGridView).InitColumn)
            {
                (listForm 
as IDataListGridView).InitColumns(useBll.GetModelPropertys(), useBll.GetTableColumns(), pdm.FindTableByTableName(useBll.GetTableName()));
                
if (listForm is IDataControlActionView)
                {
                    IDataControlActionView dForm 
= listForm as IDataControlActionView;
                    dForm.DeleteEntity 
= DeleteEntity;
                    dForm.EditEntity 
= EditEntityHandler;
                    dForm.NewEntity 
= NewEntityHandler;
                    dForm.AfterUpdateEntity
= UpdateEditCallerData;
                    dForm.AfterUpdateEntityCancel 
= CancelEditCallerData;
                    dForm.AfterUpdateEntityError 
= ErrorEditCallerData;
                    dForm.ClosingListForm 
= ClosingListFormHandler;
                }
            }
        }

 

原创作品出自努力偷懒,转载请说明文章出处http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/

posted @ 2011-09-09 11:13 努力偷懒 阅读(119) 评论(0) 编辑

在步入正题前,我们来先聊一些题外的话,魔兽世界估计很多程序员都听过,可能玩过的人也很多,我虽然没怎么玩,但我也接触过,我们来说下战场,要想打赢,必须有一个人去指挥战场,如果没人指挥,这战场十有八九会输掉,我相信,很多人打过战场的人都深有体会的。没玩过魔兽,没打过战场?没关系,拿真实的战役来说吧,三国时期的赤壁之战,这个够经典了吧,如果连这个都没听过,下面的,不用看了。为何刘备他们能打赢?那是因为有一个很了得的军师:诸葛亮做指挥。在开战前,军师们,当然不只诸葛亮是军师,曹操他们那边也有不少。军师会先分析研究地形、气候、人,以及模拟猜测敌方会使用何种计谋等,然后再根据自己自身的条件去作出不同的人员安排等等。可知,要胜利,必须要有一个站在高处,纵观所有一切可以掌握的资源,去作出合理安排的指挥者。

做软件设计何尝不是呢?这就是为何我要设计WinAction的原因,这里WinAction就是这个站在一定的层面上去设计的,是由它来安排所有的资源去运行。这样做有以下的好处:

 

  1. 各功能模块相对独立,避免不同功能模块之间的引用混乱(做产品开发的人或者是十分注重可重用性的人,对这个的理解应该会比较深入,因为很多时候,你要复用一个功能模块,你就得去引用某个dll文件,如果这个dll里面的代码不是相对独立的,同时又引用了别的功能模块的,甚至有的引用到了别的项目的dll文件,那是多么的悲哀),因此,我们最好就是在其中一个层面上去引用你需要的模块。而不要因为方便,而无任何规律地盲目引用,我们这里,就是让WinAction可引用所有项目中需要用到的资源。
  2. 类似一种MVC的框架模式,WinAction调用BLL去获取需要的数据实体,然后把数据实体的数据推送到展示层使用,因此,WinAction就充当了Controler的角色了。
  3. 方便问题分析。目前大部分业务系统都是围绕着数据库进行开发的应用,因此,查问题就可以分开来查,在WinAction中检查页面通知要执行的操作,在执行这个操作的时候,检查相关的参数是否正确,如果是不正确,跟踪检查页面逻辑。如果正确,检查WinAction中所调用的操作流程的逻辑。如果是数据库操作的,可以直接检查执行当前执行的SQL语句有没有问题。
  4. 使用接口编程的方式,页面允许日后的重载,客户自己封装。只需满足接口需要,就可以进行各窗体的灵活替换。
我们将在日后的章节来作更深入的介绍。

 

原创作品出自努力偷懒,转载请说明文章出处http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/

posted @ 2011-09-09 11:05 努力偷懒 阅读(311) 评论(0) 编辑

其实PowerDesigner的pdm文件是一个xml文件来的,我们可以当作读取xml文件一样的方式来读pdm文件。

其中,我们需要注意的是:如果我们不使用命名空间管理器的话,系统会提示如下错误:

需要命名空间管理器或 XsltContext。此查询具有前缀、变量或用户定义的函数。  
说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。  

异常详细信息: System.Xml.XPath.XPathException: 需要命名空间管理器或 XsltContext。此查询具有前缀、变量或用户定义的函数。
因此,我们读取pdm文件代码如下:


其实C#读取pdm文件难度并不大,只是一种体力活,为了后人少走点弯路,我下面公布一下代码:

ColumnInfo.cs(字段类)


PdmKey.cs(主键类)

TableInfo.cs(表信息类)PdmReader.cs(pdm文件读取器类)

使用方法:


上面是使用这个pdm读取器的一个应用,把读取出来的表数量列出来。这个读取器可能还有某些地方没有调整好,读取出来的表数量好像比实际的要多,有可能是快捷方式引用出来的表也算进去了。有空必须要完善一下才行。 

 由于这个pdm读取器只是初步成型,能够完成基本的相关表、字段、主键等的读取功能,由于时间与工作关系,还有更多的信息没有读取处理,在这,我开放给大家,希望大家能够不断地完善,并且希望完善的人可以把源码发给我补充上来。


原创作品出自努力偷懒,转载请说明文章出处http://blog.csdn.net/kfarvid或 http://www.cnblogs.com/kfarvid/


posted @ 2011-09-08 16:35 努力偷懒 阅读(13) 评论(0) 编辑

几种比较和谐的文字背景颜色搭配

 

2009-03-26 21:45

1背景色:#F1FAFA″———做正文的背景色好,淡雅
A
2背景色:#E8FFE8″———做标题的背景色较好
A
3背景色:#E8E8FF″———做正文的背景色较好,文字颜色配黑色
A
4背景色:#8080C0″———上配黄色白色文字较好
A
5背景色:#E8D098″———上配浅蓝色或蓝色文字较好
A
6背景色:#EFEFDA″———上配浅蓝色或红色文字较好
A
7背景色:#F2F1D7″———配黑色文字素雅,如果是红色则显得醒目
A
">8背景色:#336699″———配白色文字好看些
A
">9背景色:#6699CC″———配白色文字好看些,可以做标题
A
">10背景色:#66CCCC″———配白色文字好看些,可以做标题
A
">11背景色:#B45B3E″———配白色文字好看些,可以做标题
A
12背景色:#479AC7″———配白色文字好看些,可以做标题
A
13背景色:#00B271″———配白色文字好看些,可以做标题
A
14背景色:#FBFBEA″———配黑色文字比较好看,一般作为正文
A
15背景色:#D5F3F4″———配黑色文字比较好看,一般作为正文
A
16背景色:#D7FFF0″———配黑色文字比较好看,一般作为正文
A
17背景色:#F0DAD2″———配黑色文字比较好看,一般作为正文
A
18背景色:#DDF3FF″———配黑色文字比较好看,一般作为正文
A

posted @ 2011-09-05 16:52 努力偷懒 阅读(245) 评论(0) 编辑