Symfoware

Symfowareについての考察blog

大きなファイルの指定範囲の行だけ表示するアプリケーション

巨大なログファイルの一部だけ表示したいことがあったので、
C# + WPF + XAMLのお勉強がてら作ってみました。


本体のソースはこんな感じ。


  1. using System;
  2. using System.Data;
  3. using System.Text;
  4. using System.IO;
  5. using System.Xml;
  6. using System.Windows;
  7. using System.Windows.Markup;
  8. using System.Windows.Controls;
  9. using System.Windows.Data;
  10. using System.Threading;
  11. using System.ComponentModel;
  12. using Microsoft.VisualBasic.FileIO;
  13. public class EntryPoint {
  14.     [STAThread]
  15.     static void Main(string[] args) {
  16.     
  17.         MainWindow mainWin;
  18.     
  19.         // 作成したwin.xamlを読み込んで、Windowsオブジェクトを取得
  20.         using (FileStream infs = new FileStream("win.xaml", FileMode.Open)) {
  21.             XmlReader xmlReader = XmlReader.Create(infs);
  22.             mainWin = new MainWindow((Window)XamlReader.Load(xmlReader));
  23.         }
  24.         
  25.         // Window表示
  26.         var app = new Application();
  27.         app.Run(mainWin.GetWindow());
  28.     }
  29. }
  30. public class MainWindow {
  31.     private Window win;
  32.     private string fileName = "";
  33.     private int totalCount = 0;
  34.     
  35.     public MainWindow(Window win) {
  36.         this.win = win;
  37.         
  38.         //ファイル選択ボタン
  39.         ((Button)win.FindName("file_select")).Click += new RoutedEventHandler(FileSelect_Click);
  40.         //表示ボタン
  41.         ((Button)win.FindName("file_open")).Click += new RoutedEventHandler(FileOpen_Click);
  42.     }
  43.     
  44.     public Window GetWindow() {
  45.         return win;
  46.     }
  47.     
  48.     //表示ボタン
  49.     public void FileOpen_Click(object sender, RoutedEventArgs e) {
  50.         
  51.         if (totalCount == 0) {
  52.             MessageBox.Show("読み込むファイルがありません。");
  53.             return;
  54.         }
  55.         
  56.         int readStart = GetInputNumber("read_start", "開始");
  57.         if (readStart <= 0) {
  58.             return;
  59.         }
  60.         int readEnd = GetInputNumber("read_end", "終了");
  61.         if (readEnd <= 0) {
  62.             return;
  63.         }
  64.         
  65.         if (readEnd < readStart){
  66.             MessageBox.Show("開始≦終了となるよう指定してください。");
  67.         }
  68.         
  69.         if (totalCount < readEnd) {
  70.             MessageBox.Show("終了にファイルの行数以上の値が指定されています。");
  71.         }
  72.         
  73.         
  74.         //指定範囲の行を読み込み
  75.         StringBuilder sb = new StringBuilder();
  76.         using (var reader = new StreamReader(fileName, GetEncoding())) {
  77.             
  78.             string line;
  79.             int count = 0;
  80.             
  81.             while((line = reader.ReadLine()) != null){
  82.                 count++;
  83.                 if (readEnd < count) {
  84.                     break;
  85.                 }
  86.                 if (count < readStart) {
  87.                     continue;
  88.                 }
  89.                 sb.Append(line);
  90.                 sb.Append("\n");
  91.             }
  92.         }
  93.         
  94.         //テキストボックスに表示
  95.         TextBox textBox = (TextBox)win.FindName("result_text");
  96.         textBox.Text = sb.ToString();
  97.         
  98.         
  99.         //ListViewに表示
  100.         DataTable table = new DataTable();
  101.         GridView grdView = new GridView();
  102.         grdView.AllowsColumnReorder = true;
  103.         
  104.         Stream stream = new MemoryStream(GetEncoding().GetBytes(sb.ToString()));
  105.         using (TextFieldParser parser = new TextFieldParser(stream, GetEncoding())) {
  106.             parser.TextFieldType = FieldType.Delimited;
  107.             parser.SetDelimiters(",");
  108.             
  109.             bool isFirst = true;
  110.             int count = readStart;
  111.             while (!parser.EndOfData){
  112.                 string[] data = parser.ReadFields();
  113.                 if (isFirst) {
  114.                     CreateColumns(grdView, table, data);
  115.                     isFirst = false;
  116.                 }
  117.                 
  118.                 DataRow row = table.NewRow();
  119.                 row[0] = count;
  120.                 for (int i = 0; i < data.Length; i++) {
  121.                     row[i+1] = data[i];
  122.                 }
  123.                 
  124.                 table.Rows.Add(row);
  125.                 count++;
  126.             }
  127.         }
  128.         
  129.         ListView listView = (ListView)win.FindName("result_grid");
  130.         listView.View = grdView;
  131.         listView.ItemsSource = table.DefaultView;
  132.         
  133.     }
  134.     
  135.     private void CreateColumns(GridView grdView, DataTable table, string[] data) {
  136.         GridViewColumn column = new GridViewColumn();
  137.         column.DisplayMemberBinding = new Binding("No");
  138.         column.Header = "No";
  139.         grdView.Columns.Add(column);
  140.         
  141.         table.Columns.Add(new DataColumn("No"));
  142.         
  143.         for (int i = 0; i < data.Length; i++) {
  144.             column = new GridViewColumn();
  145.             column.DisplayMemberBinding = new Binding(i.ToString());
  146.             column.Header = i.ToString();
  147.             grdView.Columns.Add(column);
  148.             
  149.             table.Columns.Add(new DataColumn(i.ToString()));
  150.         }
  151.     }
  152.     
  153.     
  154.     private int GetInputNumber(string name, string title) {
  155.         TextBox textBox = (TextBox)win.FindName(name);
  156.         int ret;
  157.         if (!int.TryParse(textBox.Text, out ret)) {
  158.             MessageBox.Show(title + "に数値を入力してください。");
  159.             return -1;
  160.         }
  161.         if (ret <= 0) {
  162.             MessageBox.Show(title + "は自然数を入力してください。");
  163.             return -1;
  164.         }
  165.         
  166.         return ret;
  167.     }
  168.     
  169.     
  170.     //ファイル選択ボタン
  171.     public void FileSelect_Click(object sender, RoutedEventArgs e) {
  172.         var dlg = new Microsoft.Win32.OpenFileDialog();
  173.         if (dlg.ShowDialog() == false) {
  174.             return;
  175.         }
  176.         TextBox textBox = (TextBox)win.FindName("file_name");
  177.         textBox.Text = dlg.FileName;
  178.         fileName = dlg.FileName;
  179.         
  180.         totalCount = 0;
  181.         using (var reader = new StreamReader(dlg.FileName, GetEncoding())) {
  182.             
  183.             while(reader.ReadLine() != null){
  184.                 totalCount++;
  185.             }
  186.         }
  187.         Label lbl = (Label)win.FindName("file_length");
  188.         lbl.Content = totalCount + "行";
  189.     }
  190.     
  191.     //コンボボックスで選択しているエンコーディングを取得
  192.     private Encoding GetEncoding() {
  193.         ComboBox comboBox = (ComboBox)win.FindName("encoding");
  194.         return Encoding.GetEncoding(comboBox.Text);
  195.     }
  196.     
  197.     
  198. }




画面を構成するxamlはこんな感じ。


  1. <Window
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. Title="指定行テキストリーダー"
  5. Width="500"
  6. Height="500"
  7. FontSize="16"
  8. WindowStartupLocation="CenterScreen"
  9. >
  10.     <DockPanel>
  11.         <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="10,5,10,5">
  12.             <Label>ファイルの文字コード:</Label>
  13.             <ComboBox Name="encoding" SelectedIndex="0">
  14.                 <ComboBoxItem>utf-8</ComboBoxItem>
  15.                 <ComboBoxItem>utf-16</ComboBoxItem>
  16.                 <ComboBoxItem>shift_jis</ComboBoxItem>
  17.                 <ComboBoxItem>EUC-JP</ComboBoxItem>
  18.             </ComboBox>
  19.         </StackPanel>
  20.         <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="10,5,10,5">
  21.             <Label>ファイル:</Label>
  22.             <TextBox Name="file_name" Width="260" IsReadOnly="True" />
  23.             <Button Name="file_select" Content="選択" />
  24.             <Label Name="file_length" Margin="10,0,10,0" />
  25.         </StackPanel>
  26.         <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="10,5,10,5">
  27.             <Label>表示行数:</Label>
  28.             <TextBox Name="read_start" Width="80" />
  29.             <Label>行から</Label>
  30.             <TextBox Name="read_end" Width="80" />
  31.             <Label>行まで</Label>
  32.             <Button Name="file_open" Content="表示" />
  33.         </StackPanel>
  34.         <TabControl DockPanel.Dock="Top">
  35.             <TabItem Header="テキスト">
  36.                 <TextBox Name="result_text" AcceptsReturn="True"
  37.                      VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" />
  38.             </TabItem>
  39.             <TabItem Header="グリッド">
  40.                 <ListView Name="result_grid">
  41.                 </ListView>
  42.             </TabItem>
  43.         </TabControl>
  44.     </DockPanel>
  45.     
  46. </Window>




ビルドはこんなバッチファイルで行いました。


  1. @echo off
  2. if "%1"=="3" (
  3.     goto NET3
  4. )
  5. echo .NET 4
  6. set csc="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe"
  7. set opt=/nologo /r:WPF\PresentationCore.dll;WPF\PresentationFramework.dll;WPF\WindowsBase.dll;System.Xaml.dll;Microsoft.VisualBasic.dll
  8. set opt=%opt% /target:winexe
  9. %csc% %opt% BigFileReader.cs
  10. goto END
  11. :NET3
  12. echo .NET 3
  13. set csc="C:\Windows\Microsoft.NET\Framework64\v3.5\csc.exe"
  14. set opt=/nologo /r:PresentationCore.dll;PresentationFramework.dll;WindowsBase.dll;Microsoft.VisualBasic.dll
  15. set opt=%opt% /target:winexe
  16. %csc% %opt% BigFileReader.cs
  17. :END





使い方は簡単。
起動したら読み込むファイルの文字コードをコンボボックスから選びます。
「選択」ボタンを押して、読み込むファイルを指定。

184_01.png


サンプルとして、郵便番号データを読み込んでみます。
http://www.post.japanpost.jp/zipcode/download.html

ファイルを選択すると、行数が表示されます。
表示したい範囲を入力して「表示」をクリック。

184_02.png


テキストそのまま表示と、

184_03.png


CSVファイルとして解析した結果をグリッドで表示します。

184_04.png


WPFのListViewへのデータバインディングのサンプルとして、
ご参考になればと。


備忘録がてら、ソースを公開しておきました。
https://bitbucket.org/symfo/bigfilereader/src


また、ビルド済のexeをここからダウンロードできます。
https://bitbucket.org/symfo/bigfilereader/downloads

BigFileReader3.exeが.NET 3.5でビルドしたもの。
BigFileReader4.exeが.NET 4.0でビルドしたものになります。





【参考URL】

方法 : StackPanel にコンテンツを水平方向または垂直方向に配置する
http://msdn.microsoft.com/ja-jp/library/vstudio/ms752328.aspx

The WPF Tab Control - Inside and Out
http://tech.pro/tutorial/730/the-wpf-tab-control-inside-and-out

GridView in WPF
http://www.c-sharpcorner.com/uploadfile/mahesh/gridview-in-wpf/

[C#] CSVファイルやTSVファイルを読み込む (TextFieldParser利用)
http://www.ipentec.com/document/document.aspx?page=csharp-read-csv-file-by-textfieldparser

StreamとStringの変換
http://daisy64.blogspot.jp/2008/07/streamstring.html

方法 : ComboBox を作成する
http://msdn.microsoft.com/ja-jp/library/ms745118(v=vs.80).aspx

Encodingクラスで扱えるエンコーディング名は?[C#、VB]
http://www.atmarkit.co.jp/fdotnet/dotnettips/013enumenc/enumenc.html

Data Binding in WPF ListView
http://www.c-sharpcorner.com/UploadFile/raj1979/DataBinding10042007165025PM/DataBinding.aspx

関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2013/02/11(月) 18:18:26|
  2. 備忘録
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ