- PR -

【C#】単一テキストファイルから重複行削除

投稿者投稿内容
ちづる
ベテラン
会議室デビュー日: 2004/07/27
投稿数: 72
投稿日時: 2006-06-14 10:01
引用:

夏椰さんの書き込み (2006-06-14 09:13) より:
なんか勘違いされている気がしました・・・・。

Stringの変数を+=で連結していくことが問題であり、
改行コードを連結することが問題ではありません。
#よって
# strData += "rn";だけでなく
# strData += dr.GetValue(0); も問題なんですよ。

理由については R・田中一郎さんがフォローしてくれています。

また、なちゃさんもおっしゃっていますが、
変数にためる必要性を今一度、ご考慮くださいませ。





あっ!
す、すみません。大きく勘違いしていました(汗

>がらす様
なるほど・・・
StringBuilderが早いというのは知っていたのですが、何がどう早いのか
理解していませんでした・・・。
>StringBuilderはあらかじめ大き目のメモリを確保しておいて
とのことですが、例えばメモリが256MB程度しか積んでいないPCの場合は、
StringBuilderが先にメモリの確保をすると、一瞬PCが遅くなったりしないのでしょうか?
StringBuilderに限った事ではないと思うのですが、どのタイミングでメモリの解放が
行われるのかな、と。
明示的にメモリ解放ってこの場合できたりするんでしょうか?
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2006-06-14 10:20
メモリの解放の話は過去スレもけっこうありますし、他の方に任せるとして…

他の方も仰っているように、今回のケースでは、StringBuilderすら使う必要はないと思います。
単純にStreamWriterのWriteLineでどんどん書いていけばいいと思いますよ?

あと、直接時間を縮める処理ではありませんが、処理が「固まっていない」ことがわかり、かつ「今どれくらい進んでいるか」がわかれば、人間は以外と待てるものです。

なので、もし時間がかかるようなら、最初に
select count(*) from (select distinct * from textfile.txt) AS A
みたいなSQLで全体の件数を取得しておいて、365/10000 みたいなラベルを表示したり、プログレスバーを表示したりするのも一つの手だと思います。

ご参考
DOBON.NET .NET Tips - 時間のかかる処理の進行状況を表示する
DOBON.NET .NET Tips - 時間のかかる処理をユーザーが停止できるようにする

[ メッセージ編集済み 編集者: ぼのぼの 編集日時 2006-06-14 10:28 ]
ちづる
ベテラン
会議室デビュー日: 2004/07/27
投稿数: 72
投稿日時: 2006-06-14 10:37
引用:

ぼのぼのさんの書き込み (2006-06-14 10:20) より:
メモリの解放の話は過去スレもけっこうありますし、他の方に任せるとして…

他の方も仰っているように、今回のケースでは、StringBuilderすら使う必要はないと思います。
単純にStreamWriterのWriteLineでどんどん書いていけばいいと思いますよ?

あと、直接時間を縮める処理ではありませんが、処理が「固まっていない」ことがわかり、かつ「今どれくらい進んでいるか」がわかれば、人間は以外と待てるものです。

なので、もし時間がかかるようなら、最初に
select count(*) from (select distinct * from textfile.txt) AS A
みたいなSQLで全体の件数を取得しておいて、365/10000 みたいなラベルを表示したり、プログレスバーを表示したりするのも一つの手だと思います。

ご参考
DOBON.NET .NET Tips - 時間のかかる処理の進行状況を表示する
DOBON.NET .NET Tips - 時間のかかる処理をユーザーが停止できるようにする

[ メッセージ編集済み 編集者: ぼのぼの 編集日時 2006-06-14 10:28 ]


StringBuilderを使用しないで直接書き込む処理に変更したら
ものすごく早くなりました!
何か処理する→変数に入れる→それを書き込む、といった考えをする癖があり、
何の迷いもなくそうしていました…(恥)。
人間が待てるレベルの速さになったのですが、念のためプログレスバーなどの
処理も考えてみます。
まだまだ課題が沢山(これを応用して、単一ファイルではなく複数ファイルから読込、重複削除や、指定した文字列を同時に削除など)あるのですが、大きな壁がクリアできて
本当に嬉しいです!

じゃんぬねっと様、がうしぇ様、なちゃ様ぼのぼの様、夏椰様、R・田中一郎様、がらす様
ありがとうございました!!
k_kazu
常連さん
会議室デビュー日: 2006/02/11
投稿数: 25
投稿日時: 2006-06-15 10:13
解決済みになってしまいましたが、ぼのぼのさんの提案「(1) Hashtable を使う方法」を使う方法が議論されていないのでサンプルを提供します。
私の環境では、ファイルサイズ 10MByte 12万行が3・4秒で終了します。

コード:
     StreamReader sr = null;
     StreamWriter sw = null;
     try {
         sr = new StreamReader(inFile, Encoding.GetEncoding(932));
         sw = new StreamWriter(outFile,false,Encoding.GetEncoding(932));

         Hashtable uniqHash = new Hashtable();// 過去に出現した文字列を格納
         string readString;
         while ((readString = sr.ReadLine()) != null)
         {
             if (uniqHash[readString] == null)
             {
                 // 過去に同じ文字列が現れていない
                 uniqHash[readString] = true;
                 sw.WriteLine(readString);
             }
         }
         sr.Close();
         sw.Flush();
         sw.Close();
     }
     finally{
         if (sr != null) sr.Dispose();
         if (sw != null) sw.Dispose();
     }


メモリ内に収まるサイズであればHashtableを使うと良いと思います。
メモリに収まらないような数ギガのファイルは、ファイルを分割してマージソート&重複する文字の削除を行うのが良いと思います。
ちづる
ベテラン
会議室デビュー日: 2004/07/27
投稿数: 72
投稿日時: 2006-06-15 11:47
引用:

k_kazuさんの書き込み (2006-06-15 10:13) より:
解決済みになってしまいましたが、ぼのぼのさんの提案「(1) Hashtable を使う方法」を使う方法が議論されていないのでサンプルを提供します。
私の環境では、ファイルサイズ 10MByte 12万行が3・4秒で終了します。

コード:
     StreamReader sr = null;
     StreamWriter sw = null;
     try {
         sr = new StreamReader(inFile, Encoding.GetEncoding(932));
         sw = new StreamWriter(outFile,false,Encoding.GetEncoding(932));

         Hashtable uniqHash = new Hashtable();// 過去に出現した文字列を格納
         string readString;
         while ((readString = sr.ReadLine()) != null)
         {
             if (uniqHash[readString] == null)
             {
                 // 過去に同じ文字列が現れていない
                 uniqHash[readString] = true;
                 sw.WriteLine(readString);
             }
         }
         sr.Close();
         sw.Flush();
         sw.Close();
     }
     finally{
         if (sr != null) sr.Dispose();
         if (sw != null) sw.Dispose();
     }


メモリ内に収まるサイズであればHashtableを使うと良いと思います。
メモリに収まらないような数ギガのファイルは、ファイルを分割してマージソート&重複する文字の削除を行うのが良いと思います。



サンプルありがとうございます。
その後いろいろ処理を追加(読み込んだファイルからNGリストに載っている文字列を削除、複数ファイルからの重複削除)してみたところ、
テーブルで行う場合とハッシュで行う場合、ハッシュのほうが早い事が判明しました。
現状は、ソート機能も必要なので、ソートしない→HushTable、ソートする→SortedList
で分けて処理をしようかな、と思っています。

ちなみになのですが、Hushtableから、Keyのみを全部取得する場合はどうすればよいのでしょうか?
いろんなサイトを見てみましたが、KeyとValueが1組で取得したりようで…。
SortedListにあるGetKey()のような機能はHushTableにはないのでしょうか…。
むら
会議室デビュー日: 2006/04/11
投稿数: 11
お住まい・勤務地: さっぽろ
投稿日時: 2006-06-15 13:48
こんにちは。

Hushtableから、Keyのみを全部取得する場合は・・
例えば以下のような感じで取得することが可能ですよ。

-------------------------------------------------------
Private htb As New Hashtable
(いろいろ省略)
Dim ide As System.Collections.IDictionaryEnumerator = htb.Keys.GetEnumerator
While ide.MoveNext
Console.Write(DirectCast(ide.Current, String))
End While
-------------------------------------------------------

#VBで書いてしもた・・
#このスレは結構勉強になりました。

[ メッセージ編集済み 編集者: むら 編集日時 2006-06-15 13:50 ]
ちづる
ベテラン
会議室デビュー日: 2004/07/27
投稿数: 72
投稿日時: 2006-06-15 15:49
またも問題が・・・
ソートリスト使用時、10万件のデータで
sr = new StreamReader(ifn[i].ToString(),Encoding.GetEncoding("Shift_JIS")) ;
string rl ;
while ((rl = sr.ReadLine()) != null)
{
if(!sortlist.ContainsKey(rl))
{
sortlist[rl]="OK";
//sw.Write(sortlist.ToString() );
//sw.Write( "\r\n" );
}
上記の処理で50秒近くかかる事が判明しました…
本当はHushTableでソートできれば一番いいのですが、どうもできないようなので
ソートリストを使ってみたのですが…。
仮想テーブルを使用したほうが早いのですが、NGリスト除去処理を行おうとして

string sql = "SELECT DISTINCT * FROM textfile.txt EXCEPT SELECT DISTINCT * FROM ngfile.txt";

上記のようなSQLを投げるとエラーが帰ってきてしまいます。
(UNION系は大丈夫でした。INTERSECT 、EXCEPTだとエラーになるようです)

一難去ってまた一難です…

今川 美保(夏椰)
ぬし
会議室デビュー日: 2004/06/10
投稿数: 363
お住まい・勤務地: 神奈川県茅ヶ崎市
投稿日時: 2006-06-15 16:08
SELECT DISTINCT * FROM textfile.txt where f1 not in (
SELECT f1 FROMngfile.txt)
こんなSQLでいけるんじゃないでしょうか?


_________________
夏椰 @ わんくま同盟
夏椰の庵
Microsoft MVP for Windows Server System - SQL Server ( Jul 2006 - Jun 2008 )

スキルアップ/キャリアアップ(JOB@IT)