最近的工作

这篇博客只是对我之前几个月学习的回顾,浅显之处望包涵,如果正在读这篇博客的读者有更好的解决方法,请留言,有用的地方我会吸收消化

最近一段时间做了两个还算全面的程序,一个是携程抓最低价,第二个是把数据按淘宝的要求解析完后以EXCEL的格式发过去。
完成第二个程序后我觉得有必要写下来,希望一年后再回来看自己现在写的东西时会觉得现在写的东西都太简单,LOL。

携程抓最低价那个程序是一个WCF程序,是我工作以来第一个能完全思考如何实现这样一个功能的程序,而写这个程序也把我之前学的东西都全整合了一次,第一次有了在搭积木的感觉,把这个程序一步一步搭起来,虽然这个程序里考虑到了以后可能会扩展到多个程序所以用了委托并留出了接口,也花了很多时间去想数据该怎么解析成携程接口允许的格式,以及返回数据后该保存的保存,该存Redis的存Redis,公司有数据库访问组件,Redis的操作也用组件封装好了的,所以这个程序其实充其量就是个小程序。
这是携程给的接口需要的参数格式

如果要说在解析这些指令遇到的比较好耍的事情,一个是把机场码转为城市码时,当时做了个缓存字典,因为字典是引用类型,以前写缓存时都是把缓存字典A指向新构造出来的字典就可以了,但会觉得就算老缓存字典没有再指向它的指针过后就会被标记然后被GC回收,一想到要两次标记之前还有这么大一堆数据存在内存中,就换了一直方法,根据ModifyTime就能只拿出变化的数据,在根据现在有,以前无新增,现在有以前也有更新,然后剩下的就是需要删除的数据了。这个只能算是时间换空间吧。第二个就是构造日期格式,现在还记得那天晚上回来再想这个怎么处理,要考虑到属于的是不是标准格式,还有补0操作,那天晚上自言自语的把它解决了,不过想通了之后,再遇到拆分补0之类的就感觉是一个解决办法了,最后一个,是关于正则表达式的,在写这个程序之前,我不会正则表达式,虽然有看资料试着写了些,但这突然一个复杂点的返回数据我就懵逼了,发起请求后返回的数据是XML格式的几百条数据,每一条数据又由于航程类型的不同而有不同的十多个字段,所以这一部分是拿给我们组的binwei写的,不过他写完后我就偷学了。。。然后在给测试同学写测试程序时,我把正则用上了.

 public class Program
    {
        static Program()
        {
            Console.Title = "第三方抓价测试测序";
            Console.WriteLine("********************************************************************************");
            Console.WriteLine("输入行程类型,出发城市,到达,航司,出发日期,运价类型(0:公布 1:私有),乘客人数");
            Console.WriteLine("  旅客资质(0: ADT,1: STU,2: LAB,3: EMI,4: SEA),是否含税,航班号,舱位");
            Console.WriteLine();
            Console.WriteLine("          单程示例:OW,SHA,PEK,MU,2016-02-20,,1,1,1,0,MU1686,Y+S");
            Console.WriteLine(" 往返示例:RT,CTU,BJS,CA,2010-10-10,2011-10-28,0,9,4,0,CA16846/CA678,Y+S/M+S");
            Console.WriteLine("********************************************************************************");
            Console.WriteLine();
        }

        public static void Main(string[] args)
        {
            try
            {
                while (true) 
                {
                    Better517Na.InterPriceCatchService.Helper.Helper helper = new Better517Na.InterPriceCatchService.Helper.Helper();
                    MParamInterPriceContrast model = new MParamInterPriceContrast();
                    string inputStr = Console.ReadLine();

                    Regex pattern = new Regex(@"(?<VoyageType>[A-Z]{2})\,(?<FromCity>[A-Z]{3})\,(?<ToCity>[A-Z]{3})\,(?<Carrier>([A-Z]{2})|([3][U]))\,(?<OutwardDate>[0-9]{4}\-[0-1][0-9]\-[0-3][0-9])(?=\,)(\,(?<BackwardDate>[0-9]{4}\-[0-1][0-9]\-[0-3][0-9]\,)|(\,\,))(?<PriceType>[0-1])\,(?<PassergerNum>[1-9])\,(?<PassergerQualify>[0-4])\,(?<IsTaxInclueded>[0-1])\,(?<FLTNum>(([A-Z]{2})|([3][U]))\d+(\/(([A-Z]{2})|([3][U]))\d+)?)\,(?<Cabin>(([A-Z]\+)*?[A-Z])(\/([A-Z]\+)*?[A-Z])?)$");
                    Match mat = Regex.Match(inputStr, pattern.ToString(), RegexOptions.None);
                    if (!mat.Success)
                    {
                        Console.WriteLine("输入格式不正确,请参照示例");
                    }
                    else
                    {
                        model.VoyageType = mat.Groups["VoyageType"].Value == "RT" ? 1 : 0;
                        model.FromCity = mat.Groups["FromCity"].Value;
                        model.ToCity = mat.Groups["ToCity"].Value;
                        model.Carrier = mat.Groups["Carrier"].Value;
                        model.OutwardVoyageDate = DateTime.Parse(mat.Groups["OutwardDate"].Value);
                        model.BackVoyageDate = mat.Groups["BackwardDate"].Value == string.Empty ? DateTime.MinValue : DateTime.Parse(mat.Groups["BackwardDate"].Value);
                        model.PriceType = int.Parse(mat.Groups["PriceType"].Value);
                        model.PassengeNum = int.Parse(mat.Groups["PassergerNum"].Value);
                        model.Passengerqualify = int.Parse(mat.Groups["PassergerQualify"].Value);
                        model.IsTaxIncluded = int.Parse(mat.Groups["IsTaxInclueded"].Value);
                        model.FLTNumber = mat.Groups["FLTNum"].Value;
                        model.Cabin = mat.Groups["Cabin"].Value;

                        string para = JsonConvert.SerializeObject(model);
                        helper.InterPriceCatch("第三方抓价服务存ridis测试", para, "pnr");
                    }
                }
            }
            catch (Exception ex) 
            {
                Console.WriteLine(ex.ToString());
                Console.ReadLine();
            }
        }
    }

写完这个正则,我就发觉至少这一点上,效果比看书来得快。。

第二个程序就是这两天弄完的一个小程序,我们需要把我们的数据解析好后按淘宝给出的Excel文档格式保存到Excel给淘宝发过去。平时闲时写了一个配置文件读写在这个程序里用上了,Excel导出本地,在查了资料后,写出来了。

        /// <summary>
        /// 将对象链表保存到内存表
        /// </summary>
        /// <param name="list">淘宝对象链表</param>
        /// <returns>内存表</returns>
        private DataTable ConvertToDataTable(List<MTaobaoPolicy> list) 
        {
            //// 构造内存表
            DataTable table = new DataTable();
            table.Columns.Add("列名1", typeof(string));
            table.Columns.Add("列名2", typeof(string));
            table.Columns.Add("列名3", typeof(string));
            table.Columns.Add("列名4", typeof(string));


            //// 数据绑定
            foreach (var item in list) 
            {
                DataRow row = table.NewRow();
                row[0] = item.ProductNum;
                row[1] = item.HalfRtCalcWay;
                row[2] = item.Carrier;
                row[3] = item.AddonLimit;
                table.Rows.Add(row);
            }

            return table;
        }
  /// <summary>
        /// 将对象链表保存到内存表
        /// </summary>
        /// <param name="list">淘宝对象链表</param>
        /// <returns>内存表</returns>
        private DataTable ConvertToDataTable(List<MTaobaoPolicy> list) 
        {
            //// 构造内存表
            DataTable table = new DataTable();
            table.Columns.Add("列名1", typeof(string));
            table.Columns.Add("列名2", typeof(string));
            table.Columns.Add("列名3", typeof(string));
            table.Columns.Add("列名4", typeof(string));


            //// 数据绑定
            foreach (var item in list) 
            {
                DataRow row = table.NewRow();
                row[0] = item.ProductNum;
                row[1] = item.HalfRtCalcWay;
                row[2] = item.Carrier;
                row[3] = item.AddonLimit;
                table.Rows.Add(row);
            }

            return table;
        }

        /// <summary>
        /// 将内存表转换到Excel
        /// </summary>
        /// <param name="table">内存表</param>
        private void ConvertToExcel(DataTable table) 
        {
            object miss = System.Reflection.Missing.Value;
            Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
            Workbook workBook = app.Workbooks.Add(true);
            Worksheet workSheets = workBook.Worksheets[1] as Worksheet;
            app.Visible = false;

            try
            {
                int colIndex = 0;
                int rowIndex = 1;

                //// 构造Excel列名
                foreach (DataColumn item in table.Columns)
                {
                    colIndex++;
                    workSheets.Cells[1, colIndex] = item.ColumnName;
                }

                //// 填充数据
                foreach (DataRow cow in table.Rows)
                {
                    colIndex = 0;
                    rowIndex++;
                    foreach (DataColumn col in table.Columns)
                    {
                        colIndex++;

                        //// 注意这里 EXCEL的单元格行和列不能从0开始   但DataRow索引从0开始
                        workSheets.Cells[rowIndex, colIndex] = cow[colIndex - 1];
                    }
                }

                string path = ConfigurationSettings.AppSettings.Get("ExcelPath").Trim();
                if (!Directory.Exists(path)) 
                {
                    Directory.CreateDirectory(path);
                }

                if (!path.EndsWith("\\") && !path.EndsWith("/")) 
                {
                    path = path + "/";
                }

                Console.WriteLine(string.Format("Excel保存路径: {0}", path));
                string fileName = path + DateTime.Now.ToString().Replace(":", string.Empty).Replace("/", string.Empty).Replace(" ", string.Empty) + ".xls";
                workSheets.SaveAs(fileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlExcel5, miss, miss, miss, miss, miss, miss, miss, miss);
                Console.WriteLine("保存成功");
            } catch (Exception ex)
            {
                //// 记日志
            }
            finally 
            {
                workBook.Close(Microsoft.Office.Interop.Excel.XlSaveAction.xlSaveChanges, Missing.Value, Missing.Value);
                app.Quit();
            }
        }

这段代码在网上很常见,任何的对数据的操作,都是在内存里完成的,再将这些数据导出就是我们想要的结果,EXCEL表在导出之前,我们先将数据装填进一张内存表,我原本以为这张表就是我们导出的EXCEL表,尽管从感性上来想的话我们确实在内存里按照我们想要的EXCEL格式和结果构造了这张表,但是最终这张内存表中的数据要被全部转移到workSheet表中,似乎既然如此,不去构造这张内存表直接按相同的方式在嵌套循环里把table换成对象链表就可以了,不过这样处理的话,对象里的成员无法用foreach遍历,只能一个个的赋值,并且还得赋一个值加一次索引,列名也是这样。虽然这样可以少使用写内存,不过如果内存容量很小的情况下,这样的操作会清晰简单很多。

接着昨天晚上写的继续写。
今天用NPOI把导出那段代码重写了一次,使用Microsoft.Office.Interop.Excel.DLL遇到了下面几个问题:

1.往Cells里写数据时,行和列的索引不能从0开始。否则报HRESULT:0X800A03EC这里写图片描述

这个异常至少你还能抓住。

2.我专门测了下,往一个单元格里写入的字符长度超过127个,异常:超出当前范围,HRESULT:0X8002000A,溢出了。
这里写图片描述
这个异常你Catch不到,只能看到导出的Excel里那个格子的值为空。

3.格式很怪。

换成了NOPI上面的问题就都解决了,关于NOPI的EXCEL操作,可以看这里

https://msdn.microsoft.com/zh-tw/ee818993.aspx

只能说明白OfficeDLL的那段写法,用NPOI就很容易写了,只是多了内存类和文件流过程。

        /// <summary>
        /// 将内存表转换到Excel
        /// </summary>
        /// <param name="table">内存表</param>
        private void ConvertToExcel(DataTable table) 
        {
            ////object miss = System.Reflection.Missing.Value;

            //// EXCEL文档
            HSSFWorkbook workBook = new HSSFWorkbook();

            //// 文档中的工作表
            HSSFSheet workSheet = workBook.CreateSheet() as HSSFSheet;

            //// 构造工作表中的第一行
            HSSFRow row = workSheet.CreateRow(0) as HSSFRow;
            //Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
            //Workbook workBook = app.Workbooks.Add(true);
            //Worksheet workSheets = workBook.Worksheets[1] as Worksheet;
            //app.Visible = false;

             //// NPOI是先行后列构造EXCEL表 而Office是先列后行构造表 效果一样 但Office的方式会少一步构造单元格 按列划分其实就是在构造单元格
            try
            {
                int colIndex = 0;
                int rowIndex = 0;

                //// 构造Excel列名
                foreach (DataColumn item in table.Columns)
                {
                    //// 设置第一行的所有单元格
                    row.CreateCell(colIndex, NPOI.SS.UserModel.CellType.String).SetCellValue(item.ColumnName);
                    //// workSheet.col = item.ColumnName;
                    colIndex++;
                }

                //// 填充数据
                foreach (DataRow cow in table.Rows)
                {
                    colIndex = 0;
                    rowIndex++;

                    //// 新建行
                    row = workSheet.CreateRow(rowIndex) as HSSFRow;
                    foreach (DataColumn col in table.Columns)
                    {
                        ////OfficeDLL的EXCEL单元格行和列不能从0开始  
                        //// NPOI中和数组索引一致为0开始
                        row.CreateCell(colIndex, NPOI.SS.UserModel.CellType.String).SetCellValue(cow[colIndex].ToString());
                        colIndex++;
                    }
                }

                //// NPOI另一个与Office不同的地方是通过内存表-流-FileStream完成的
                //// 另外运行速度来讲,NPOI的速度比Office的Excel快很多
                MemoryStream stream = new MemoryStream();

                //// 内存表写入流
                workBook.Write(stream);
                string path = ConfigurationSettings.AppSettings.Get("ExcelPath").Trim();
                if (!Directory.Exists(path)) 
                {
                    Directory.CreateDirectory(path);
                }

                if (!path.EndsWith("\\") && !path.EndsWith("/")) 
                {
                    path = path + "/";
                }

                Console.WriteLine(string.Format("Excel保存路径: {0}", path));
                string fileName = path + DateTime.Now.ToString().Replace(":", string.Empty).Replace("/", string.Empty).Replace(" ", string.Empty) + ".xls";
                FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write);
                byte[] data = stream.ToArray();
                file.Write(data, 0, data.Length);
                //// workSheets.SaveAs(fileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlExcel5, miss, miss, miss, miss, miss, miss, miss, miss);
                Console.WriteLine("保存成功");

                //// 处理数据
                stream.Flush();
                stream.Close();
                file.Close();

                stream = null;
                file = null;
                data = null;
            }
            catch (Exception ex)
            {
                //// 记日志
            }
            finally 
            {
                //// NPOI自己不会开启Excel进程 所以不用关闭

                //workBook.Close(Microsoft.Office.Interop.Excel.XlSaveAction.xlSaveChanges, Missing.Value, Missing.Value);
                //app.Quit();
            }
        }

用NPOI的导出速度快了很多,而且导出的格式也是规范的格式。

最后记一个,也是在这个程序里,一个解析航班号的功能,如果用户选择的是要排除的航班号,我们必须把适用的航班号导进EXCEL中,但是由于前端对这个字段没有用正则表达式进行输入限制,本来JS里几句话的判断在后台要用很多行来解析判断,应该是MU0921/MU0032/…..的数据取出来之后成了MU921/MU32/… 或者MU921MU032/…. ,需求是航班号是在0001和9999之间。所以自己的处理思路:

1,按‘/’拆分航班号,去掉航司,去掉左边补上的0,取出数字。
2.写一个冒泡来排序。
3.考虑到可能出现航司时候有5位数,所以边界值也必须放进去排序。
4,边界值被排的情况,要寻找新的并且不存在于排除数字数组中的新边界值。
5,边界值没被排的情况,由于不能写死,因此只能加边界值加入排序。
6,根据比较排除数字之间的差值为0,为1,大于1,选择两个都不要,取中间唯一的一个,取中间的一个范围。
7.被排除的数只能从的结束和开始范围在相邻的两个数。

结果这段代码写了将近200行,用了4个标志位,功能是实现了,但是写的比较傻,如果是不考虑超过边界值9999的情况,这样想来,上面的逻辑对排序完后数据的优势没完全用上。如果允许的最大值为9999:

3.将排序后的链表最大值和允许的最大值N=9999比较,如果相等,删掉链表中的这个值,更新N=N-1,再与链表中次大值比较,直到没有相等的数。N作为新的右边界,否则维持9999为边界。
4.左边界3逻辑一样。
5.根据比较排除数字之间的差值为0,为1,大于1,选择两个都不要,取中间唯一的一个,取中间的一个范围。

这样就可以不要这4个标志位了。只不过当时我是怎么想到用头一个方法的。

就写到此吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值