2

次のようなテキスト ファイルがあります。

grn129          agri-
ac-214          ahss
hud114          ahss
lov1150         ahss
lov1160         ahss
lov1170         ahss
lov1210         ahss

最初の列をキー、2 番目の列を値として HashMap を作成する場合、Java を使用してこのファイルを解析する最良の方法は何ですか。

Scanner クラスを使用する必要がありますか? ファイル全体を文字列として読み込んで分割してみませんか?

最善の方法は何ですか?

4

7 に答える 7

4

これが私がそれを行う方法です!私は 2000 年以来、Java プログラマーにほぼ専念しているので、少し時代遅れかもしれません。特に私が少し誇りに思っている行があります。

new InputStreamReader(fin, "UTF-8");

http://www.joelonsoftware.com/articles/Unicode.html

楽しみ!

import java.io.*;
import java.util.*;

public class StackOverflow2565230 {

  public static void main(String[] args) throws Exception {
    Map<String, String> m = new LinkedHashMap<String, String>();
    FileInputStream fin = null;
    InputStreamReader isr = null;
    BufferedReader br = null;
    try {
      fin = new FileInputStream(args[0]);
      isr = new InputStreamReader(fin, "UTF-8");
      br = new BufferedReader(isr);
      String line = br.readLine();
      while (line != null) {
        // Regex to scan for 1 or more whitespace characters
        String[] toks = line.split("\\s+");
        m.put(toks[0], toks[1]);
        line = br.readLine();
      }
    } finally {
      if (br != null)  { br.close();  }
      if (isr != null) { isr.close(); }
      if (fin != null) { fin.close(); }
    }

    System.out.println(m);
  }

}

出力は次のとおりです。

julius@flower:~$ javac StackOverflow2565230.java 
julius@flower:~$ java -cp .  StackOverflow2565230  file.txt 
{grn129=agri-, ac-214=ahss, hud114=ahss, lov1150=ahss, lov1160=ahss, lov1170=ahss, lov1210=ahss}

はい、私のコンピューターの名前は Flower です。バンビのスカンクにちなんで名付けられました。

最後に 1 つ: close() は IOException をスローする可能性があるため、ストリームを実際に閉じる方法は次のとおりです。

} finally {
  try {
    if (br != null) br.close();
  } finally {
    try {
      if (isr != null) isr.close();
    } finally {
      if (fin != null) fin.close();
    }
  }
}
于 2010-04-02T06:21:51.767 に答える
3

@Julius Daviesに基づいて、これは短いバージョンです。

import java.io.*; 
import java.util.*; 

public class StackOverflow2565230b { 
  public static void main(String... args) throws IOException { 
    Map<String, String> m = new LinkedHashMap<String, String>(); 
    BufferedReader br = null; 
    try { 
      br = new BufferedReader(new FileReader(args[0])); 
      String line;
      while ((line = br.readLine()) != null) { 
        // Regex to scan for 1 or more whitespace characters 
        String[] toks = line.split("\\s+"); 
        m.put(toks[0], toks[1]); 
      } 
    } finally { 
      if (br != null) br.close(); // dont throw an NPE because the file wasn't found.
    } 

    System.out.println(m); 
  } 
}
于 2010-04-02T08:27:53.987 に答える
2

最善の方法はわかりませんが、最も効率的な方法は、一度に1行ずつ読み取り(BufferedReaderを使用)、最初の空白文字を見つけてそこで分割し、次にトリミングすることで各行を分割することだと思います。両側。ただし、超高速である必要がない限り、好きなものは何でも構いません。

私は個人的に、ファイル全体を一度にロードすることに偏っています...ファイル全体を保持するのに十分なメモリがあると想定しているという事実を除けば、並列計算はできません(たとえば、入力が入ってくる場合)パイプから)。入力がまだ生成されている間に入力を処理できることは理にかなっています。

于 2010-04-02T06:10:09.317 に答える
1

スキャナーまたは通常のFileReader+String.split()を使用すると、どちらも正常に機能するはずです。速度の違いはごくわずかだと思います。非常に大きなファイルを何度も読み取る予定がない限り、問題はありません。

編集:実際には、2番目の方法ではBufferedReaderを使用します。getLine()メソッドがあり、少し簡単になります。

于 2010-04-02T06:07:59.133 に答える
0

教科書の解決策に従う場合は、StringTokenizerを使用してください。簡単で、習得が簡単で、非常にシンプルです。構造の単純な逸脱(空白文字の可変数、不均一なフォーマット行など)を克服できます。

ただし、テキストが100%整形式で予測可能であることがわかっている場合は、一連の行をバッファーに読み込んで、一度に1つずつ取り出し、文字列の一部をHashMapキーと値に取り出します。StringTokenizerよりも高速ですが、柔軟性に欠けています。

于 2010-04-02T06:08:52.090 に答える
0

正規表現をキャッシュするのはどうですか?(String.split() は呼び出しごとに正規表現をコンパイルします)

いくつかの大きなファイル (100、1k、100k、1m、10m エントリ) で各メソッドのパフォーマンスをテストし、パフォーマンスがどのように比較されるかを確認していただければ幸いです。

import java.io.*;
import java.util.*;
import java.util.regex.*;

public class So2565230 {

    private static final Pattern rgx = Pattern.compile("^([^ ]+)[ ]+(.*)$");

    private static InputStream getTestData(String charEncoding) throws UnsupportedEncodingException {
        String nl = System.getProperty("line.separator");
        StringBuilder data = new StringBuilder();
        data.append(" bad data " + nl);
        data.append("grn129          agri-" + nl);
        data.append("grn129          agri-" + nl);
        data.append("ac-214          ahss" + nl);
        data.append("hud114          ahss" + nl);
        data.append("lov1150         ahss" + nl);
        data.append("lov1160         ahss" + nl);
        data.append("lov1170         ahss" + nl);
        data.append("lov1210         ahss" + nl);
        byte[] dataBytes = data.toString().getBytes(charEncoding);
        return new ByteArrayInputStream(dataBytes);
    }

    public static void main(final String[] args) throws IOException {
        String encoding = "UTF-8";

        Map<String, String> valuesMap = new LinkedHashMap<String, String>();

        InputStream is = getTestData(encoding);
        new So2565230().fill(valuesMap, is, encoding);

        for (Map.Entry<String, String> entry : valuesMap.entrySet()) {
            System.out.format("K=[%s] V=[%s]%n", entry.getKey(), entry.getValue());
        }
    }

    private void fill(Map<String, String> map, InputStream is, String charEncoding) throws IOException {
        BufferedReader bufReader = new BufferedReader(new InputStreamReader(is, charEncoding));
        for (String line = bufReader.readLine(); line != null; line = bufReader.readLine()) {
            Matcher m = rgx.matcher(line);
            if (!m.matches()) {
                System.err.println("Line has improper format (" + line + ")");
                continue;
            }
            String key = m.group(1);
            String value = m.group(2);
            if (map.put(key, value) != null) {
                System.err.println("Duplicate key detected: (" + line + ")");
            }
        }
    }
}
于 2010-04-02T19:04:13.990 に答える
-1

Julius Davies の答えは結構です。

ただし、解析するテキスト ファイルの形式を定義する必要があると思います。たとえば、最初の列と 2 番目の列の間の別の文字は何ですか。それが修正されていないと、さらに問題が発生します。

于 2010-04-02T10:41:11.483 に答える