dポイントプレゼントキャンペーン実施中!

ファイル内の1行(レコード)から指定バイト数だけを読込みたい。

// ファイルオープン
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr;
isr = new InputStreamReader(fis);
BufferedReader result = new BufferedReader(isr);

while (result.readLine() != null) {
// 末尾まで移動する
}


上記のようにBufferedReaderを使い、ファイルを読み込んでいるのですが、
ファイル内の1行ごとに指定バイト数だけを読み込みたいです。
readLineメソッドでは1行を全部読み込んでしまうため出来ません。
(readLineで一度他の変数へ入れてからというのは無しです。読込む時点で指定バイト数としたいのです。)
readメソッドでは、行ごとの指定って出来ないですよね・・・。
ファイル操作系の知識が少ない為、なかなか探せません。

読み込む時点で1行の取得レコード長に制限値を設ける方法はないでしょうか?
また、取得レコード長が制限値を超えた場合、エラーメッセージを出したいのですがその判定とかも可能でしょうか?

どうかよろしくお願いします。



ロジックとしてはこんな感じにしたいです。

while(ファイルの終端まで行を読み込む){
  // 読み込んだ1行が2048バイト未満かを判定
  if(読み込んだ1行が2048バイトを超えている場合){
    エラーメッセージを出力する(処理はそのまま続行)
  }
  1行から2048バイト未満を取得
}

A 回答 (6件)

No.5の続きです。


補足にて示して頂いたコードで、

for (c = result.read(); c >= 0 && (char)c != '\r' ||
    c >= 0 && (char)c != '\n'; c = result.read()){
  // この部分はとりあえず適当に書いています。
  // 実際にはbreakするとループを抜けてしまうので次の行を見れません。
  break;
}

上記の部分を下記のように修正して頂ければ次の行、あるいはファイルの終端まで読み飛ばす処理になります。

for (c = result.read(); c >= 0 && (char)c != '\r' ||
    c >= 0 && (char)c != '\n'; c = result.read());

read()にて読み込んだ1文字を次々と変数cに上書きするので、結果として読み飛ばしていることになります。
下記記述の方がわかりやすかったかも知れません。

do {
  c = result.read();
} while(c >= 0 && (char)c != '\r' || c >= 0 && (char)c != '\n');

このループを抜けた時は、改行コードを読み取った直後かファイルの終端であるので、ファイルの終端でない場合は、次に read() した時が次の行の始めの文字を取得することになります。

skip(long n)を使用するには、次の改行コードまでの長さがどのくらいあるかがわかった上で使用しなければならないので、今回のような1文字ずつ読み込む方法には適さないと思います。

また、read(char[] cbuf, int off, int len) も、改行コードを超えて読み込んでしまった場合、超えた分のデータをどう処理するか考えなければならず、処理が複雑になってしまうのでやはり適さないと思います。

この回答への補足

あと、
「 c != '\n';」←この判定は
「(char)c != '\n';」としなくてもちゃんと判定してくれるのですね。

補足日時:2010/07/14 14:05
    • good
    • 0
この回答へのお礼

再度のアドバイスありがとうございます。
とても感謝しております。

後々、別のメソッドでこの読込んだBufferedReaderから文字列(レコード)を取得する時に、
2048バイト以降が読込まれていたら困ると思っていたのですが、
for分内での「c = result.read()」は結果的に読込まれていないのですねぇ。

skipの使用も、どう実現するか結構悩みました。見えそうで見えず…。やはり適さないのですね。
read(char[] cbuf, int off, int len)もいざ使用するとなると相当大変そうですね…。

あと、改行の「\r」「\n」の区別ですが
for分で「||」を付けると次の行ではなく、ファイルの終端まで行ってしまい、for分を抜けてしまいました。
「|」で試したらなぜか次の行を見てくれました。(下記の記述)

for (c = result.read(); c >= 0 && c != '\r' |
    c >= 0 && c != '\n'; c = result.read());


また、最初にアドバイスをいただいたコードを応用した、
下記のやり方の方が余分な改行を読込まなくてすむのでこちらのやり方の方がよさそうですね。

int c = -1;
do {
  int readBytes = 0;
  for (c = result.read(); c >= 0 && c != '\r' | c >= 0 && c != '\n'; c = result.read()) {
    readBytes++;
    System.out.println("char = " + (char)c); //確認
    if (readBytes > 4) {
      System.err.println("2048バイトを超える行を検出しました。");
      // ファイルの終わりか改行まで読み飛ばす。
      for (c = result.read(); c >= 0 && c != '\r' | c >= 0 && c != '\n'; c = result.read());
      break;
    }
  }
} while (c >= 0);

お礼日時:2010/07/14 14:00

行の区切りは改行コードを読み込むまでわかりません。


行ないたい処理は、2048バイト読み込んだかという条件を、改行コードを読み込む間での間に判定する必要があるので、行単位で処理をするのではなく、文字単位で処理をした方が良いと思いました。


BufferedReader br = new BufferedReader(
  new FileReader("読み込みたいファイル名のフルパス"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();

int c = -1;
do {
  int readBytes = 0;
  for (c = br.read(); c >= 0 && c != '\n'; c = br.read()) {
    readBytes++;
    if (readBytes > 2048) {
      System.err.println("2048バイトを超える行を検出しました。");
      //ファイルの終わりか改行まで読み飛ばす。
      for (c = br.read(); c >= 0 && c != '\n'; c = br.read());
      break;
    }
    baos.write(c);
  }
  System.out.println(baos.toString());
  baos.reset();
} while (c >= 0);

baos.close();
br.close();


この例ではファイルの終わりか改行コードが出現するまで、1文字ずつ読み込み都度バッファに書き込みます。
連続して読み込んだ文字が2048バイトを超えた時点でエラーメッセージを出力した後に、残りの文字を読み飛ばし、バッファへの書き込みをやめます。
次に、バッファへ書き込んだ内容を文字列として取得し、画面へ出力します。
上記の処理をファイルの終わりまで繰り返します。

出力先が何であるかがわからなかったため、今回はByteArrayOutputStreamを出力先に使用しましたが、OutputStreamやWriterにはwriteメソッドがあるので、他の出力先に応用できると思います。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
大変参考になります。

ですが、少々変わった点がございますので下記で回答いたします。
まず実際の処理ですが、一旦読込んでおいて、
後から別のメソッドで文字列を取得するという形になりました。
ですので、その際に1行に対して2048バイトを超えていたら、そこから改行までを読込まないようにしたいのです。
で、次の行からまた読み込みたいのです。(全レコードに対して2048バイト未満を読込む。はみ出た分は読込まない。)

簡単にですが、もう少し細かく説明しますと、下記のような感じです。
[ループ] (ファイルの終端まで1バイトずつ読込んでいく)
 2048バイト未満で改行コードがあったら(カウンターを0にでも初期化しておいて)、次の行を見に行く。
 1行を読込んでいる間に、2048バイトを超えていたらエラー出力し、処理はそのまま続行(はみ出た分は読み捨てる。というか読込まない。)
 (処理をそのまま続行というのは、ループは抜けずに次の行を見にいくということです)
 次の行を指定し、そこから読込めるようにする。
[ループ]

あと、改行コードですが実際には「\n」だけでなく「\r」もあるので両方の判定が必要になります。
一応下記で、参考にさせてもらいつつ、それらしいロジックを書いてみました。

疑問点としては、ロジックではなく言葉で言うと、
1行に対して2048バイトを超えていたら、その超えた部分から次の行までを読まずにスキップさせたい
↑このやり方が分かりません。
もしよろしければお知恵を拝借できないでしょうか?

skip()というメソッドを使うだろうとは予測しますが、それをどのように使えばいいかが分かりません。
あとは「read()」ではなく、「read(char[] cbuf, int off, int len)」の方を使うだろうと予測します。


InputStreamReader isr;
isr = new InputStreamReader(fis);
BufferedReader result = new BufferedReader(isr);

int c = 0; // 整数としての読み込まれた文字
int cnt = 0; // 行をカウント
final int BUFSIZE = 2048; // サイズ

while((c = result.read()) != -1){
  cnt++;
  // 改行コードが含まれているか判定
  if ((char)c == '\r' || (char)c == '\n') {
    // 改行コードが含まれているため、カウンターを初期化
    cnt = 0;
  }
  // 1行の読込み数を判定
  if (cnt > BUFSIZE) {
    // エラーメッセージを出力
    System.out.println("2048バイトを超える行を検出しました。");
    // ここで次の行まで読み捨てたい
    // つまり2048バイト未満だけを読込んだままにし、
    // 超えた分は読み飛ばし、次の行を見るようにしたい
    for (c = result.read();
    c >= 0 && (char)c != '\r' || c >= 0 && (char)c != '\n'; c = result.read()){
      // この部分はとりあえず適当に書いています。
      // 実際にはbreakするとループを抜けてしまうので次の行を見れません。
      break;
    }
    // カウンターの初期化
    cnt = 0;
  }
}

お礼日時:2010/07/13 16:12

ByteBufferじゃ駄目なんですか?



...
import java.nio.*;
...
private static final int BSIZE = 1024;
...
ByteBuffer buff = ByteBuffer.allocate(BSIZE);
...


http://oshiete.goo.ne.jp/qa/5981150.html


改行は portability を考慮した
public static final String NEWLINE = System.getProperty("line.separator");
を用いるのが、リファクタリング上、推奨されます。
    • good
    • 0
この回答へのお礼

ありがとうございます。
ByteBufferなるものがあるのですね。
これもまた詳しくみておきます。(ただ今回はBefferedReaderクラスを使います)

System.getProperty("line.separator");
↑これについては、実際の処理ではバイト単位で読込んでいるのに対し、
これは「\r\n」で取れてくる(String型)なので処理ができなくなってしまいます。

お礼日時:2010/07/13 13:22

えぇと.... コードとして書く前に, ロジックをきちんと考えていますか?


たとえば, while の中で最後の方にある
      int c = cbuf[currpos++];
      // cbufに改行を含む
      if (c == '\n') {
        startLine = true;
      }
の部分は「どのような動作をさせたい」と思って書いたのか, 説明してみてください.

この回答への補足

ご回答ありがとうございます。

もう少し詳しくロジックを書いてみました。(ただ、これも中途半端です)
やりたいことは質問に書いたとおりなのですが、
readメソッドを使った事が無く、また使用例も少ない為いまいちわかりにくいです。

どのように動作させたいかというと、
ファイル内の次の文字を読みに行き、
改行があればstartLineをtrueに設定し、
また次の行を見に行く・・・というようなことを書きたかったです。
最初にそのロジックを書いた時言ったように、実現の仕方がよく分からず、中途半端になってしまっています。
その書き方がよくわからないので、とりあえずのロジックを乗せて、よろしければヒントをもらえないでしょうか?という質問をしました。

単純にしたいのは
・ファイル内の1行を読込み時に2048バイト未満を読み込む。
 →2048バイトを超えていたらエラー出力し、そのまま処理を続行する。(2048バイト分は読込み、はみ出た分は読み捨てる)
・読み込んだ一行を取得する。
というだけなのですが・・・。

while (true) {
result.read(cbuf, currpos, BUFSIZE);
if (startLine) {
// 読み込んだのは行の開始
startLine = false;
}
int c = cbuf[currpos++];
// cbufに改行を含む
if (c == '\n') {
startLine = true;
// ここで次の行を見るようにするロジックを入れる?

}

//読み込んだ1行が2048バイトを超えていたらエラーを出したい。(処理は続行し、2048バイト分だけ読み込む)
//cbufの0番目から改行までを取得(やり方が分からない。ここで記述すべきじゃない?)

// EOFならば-1を返す。
if (c == -1) {
break;
}
}

補足日時:2010/07/12 19:50
    • good
    • 0

答えはreadメソッドよ。



readLineメソッドは内部で
改行までを読み込んでいるに過ぎないわ。

つまりロジック的には次のようになるわね。

boolean startLine = true;
while(ナニカ) {
read(buf, pos, len)
if (startLine) {
// TODO 読み込んだのは行の開始
startLine = false;
}
if (bufに改行を含む) {
//TODO pos位置を調整
startLine = true;
}
}

私ならBufferedReaderを継承して作った新しいReaderを用意して
readLineSmallメソッドでも作って
そこでやらせるわね。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
だんだんとわかってはきました・・・が、

おっしゃってることはそれなりに理解できたつもりなのですが
実際に組む時、不慣れな為どうやって組むかがいまいちで・・・。

一応下記のように組んではいるのですが、中途半端な組み方になってしまって・・・。
あと、デバッガで動かすとなぜか最初の判定で「startLine」がfalseになってしまっています。

もう少しヒントをいただくことって出来ないでしょうか?
よろしかったらお願いいたします。

public static void main(String[] args) throws IOException {

    File testFile = new File("C:\\work\\test\\file\\readTest.txt");
    // ファイルオープン
    FileInputStream fis = new FileInputStream(testFile);
    InputStreamReader isr;
    isr = new InputStreamReader(fis);
    BufferedReader result = new BufferedReader(isr);

    boolean startLine = true;
    final int BUFSIZE = 2048;
    char cbuf[] = new char[BUFSIZE];
    int currpos = 0;
    int maxpos = 0; //とりあえず書いてみたけど使っていない

    while (true) {
      result.read(cbuf, 0, BUFSIZE);
      if (startLine) {
        // 読み込んだのは行の開始
        startLine = false;
      }
      int c = cbuf[currpos++];
      // cbufに改行を含む
      if (c == '\n') {
        startLine = true;
      }
    }
    result.close();
}

お礼日時:2010/07/12 18:02

1行の区切りをどうやって認識するのかがわかってないようですけど。


それが出来なければ、1行を読むことは不可能ですね。

>readLineで一度他の変数へ入れてからというのは無しです
その理由がわかりませんね。

この回答への補足

ご回答ありがとうございます。

>1行の区切りをどうやって認識するのかがわかってないようですけど。
1行のテキストを読み込む場合、1行の終端は、改行「\n」か、復帰「\r」、または復行とそれに続く改行のどれかで認識されます。

>>readLineで一度他の変数へ入れてからというのは無しです
>その理由がわかりませんね。
1行が長すぎる場合があり、読み込む時点でOutOfMemoryが発生する可能性があるからです。
その為、1行を読み込む時点で取得サイズに制限をかけたいのです。

補足日時:2010/07/12 14:26
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!