9

だから私は.csvファイルの解析に取り組んでいます。StackOverflow のどこかにある別のスレッドのアドバイスを受けて、SuperCSV をダウンロードしました。最終的にほとんどすべてが機能するようになりましたが、修正が難しいと思われるバグに遭遇しました。

この問題は、データの最後の 2 つの列が入力される場合と入力されない場合があるために発生します。以下は、最初の行に最後の列がなく、2 番目の行が完全な .csv ファイルの例です。

2012:07:25,11:48:20,922,"uLog.exe","",キーが押されました,1246,341,-1.00,-1.00,1.00,Shift 2012:07:25,11:48:21,094," uLog.exe","",キーが押されました,1246,341,-1.00,-1.00,1.00,b,Shift

Super CSV Javadocに関する私の理解では、可変数の列がある場合、Java Bean にCsvBeanReaderを設定する方法はありません。Bean の初期化時に、これらの欠落している列を null またはその他のデフォルト値にすることを許可する必要があると思うので、これは本当にばかげているようです。

参考までに、パーサーの完全なコードを次に示します。

public class ULogParser {

String uLogFileLocation;
String screenRecorderFileLocation;

private static final CellProcessor[] cellProcessor = new CellProcessor[] {
    new ParseDate("yyyy:MM:dd"),
    new ParseDate("HH:mm:ss"),
    new ParseDate("SSS"),
    new StrMinMax(0, 100),
    new StrMinMax(0, 100),
    new StrMinMax(0, 100),
    new ParseInt(),
    new ParseInt(),
    new ParseDouble(),
    new ParseDouble(),
    new ParseDouble(),
    new StrMinMax(0, 100),
    new StrMinMax(0, 100),
};

public String[] header = {"Date", "Time", "Msec", "Application", "Window", "Message", "X", "Y", "RelDist", "TotalDist", "Rate", "Extra1", "Extra2"}; 

public ULogParser(String uLogFileLocation, String screenRecorderFileLocation)
{
    this.uLogFileLocation = uLogFileLocation;
    this.screenRecorderFileLocation = screenRecorderFileLocation;
}

public void parse()
{
    try {
        ICsvBeanReader reader = new CsvBeanReader(new BufferedReader(new FileReader(uLogFileLocation)), CsvPreference.STANDARD_PREFERENCE);
        reader.getCSVHeader(false); //parse past the header
        Entry entry;
        entry = reader.read(Entry.class, header, cellProcessor);
        System.out.println(entry.Application);
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public void sendToDB()
{
    Query query = new Query();
}
}

Entry クラスのコードは次のとおりです。

public class Entry
{
private Date Date;
private Date Time;
private Date Msec;
private String Application;
private String Window;
private String Message;
private int X;
private int Y;
private double RelDist;
private double TotalDist;
private double Rate;
private String Extra1;
private String Extra2;

public Date getDate() { return Date; }
public Date getTime() { return Time; }
public Date getMsec() { return Msec; }
public String getApplication() { return Application; }
public String getWindow() { return Window; }
public String getMessage() { return Message; }
public int getX() { return X; }
public int getY() { return Y; }
public double getRelDist() { return RelDist; }
public double getTotalDist() { return TotalDist; }
public double getRate() { return Rate; }
public String getExtra1() { return Extra1; }
public String getExtra2() { return Extra2; }

public void setDate(Date Date) { this.Date = Date; }
public void setTime(Date Time) { this.Time = Time; }
public void setMsec(Date Msec) { this.Msec = Msec; }
public void setApplication(String Application) { this.Application = Application; }
public void setWindow(String Window) { this.Window = Window; }
public void setMessage(String Message) { this.Message = Message; }
public void setX(int X) { this.X = X; }
public void setY(int Y) { this.Y = Y; }
public void setRelDist(double RelDist) { this.RelDist = RelDist; }
public void setTotalDist(double TotalDist) { this.TotalDist = TotalDist; }
public void setRate(double Rate) { this.Rate = Rate; }
public void setExtra1(String Extra1) { this.Extra1 = Extra1; }
public void setExtra2(String Extra2) { this.Extra2 = Extra2; }

public Entry(){}
}

そして、私が受け取っている例外 (これは上記の例とは異なる行であり、最後の 2 つの列の両方が欠落していることに注意してください):

スレッド「メイン」の例外 値配列 (サイズ 12) はプロセッサ配列 (サイズ 13) と一致する必要があります: おそらく、指定された cellprocessors の数とは異なる列数の CSV 行を読み取っています コンテキスト: 行: 2 列: 0生の行:
[2012:07:25, 11:48:05, 740, uLog.exe, , ログ開始, -1, -1, -1.00, -1.00, -1.00, ]
 問題のあるプロセッサ: null
    org.supercsv.util.Util.processStringList (不明なソース) で
    org.supercsv.io.CsvBeanReader.read (不明なソース) で
    処理中.ULogParser.parse(ULogParser.java:59)
    ui.ParseImplicitData.main(ParseImplicitData.java:15) で

はい、ゲッターとセッターをすべて書くのは大変でした。また、申し訳ありませんが、SuperCSV の使用には完全な慣習はありません (変更されていない文字列が必要な場合に使用する CellProcessor など) が、アイデアはわかります。また、このコードは明らかに完全ではありません。今のところ、1 行のデータを正常に取得しようとしています。

この時点で、私の目的のために CsvBeanReader を使用できるかどうか疑問に思っています。そうでない場合は、CsvListReader (ハイパーリンクを投稿しますが、StackOverflow も私を許可していません) は、API をまったく使用せず、Scanner.next を使用するのと同じくらい簡単であるため、少しがっかりしています。 ().

どんな助けでも大歓迎です。前もって感謝します!

4

3 に答える 3

4

編集:スーパーCSV2.0.0-beta-1の更新

Super CSV 2.0.0-beta-1でAPIが変更されていることに注意してください(コード例は1.52に基づいています)。getCSVHeader()これで、すべてのリーダーのメソッドが(ライターのメソッドgetHeader()と一致するように)なりました。writeHeader

また、SuperCSVExceptionに名前が変更されましたSuperCsvException


編集:スーパーCSV2.1.0の更新

バージョン2.1.0以降では、新しいメソッドを使用して、CSVの行を読み取ったにセルプロセッサを実行することができます。executeProcessors()詳細については、プロジェクトのWebサイトでこの例を参照してください。CsvListReaderこれは、可変の列長を許可する唯一のリーダーであるため、にのみ関連することに注意してください。


正解ですCsvBeanReader。可変数の列を持つCSVファイルはサポートされていません。ほとんどのCSV仕様(RFC 4180を含む)によれば、列の数はすべての行で同じでなければなりません。

このため(スーパーCSV開発者として)、この機能をスーパーCSVに追加することには消極的です。あなたがそれを追加するためのエレガントな方法を考えることができるなら、プロジェクトのSourceForgeサイトで気軽に提案をしてください。それはおそらく、拡張する新しいリーダーを意味しますCsvBeanReader:読み取りとマッピング/処理を2つの別々のメソッドに分割する必要があります(列の数がわからない限り、Beanのフィールドへの処理またはマッピングを行うことはできません) 。

簡単な解決策

これに対する簡単な解決策(作業しているCSVファイルを制御できる場合)は、CSVファイルを書き込むときに空白の列を追加することです(例の最初の行の最後にコンマがあります-最後の列は空です)。そうすれば、CSVファイルが有効になり(すべての行に同じ数の列が含まれるようになります)、CsvBeanReaderすでに行っているように使用できます。

それが不可能な場合でも、すべてが失われることはありません

ファンシーソリューション

ご存知かもしれませんCsvBeanReaderが、名前マッピングを使用してCSVファイルの各列をBeanのフィールドに関連付け、CellProcessor配列を使用して各列を処理します。つまり、使用する場合は、列がいくつあるか(およびそれらが何を表すか)を知る必要があります。

CsvListReader一方、は非常に原始的であり、さまざまな長さの行を読み取ることができます(それらを処理またはマップする必要がないため)。

したがって、両方のリーダーでファイルを並行して読み取ることにより、CsvBeanReaderwithのすべての機能を組み合わせることができます(使用して列の数を把握し、処理/マッピングを実行します)。CsvListReaderCsvListReaderCsvBeanReader

これは、存在しない可能性があるのはbirthDate列のみであると想定していることに注意してください(つまり、どの列が欠落しているかがわからない場合は機能しません)。

package example;

import java.io.StringReader;
import java.util.Date;

import org.supercsv.cellprocessor.ParseDate;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.exception.SuperCSVException;
import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.CsvListReader;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.io.ICsvListReader;
import org.supercsv.prefs.CsvPreference;

public class VariableColumns {

    private static final String INPUT = "name,birthDate,city\n"
        + "John,New York\n" 
        + "Sally,22/03/1974,London\n" 
        + "Jim,Sydney";

    // cell processors
    private static final CellProcessor[] NORMAL_PROCESSORS = 
    new CellProcessor[] {null, new ParseDate("dd/MM/yyyy"), null };
    private static final CellProcessor[] NO_BIRTHDATE_PROCESSORS = 
    new CellProcessor[] {null, null };

    // name mappings
    private static final String[] NORMAL_HEADER = 
    new String[] { "name", "birthDate", "city" };
    private static final String[] NO_BIRTHDATE_HEADER = 
    new String[] { "name", "city" };

    public static void main(String[] args) {

        // using bean reader and list reader together (to read the same file)
        final ICsvBeanReader beanReader = new CsvBeanReader(new StringReader(
                INPUT), CsvPreference.STANDARD_PREFERENCE);
        final ICsvListReader listReader = new CsvListReader(new StringReader(
                INPUT), CsvPreference.STANDARD_PREFERENCE);

        try {
            // skip over header
            beanReader.getCSVHeader(true);
            listReader.getCSVHeader(true);

            while (listReader.read() != null) {

                final String[] nameMapping;
                final CellProcessor[] processors;

                if (listReader.length() == NORMAL_HEADER.length) {
                    // all columns present - use normal header/processors
                    nameMapping = NORMAL_HEADER;
                    processors = NORMAL_PROCESSORS;

                } else if (listReader.length() == NO_BIRTHDATE_HEADER.length) {
                    // one less column - birth date must be missing
                    nameMapping = NO_BIRTHDATE_HEADER;
                    processors = NO_BIRTHDATE_PROCESSORS;

                } else {
                    throw new SuperCSVException(
                            "unexpected number of columns: "
                                    + listReader.length());
                }

                // can now use CsvBeanReader safely 
                // (we know how many columns there are)
                Person person = beanReader.read(Person.class, nameMapping,
                        processors);

                System.out.println(String.format(
                        "Person: name=%s, birthDate=%s, city=%s",
                        person.getName(), person.getBirthDate(),
                        person.getCity()));

            }
        } catch (Exception e) {
            // handle exceptions here
            e.printStackTrace();
        } finally {
            // close readers here
        }
    }

    public static class Person {

        private String name;
        private Date birthDate;
        private String city;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Date getBirthDate() {
            return birthDate;
        }

        public void setBirthDate(Date birthDate) {
            this.birthDate = birthDate;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }
    }

}

これがお役に立てば幸いです。

Entryああ、クラスのフィールドが通常の命名規則(camelCase)に従わない理由はありますか?キャメルケースを使用するように配列を更新するheaderと、フィールドもキャメルケースにすることができます。

于 2012-07-28T05:56:17.567 に答える
1

uniVocity-parsersを使用すると、さまざまな列数を持つ CSV ファイルを Java Bean にマップできます。注釈の使用:

class TestBean {

// if the value parsed in the quantity column is "?" or "-", it will be replaced by null.
@NullString(nulls = { "?", "-" })
// if a value resolves to null, it will be converted to the String "0".
@Parsed(defaultNullRead = "0")
private Integer quantity;   // The attribute type defines which conversion will be executed when processing the value.
// In this case, IntegerConversion will be used.
// The attribute name will be matched against the column header in the file automatically.

@Trim
@LowerCase
// the value for the comments attribute is in the column at index 4 (0 is the first column, so this means fifth column in the file)
@Parsed(index = 4)
private String comments;

// you can also explicitly give the name of a column in the file.
@Parsed(field = "amount")
private BigDecimal amount;

@Trim
@LowerCase
// values "no", "n" and "null" will be converted to false; values "yes" and "y" will be converted to true
@BooleanString(falseStrings = { "no", "n", "null" }, trueStrings = { "yes", "y" })
@Parsed
private Boolean pending;
...
}

CSV をTestBeanインスタンスのリストに解析するには:

// BeanListProcessor converts each parsed row to an instance of a given class, then stores each instance into a list.
BeanListProcessor<TestBean> rowProcessor = new BeanListProcessor<TestBean>(TestBean.class);
CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.setRowProcessor(rowProcessor);
//Uses the first valid row of the CSV to assign names to each column
parserSettings.setHeaderExtractionEnabled(true);

CsvParser parser = new CsvParser(parserSettings);
parser.parse(new FileReader(yourFile));

// The BeanListProcessor provides a list of objects extracted from the input.
List<TestBean> beans = rowProcessor.getBeans();

開示:私はこのライブラリの作成者です。オープンソースで無料です (Apache V2.0 ライセンス)。

于 2014-11-16T03:05:51.393 に答える
1

さて、SuperCSV はオープン ソースです。可変数の後続フィールドで入力を処理するなどの機能を追加する場合、基本的に 2 つのオプションがあります。

  1. SourceForge サイトにサポート リクエストを投稿し、作成者が同意し、時間があることを願っています。
  2. ソースをダウンロードし、好みに合わせて変更し、変更をプロジェクトに貢献してください。

これがオープンソースの仕組みです。

于 2012-07-26T22:04:55.090 に答える