替代创建一个String对象的新方法?

时间:2015-03-17 17:12:43

标签: java csv refactoring

我遇到了一个问题:我有一个csv文件来准备和提取值。这是代码(并且工作正常):

public class Extract {

final int[] ID = new int[]{0, 10};
final int[] NAME = new int[]{15, 45};

public static void main(String args[]) {

  FileInputStream fis = new FileInputStream("C:\\cnab.txt");        
  String line;

  while ((line = fis.readLine()) != null) {
      System.out.println(getValue(line, ID));
      System.out.println(getValue(line, NAME)); 
  }
}

private String getValue(String line, int[] slice) {
    return line.substring(slice[0], slice[1]);
}

}

直到那时好,但我想要一些更具可读性,更优雅的东西,例如:

System.out.println(line.getValue(NAME));

但我们都知道String是final(应该是)。有人有什么建议吗?

3 个答案:

答案 0 :(得分:3)

一个选项是拥有一个枚举,你当前有一个int数组:

public enum Field {
    ID(0, 10),
    NAME(15, 45);

    private final int start;
    private final int end;

    private Field(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public string extractFrom(String line) {
        return line.substring(start, end);
    }
}

然后:

System.out.println(Fields.NAME.extractFrom(line));
System.out.println(Fields.ID.extractFrom(line));

这意味着您可以传递字段(例如,有一组必需的字段进行验证),而不仅仅是int[],这可能意味着什么 - 您可以添加任何其他自定义方法和信息。 (例如,对于验证,您可以拥有Pattern字段...)

答案 1 :(得分:0)

你可以创建自己的类,它是String的包装器(即组合而不是继承):

public MyString {

    private final String string;

    public MyString(String string) {
        this.string = string;
    }

    public String getId() {
        return string.substring(0, 10);
    }

    // ...

}

我建议使用与MyString不同的名称,它应该是描述此对象的内容。如果您不需要任何String方法,这应该没问题。如果你需要一些,你可以简单地委托他们

public MyString {

    // ...

    public int length() {
        return string.length();
    }

    // ...

}

如果你需要全部,Project Lombok有一个@Delegate注释可以解决这个问题,我实际上并不确定。

答案 2 :(得分:0)

看起来您正在寻找一些抽象,允许您查询文件中的不同列。为什么不模仿java.sql.ResultSet? e.g。

public static void main(String args[]) throws IOException {
    BufferedReader file = new BufferedReader(new FileReader("./datacol.txt"));

    // Build the recordset for the file
    RecordSet rec = new RecordSet(file);

    // Iterate on the lines or "records" in the file
    while (rec.next()) {
        try {
            System.out.println(rec.getInt(FooField.ID)); // get the right type
            System.out.println(rec.getString(FooField.NAME));
        }
        catch (Exception e) {
            System.out.println(e.toString());
        }
    }
}

RecordSet类包装文件并允许您获取行。为此,您需要使用RecordSet将使用的某个对象来描述文件列。以下是@Jon提出的描述符,enum,但它实现了一个简单的接口RecordField。此接口的目的是允许RecordSet使用不同的字段描述符

public interface RecordField {
    String getFieldValue(String line);
}

// Describe a specific file ("Foo") with its columns (or fields)
public enum FooField implements RecordField {
    ID(0,10),
    NAME(15, 45);

    private final int start;
    private final int end;

    private FooField(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public String getFieldValue(String line) {
        if (line.length() < start || line.length() < end) {
            throw new RuntimeException("Invalid line length for "+this);
        }
        return line.substring(start, end);
    }

}

现在,RecordSet类非常简单,并为字段访问提供了类型值 - 此处仅intString - :

public class RecordSet {

    private BufferedReader data;
    private String line; 

    public RecordSet(BufferedReader file) {
        this.data = file;
    }

    public boolean next() throws IOException {
        line = data.readLine();
        return line!=null;
    }

    public int getInt(RecordField col) {
        checkLine();
        String val = col.getFieldValue(line);
        return Integer.parseInt(val.trim());
    }

    public String getString(RecordField col) {
        checkLine();
        String val = col.getFieldValue(line);
        return val.trim();
    }

    private void checkLine() {
        if (line == null) {
            throw new RuntimeException("No current record");
        }
    }
}