テキストファイル内の行数をプログラムで簡単に決定する方法はありますか?
真剣に遅らせられた編集:.NET 4.0以降を使用している場合
File
クラスに新しい ReadLines
メソッドが追加されました。このメソッドは、ReadAllLines
のような配列にすべてを貪欲に読み込むのではなく、遅延して行を列挙します。そのため、次の機能で効率と簡潔さの両方を実現できます。
var lineCount = File.ReadLines(@"C:\file.txt").Count();
元の回答
効率があまり気にならなければ、次のように書くことができます。
var lineCount = File.ReadAllLines(@"C:\file.txt").Length;
より効率的な方法については、次のことができます。
var lineCount = 0;
using (var reader = File.OpenText(@"C:\file.txt"))
{
while (reader.ReadLine() != null)
{
lineCount++;
}
}
編集:効率性に関する質問への回答
2番目の方が効率的だと言った理由は、必ずしも速度ではなく、メモリ使用量に関するものでした。最初のものはファイルの内容全体を配列にロードします。つまり、少なくともファイルのサイズと同じだけのメモリを割り当てる必要があります。 2番目は、一度に1行ずつループするだけなので、一度に複数行のメモリを割り当てる必要はありません。これは小さなファイルではそれほど重要ではありませんが、大きなファイルでは問題になる可能性があります(たとえば、32ビットシステムで4 GBのファイルの行数を見つけようとすると、単に十分ではありません)この大きさの配列を割り当てるユーザーモードのアドレス空間)。
速度の面では、そこに多くのものがあるとは思わないでしょう。 ReadAllLinesには内部で最適化が行われている可能性がありますが、一方で、大量のメモリを割り当てる必要がある場合があります。 ReadAllLinesは小さなファイルでは高速ですが、大きなファイルでは大幅に遅くなると思います。ただし、判断する唯一の方法は、ストップウォッチまたはコードプロファイラーで測定することです。
最も簡単:
int lines = File.ReadAllLines("myfile").Length;
これはより少ないメモリを使用しますが、おそらくより時間がかかります
int count = 0;
string line;
TextReader reader = new StreamReader("file.txt");
while ((line = reader.ReadLine()) != null)
{
count++;
}
reader.Close();
簡単に言うと、解読は簡単ですが、偶然では非効率なコード行を意味しますか?
string[] lines = System.IO.File.RealAllLines($filename);
int cnt = lines.Count();
これはおそらく、行数を知る最も簡単な方法です。
また、それを行うことができます(バッファリングするかどうかによって異なります)
#for large files
while (...reads into buffer){
string[] lines = Regex.Split(buffer,System.Enviorment.NewLine);
}
他にも多くの方法がありますが、上記のいずれかがおそらくあなたが行くでしょう。
テキストを何もせずにループを使用してインクリメントするだけで、すぐに読み込み、カウンタをインクリメントできます。
キャリッジリターン/ラインフィードをカウントします。ユニコードでは、それぞれ0x000Dと0x000Aであると信じています。こうすることで、必要なだけ効率的または非効率的になり、両方の文字を処理する必要があるかどうかを決定できます
ファイルの読み込みにはそれ自体で時間がかかりますが、改行文字をカウントするためだけにファイル全体を読み込む場合、結果のガベージコレクションは別の問題です。
ある時点で、フレームワークであるか、それがコードであるかに関係なく、誰かがファイル内の文字を読み取らなければなりません。これは、ファイルが大きい場合、ファイルを開いてメモリに読み込む必要があることを意味します。これは、メモリをガベージコレクションする必要があるため、潜在的に問題になる可能性があります。
Nima Araはあなたが考慮に入れるかもしれない素晴らしい分析をしました
提案されたソリューションは、一度に4文字を読み取り、改行文字をカウントし、次の文字比較のために同じメモリアドレスを再利用するため、提案されたソリューションです。
private const char CR = '\r';
private const char LF = '\n';
private const char NULL = (char)0;
public static long CountLinesMaybe(Stream stream)
{
Ensure.NotNull(stream, nameof(stream));
var lineCount = 0L;
var byteBuffer = new byte[1024 * 1024];
const int BytesAtTheTime = 4;
var detectedEOL = NULL;
var currentChar = NULL;
int bytesRead;
while ((bytesRead = stream.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
{
var i = 0;
for (; i <= bytesRead - BytesAtTheTime; i += BytesAtTheTime)
{
currentChar = (char)byteBuffer[i];
if (detectedEOL != NULL)
{
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 1];
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 2];
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 3];
if (currentChar == detectedEOL) { lineCount++; }
}
else
{
if (currentChar == LF || currentChar == CR)
{
detectedEOL = currentChar;
lineCount++;
}
i -= BytesAtTheTime - 1;
}
}
for (; i < bytesRead; i++)
{
currentChar = (char)byteBuffer[i];
if (detectedEOL != NULL)
{
if (currentChar == detectedEOL) { lineCount++; }
}
else
{
if (currentChar == LF || currentChar == CR)
{
detectedEOL = currentChar;
lineCount++;
}
}
}
}
if (currentChar != LF && currentChar != CR && currentChar != NULL)
{
lineCount++;
}
return lineCount;
}
上記のように、ラインフィードを表示するにはすべての文字を読み取る必要があるため、基礎となるフレームワークによって1行が1文字ずつ読み取られることがわかります。
完成したNimaとしてプロファイルを作成すると、これがかなり高速で効率的な方法であることがわかります。
実行可能なオプション、および私が個人的に使用したオプションは、ファイルの最初の行に独自のヘッダーを追加することです。私は自分のゲームのカスタムモデル形式でこれを行いました。基本的に、.objファイルを最適化し、不要ながらくたを取り除き、それらをより良いレイアウトに変換し、ライン、面、法線、頂点、およびテクスチャUVの合計数を書き込むツールがあります最初の行。そのデータは、モデルがロードされるときにさまざまな配列バッファーによって使用されます。
これは、行をカウントするために1回ではなく、ファイルを1回ループしてロードするだけでよく、作成されたバッファーにデータを再度読み込む必要があるため、便利です。