3

標準のJavaライブラリのみを使用して、引用符で囲まれたコンマ区切りの値を解析しようとしています(これが可能でなければならないことはわかっています)

例として file.txt には、各行の新しい行が含まれています

"Foo","Bar","04042013","04102013","Stuff"
"Foo2","Bar2","04042013","04102013","Stuff2"

ただし、これまでに書いたコードでファイルを解析すると、次のようになります。

import java.io.*;
import java.util.Arrays;
 public class ReadCSV{

    public static void main(String[] arg) throws Exception {

        BufferedReader myFile = new BufferedReader(new FileReader("file.txt"));

        String myRow = myFile.readLine(); 
        while (myRow != null){
            //split by comma separated quote enclosed values
            //BUG - first and last values get an extra quote
            String[] myArray = myRow.split("\",\""); //the problem

            for (String item:myArray) { System.out.print(item + "\t"); }
            System.out.println();
            myRow = myFile.readLine();
        }
        myFile.close();
    }
}

ただし、出力は

"Foo    Bar     04042013        04102013        Stuff"

"Foo2   Bar2    04042013        04102013        Stuff2"

それ以外の

Foo    Bar     04042013        04102013        Stuff

Foo2   Bar2    04042013        04102013        Stuff2

スプリットで失敗したことは知っていますが、修正方法がわかりません。

4

6 に答える 6

4

基本的に以下のコードのように、おそらくステートフルなアプローチを採用する必要があると思います(値内の引用符のエスケープを許可する場合は、別の状態が必要になります)。

import java.util.ArrayList;
import java.util.List;


public class CSV {

    public static void main(String[] args) {
        String s = "\"hello, i am\",\"a string\"";
        String x = s;
        List<String> l = new ArrayList<String>();
        int state = 0;
        while(x.length()>0) {
            if(state == 0) {
                if(x.indexOf("\"")>-1) {
                    x = x.substring(x.indexOf("\"")+1).trim();
                    state = 1;
                } else {
                    break;
                }
            } else if(state == 1) {
                if(x.indexOf("\"")>-1) {
                    String found = x.substring(0,x.indexOf("\"")); 
                    System.err.println("found: "+found);
                    l.add(found);
                    x = x.substring(x.indexOf("\"")+1).trim();
                    state = 0;
                } else {
                    throw new RuntimeException("bad format");
                }
            } else if(state == 2) {
                if(x.indexOf(",")>-1) {
                    x = x.substring(x.indexOf(",")+1).trim();
                    state = 0;
                } else {
                    break;
                }
            }
        }
        for(String f : l) {
            System.err.println(f);
        }
    }


}
于 2013-04-22T07:49:28.630 に答える
4

分割を行う前に、以下の行を使用して myRow 変数の最初の二重引用符と最後の二重引用符を削除してください。

myRow = myRow.substring(1, myRow.length() - 1);

(更新) myRow が空でないかどうかも確認してください。そうしないと、上記のコードで例外が発生します。たとえば、次のコードは myRow が空でないかどうかをチェックし、文字列から二重引用符のみを削除します。

if (!myRow.isEmpty()) {
    myRow = myRow.substring(1, myRow.length() - 1);
}
于 2013-04-22T07:24:54.107 に答える
2

代わりに、私にとっては、このタスクにより適していると思われるreplaceAllを使用できます。

myRow = myRow.replaceAll("\"", "").replaceAll(","," ");

これにより、すべてが"何も置き換えられず(削除されます)、すべて,がスペースに置き換えられます(もちろん、スペースの数を増やすことができます)。

于 2013-04-22T07:25:59.270 に答える
1

上記のコード スニペットの問題は、に基づいて文字列を分割していることです ","。あなたのラインでは、開始"foo","","stuff"終了の引用符が一致しない","ため、分割されていません。

したがって、これはJavaのバグではありません。あなたの場合、その部分を自分で処理する必要があります。

それを行うには複数のオプションがあります。それらのいくつかは以下のようになります。"1. 開始と終了が常に存在することが確実な場合は、"分割する前にそれらを String から削除することができます。2. 開始""がオプションの場合は、最初に で確認しstartsWith endsWith、存在する場合は削除してから分割できます。

于 2013-04-22T07:31:13.083 に答える
0

コンマで区切られた文字列を取得し、最初と最後の '"' を削除するだけです。=) 役に立てば幸いです :D

String s = "\"Foo\",\"Bar\",\"04042013\",\"04102013\",\"Stuff\"";
        String[] bufferArray = new String[10];
        String bufferString;
        int i = 0;
        System.out.println(s);

        Scanner scanner = new Scanner(s);
        scanner.useDelimiter(",");

        while(scanner.hasNext()) {
            bufferString = scanner.next();
            bufferArray[i] = bufferString.subSequence(1, bufferString.length() - 1).toString();
            i++;
        }

        System.out.println(bufferArray[0]);
        System.out.println(bufferArray[1]);
        System.out.println(bufferArray[2]);
于 2013-04-22T07:32:47.190 に答える
0

このソリューションは、ワンライナーよりもエレガントではありString.split()ません。利点は、壊れやすい文字列操作を避けることです。の使用String.substring()。文字列は、しかしで終わる必要があり,"ます。

このバージョンは、区切り記号間のスペースを処理します。引用符内の区切り文字は、エスケープされた引用符と同様に無視されます (例: \")。

String s = "\"F\\\",\\\"oo\"  ,    \"B,ar\",\"04042013\",\"04102013\",\"St,u\\\"ff\"";
Pattern p = Pattern.compile("(.*?)\"\\s*,\\s*\"");
Matcher m = p.matcher(s + ",\""); // String must end with ,"
while (m.find()) {
    String result = m.group(1);
    System.out.println(result);
}
于 2015-02-19T14:02:53.757 に答える