大文件可使用的文本分组统计工具(附带源码,原创)

Posted wangqiideal

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大文件可使用的文本分组统计工具(附带源码,原创)相关的知识,希望对你有一定的参考价值。

本工具可实现的效果:

1.读取大文件(大于1GB)

2.根据分隔符分割后的列分组

3.速度快。

4.处理过程中,可以随时停止处理,操作不卡死。

5.有对当前内存的实时监测,避免过多占用内存,影响系统运行。

6.实时显示处理的行数。

处理类代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace DaZhongLogTool
{

    // 定义事件的参数类
    public class ValueEventArgs : EventArgs
    {
        public int Value { set; get; }
    }
    // 定义事件使用的委托
    public delegate void ValueChangedEnentHandler(object sender, ValueEventArgs e);


    public class BigFileTongJiJobs
    {
        long ALLOW_MAX_USED_MEMORY = 1024 * 1024 * 1024;  //允许使用的最大内存,超过则结束

        public bool StartFlag { get; set; }

        // 定义一个事件来提示界面工作的进度
        public event ValueChangedEnentHandler ValueChanged;

        public void OnValueChange(ValueEventArgs e)
        {
            if (ValueChanged != null)
            {
                ValueChanged(this, e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="paramsInfo"></param>
        /// <returns>-1:未开始,就失败了;-2:文件不存在;-3,异常;大于0,处理成功</returns>
        public int StartAnalyseBigFile(TongjiParamsInfoStruct paramsInfo)
        {
            int handleLine = -1;
            
            string sTmpFile = paramsInfo.outputPath;
            if (File.Exists(sTmpFile))
            {
                File.Delete(sTmpFile);
            }

            if (!System.IO.File.Exists(sTmpFile))
            {
                FileStream fs;
                fs = File.Create(sTmpFile);
                fs.Close();
            }

            if (!File.Exists(paramsInfo.inputPath))
            {                  
                handleLine = -2;
                return handleLine;
            }

            FileStream streamInput = System.IO.File.OpenRead(paramsInfo.inputPath);
            FileStream streamOutput = System.IO.File.OpenWrite(sTmpFile);

            int iRowCount = 0;
            int iRowCharCount = 0;
            List<byte> rowByteData = new List<byte>();//行字节List
            Dictionary<string, int> tongjiDict = new Dictionary<string, int>(); //统计字典
            string rowStr = "";

            //获取当前进程对象
            Process cur = Process.GetCurrentProcess();
            //为获取当前进程使用的内存大小做准备
            PerformanceCounter curpc = new PerformanceCounter("Process", "Working Set", cur.ProcessName);
            string memoryUsedSize = "";
            try
            {
                ValueEventArgs e;
                int result;

                //根据当前进程使用内存的大小,决定是否继续分析日志文本
                memoryUsedSize = string.Format("分析开始,本进程使用内存大小:{0} KB,Date:{1}", curpc.NextValue() / 1024, DateTime.Now);
                streamOutput.Write(System.Text.UTF8Encoding.UTF8.GetBytes(memoryUsedSize), 0, System.Text.UTF8Encoding.UTF8.GetBytes(memoryUsedSize).Length);
                streamOutput.WriteByte(13); //换行符


                while ((result = streamInput.ReadByte()) != -1)
                {

                    if (StartFlag == false)
                    {
                        streamOutput.Write(System.Text.UTF8Encoding.UTF8.GetBytes("强制停止分析"), 0, System.Text.UTF8Encoding.UTF8.GetBytes("强制停止分析").Length);
                        streamOutput.WriteByte(13); 
                        break;
                    }

                    if (result == 10)
                        continue;
                    iRowCharCount++;
                    rowByteData.Add((byte)result);
                    if (result == 13)  //一行
                    {

                        //写入一次或者处理一次
                        rowStr = GetSpecificInfoFromLineText(rowByteData, paramsInfo.separator, paramsInfo.columnNum);
                        if (!string.IsNullOrEmpty(rowStr))
                        {
                            rowStr = rowStr.Length > 300 ? rowStr.Substring(0, 300) : rowStr;
                            if (tongjiDict.ContainsKey(rowStr))
                                tongjiDict[rowStr] = tongjiDict[rowStr] + 1;
                            else
                                tongjiDict[rowStr] = 1;
                        }                     

                        if (iRowCount % 10000 == 0 || iRowCount<100) //不频繁的更新UI可以极大的提高处理的效率,如果每条都更新UI,将会非常慢
                        {
                            //占用内存大于1GB,则结束本次的分析
                            if (curpc.NextValue() > ALLOW_MAX_USED_MEMORY)//当前进程使用内存的大小大于1个GB,停止分析
                            {
                                break;
                            }

                            e = new ValueEventArgs() { Value = iRowCount };
                            this.OnValueChange(e);
                        }

                        iRowCount++; //统计处理的行数
                        iRowCharCount = 0;//本行的字符数
                        rowByteData.Clear();//清空本行数据
                    }
                }

                if (tongjiDict.Count> 1)
                {
                    //根据当前进程使用内存的大小,决定是否继续分析日志文本
                    memoryUsedSize = string.Format("分析结束:本进程使用内存大小:{0} KB,Date:{1},分组个数:{2}", curpc.NextValue() / 1024, DateTime.Now,tongjiDict.Count);
                    streamOutput.Write(System.Text.UTF8Encoding.UTF8.GetBytes(memoryUsedSize), 0, System.Text.UTF8Encoding.UTF8.GetBytes(memoryUsedSize).Length);
                    streamOutput.WriteByte(13); //换行符
                }

                streamOutput.Write(System.Text.UTF8Encoding.UTF8.GetBytes("本次处理的文本对象是"), 0, System.Text.UTF8Encoding.UTF8.GetBytes("本次处理的文本对象是").Length);
                streamOutput.Write(System.Text.UTF8Encoding.UTF8.GetBytes(paramsInfo.inputPath), 0, System.Text.UTF8Encoding.UTF8.GetBytes(paramsInfo.inputPath).Length);
                streamOutput.WriteByte(13);
                
                string temLine;
                foreach (var item in tongjiDict.OrderByDescending(t => t.Value))
                {
                    temLine = string.Format("统计次数Value:{0}	 Key: {1}", item.Value, item.Key);
                    streamOutput.Write(System.Text.UTF8Encoding.UTF8.GetBytes(temLine), 0, System.Text.UTF8Encoding.UTF8.GetBytes(temLine).Length);
                    streamOutput.WriteByte(13); //换行符
                }
                //更新处理到最后一条的文字提示状态
                e = new ValueEventArgs() { Value = iRowCount };
                this.OnValueChange(e);
            }
            finally
            {
                streamInput.Dispose();
                streamOutput.Dispose();
            }

            return iRowCount;
        }

        //从文本行中提取特定信息
        private string GetSpecificInfoFromLineText(List<byte> lineArr, string separator, int columnNum)
        {
            string result;
            try
            {
                string lineStr;
                string[] columnArr;
                lineStr = System.Text.UTF8Encoding.UTF8.GetString(lineArr.ToArray());
                //把文本中的 "	",替换为分隔符 "\\t",原因是:输入的分隔符是:“	”,为了避免被转移,系统自动把输入的分隔符变成了:“\\t”
                //去掉
后面或者前面的
,避免输出的文本中根据
换行
                columnArr = lineStr.Replace("	", "\\t").Replace(‘
‘, ‘ ‘).Split(new string[] { separator }, StringSplitOptions.None);
                if (columnArr.Length < columnNum)
                {
                    return "";
                }
                result = columnArr[columnNum - 1];
            }
            catch (Exception)
            {
                result = "ExceptionLine";
                //throw;
            }
            return result;
        }

    }

    public struct TongjiParamsInfoStruct
    {
        public string inputPath { get; set; }
        public string outputPath { get; set; }
        public string separator { get; set; }
        public int columnNum { get; set; }

    }
}

  调用代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace DaZhongLogTool
{
    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
        }

        Color originalTongJiButtonColor;
        string originalTongJiButtonText;

        BigFileTongJiJobs tongjiJobs = new BigFileTongJiJobs();

        private void btnTongJi_Click(object sender, EventArgs e)
        {
            if (tongjiJobs.StartFlag)
            {
                MessageBox.Show("正在处理中...,如需停止,请单击停止");
                return;
            }

            string errmsg;
            if(string.IsNullOrEmpty(txtSeparator.Text))
            {
                MessageBox.Show("请输入分隔符");
                return;
            }
            if (numericUpDown1.Value<1)
            {
                MessageBox.Show("请输入按照分隔符分割的待统计内容的对应的列数,从1开始");
                return;
            }

            string inputPath = txtInputPath.Text.Trim();
            if (string.IsNullOrEmpty(inputPath))
            {
                MessageBox.Show("请输入等待统计的文本路径");
                return;
            }
            if(!File.Exists(inputPath))
            {
                MessageBox.Show("待统计的文本文件不存在,请重新输入");
                return;
            }


            TongjiParamsInfoStruct paramsInfo = new TongjiParamsInfoStruct();
            paramsInfo.inputPath = inputPath;
            paramsInfo.outputPath = System.IO.Path.GetDirectoryName(inputPath)[email protected]""+DateTime.Now.ToString("yyyyMMdd_HHmm")+"_result.log";
            paramsInfo.separator = txtSeparator.Text;
            paramsInfo.columnNum = (int)numericUpDown1.Value;

            originalTongJiButtonColor = this.btnTongJi.BackColor;
            originalTongJiButtonText = this.btnTongJi.Text;

            ////开始分析前,改变按钮颜色及文字
            //this.btnTongJi.Enabled = false;
            //this.btnTongJi.BackColor = Color.Gray;
            //this.btnTongJi.Text = "处理中……";

            tongjiJobs.StartFlag = true;
            tongjiJobs.ValueChanged += new ValueChangedEnentHandler(Line_ValueChange);

            Func<TongjiParamsInfoStruct, int> hander = new Func<TongjiParamsInfoStruct, int>(tongjiJobs.StartAnalyseBigFile);
            hander.BeginInvoke(paramsInfo, new AsyncCallback(AsyncCallback1), hander);
           
        }

        // 结束异步操作
        private void AsyncCallback1(IAsyncResult ar)
        {
            // 标准的处理步骤
            Func<TongjiParamsInfoStruct, int> handler = ar.AsyncState as Func<TongjiParamsInfoStruct, int>;
            int result= handler.EndInvoke(ar);

            if (result>0)
            {
                MessageBox.Show("本次成功处理了" + result + "行数据", "成功");
            }
            else if (result == -2)
            {
                MessageBox.Show("文件不存在,请重新选择");
            }
            toolStripStatusLabel1.Text = "上次任务处理完毕,等待下次开始。" + DateTime.Now.ToString();

            tongjiJobs.StartFlag = false;//处理过程停止

            //恢复按钮颜色及文字
            //this.btnTongJi.Enabled = true;
            //this.btnTongJi.BackColor = originalTongJiButtonColor;
            //this.btnTongJi.Text = originalTongJiButtonText;

        }


        private void Line_ValueChange(object sender ,ValueEventArgs e)
        {
            toolStripStatusLabel1.Text = string.Format("统计中……,已处理了{0}行日志,时间:{1}", e.Value, DateTime.Now);
        }  


        private void btnSelectFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.Multiselect = false;
            fileDialog.Filter = "(*.*)|*.*";
            fileDialog.RestoreDirectory = false;

            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                  txtInputPath.Text=fileDialog.FileName;
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
        }

        private void btnStopTongJi_Click(object sender, EventArgs e)
        {
            //点击停止按钮
            tongjiJobs.StartFlag = false; 
        }
       

    }




   
}

  该工具,是来自于实际工作的需求,用于根据某一列统计次数。简单实用。

 源代码下载:大文件分组统计简单工具

需要的小伙伴尽管拿走,不要忘记推荐一下,谢谢

以上是关于大文件可使用的文本分组统计工具(附带源码,原创)的主要内容,如果未能解决你的问题,请参考以下文章

原创Android 耗电信息统计服务——BatteryStats源码分析

每天存储数百万条可分组用于统计目的的数据的最佳方式是啥?

多环境多需求并行下的代码测试覆盖率统计工具实现

201671030119 词频统计软件项目报告

定做logo设计请人设计原创商标代设计品牌公司企业VI代设计卡通图标志定制定做logo海报代做平面包装宣传单宣传

基于文本图形(ncurses)的文本搜索工具 ncgrep