Как получить доступ к значениям столбца в .csv файле?
В задании сказано: При работе со строками известного формата удобно пользоваться методом split(). Он делит исходную строку по символу-разделителю и возвращает массив строк. Например, чтобы разбить нашу строку, из файла по строкам нужно вызвать метод с аргументом System.lineSeparator(). Эта функция возвращает конец строки:
String[] lines = fileContents.split(System.lineSeparator());
Теперь у нас есть набор строк. Осталось разобрать каждую из них с помощью того же метода. Разделение будет проходить по символу «,»:
String[] lineContents = line.split(",");
Это позволит обращаться к конкретным значениями внутри строки.
А теперь сам вопрос:
Как это все применить в моем коде, чтоб получить доступ к значениям в файле xxx.csv для дальнейшей работы с ними (например, для получения суммы столбца sum_of_one)?:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) {
System.out.println(readFileContents("xxx.csv"));
}
private static String readFileContents(String filename){
Path filePath = Paths.get(filename);
try {
return Files.readString(filePath);
} catch (IOException e) {
System.out.println("Невозможно прочитать файл. Возможно, файл не находится в нужной директории.");
}
}
}
xxx.csv
item_name,is_expense,quantity,sum_of_one
Коньки,TRUE,50,2000
Новогодняя ёлка,TRUE,1,100000
Ларёк с кофе,TRUE,3,50000
Аренда коньков,FALSE,1000,180
Продажа билетов,FALSE,3500,300
Продажа кофе,FALSE,2421,150
Ответы (2 шт):
Вы подходите к заданию, не продумывая решение. Ваша задача не в том, чтобы превратить текстовый файл в переменную String. Тогда вопрос: зачем писать код, который в итоге заведет вас в тупик? Ведь изначально было понятно, что полученная переменная типа String вам не нужна и сделать с ней либо ничего нельзя , либо крайне сложно.
Для начала нам нужно преобразовать csv файл в тот вид, с которым нам будет просто работать в мире ООПешном. В моем представлении любые "табличные" данные всегда можно провести к Map, где ключ - имя столбца, значение - значение конкретной строки этой таблицы. Любой csv можно рассматривать как табличные данные: есть заголовок, есть разделитель, являющийся по факту вертикальной чертой таблицы итд. Значит нам нужно что-то , что прочитает наш csv и приведет его к Map. Это первый шаг. Теперь мы сталкиваемся с другой проблемой: Map - отличное решение в плане универсальности использования. Но в мире строгой типизации и ООП с такими данными все еще достаточно сложно работать, посему я бы хотел иметь класс-модель, который будет принимать данные из Map, его поля будут строго типизированы, посему и с экземплярами этого класса работать будет просто. Посему следующий шаг - создание класса модели. И последнее - приведение Map к объектам этого класса.
Теперь реализация:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Parser {
private final String delimeter;
public Parser(String delimeter) {
this.delimeter = delimeter;
}
public <T> List<T> load(InputStream is, Class<T> modelClass) throws Exception {
final List<String> rows = new BufferedReader(new InputStreamReader(is, Charset.defaultCharset()))
.lines().collect(Collectors.toList());
if (rows.size() < 2) {
return new ArrayList<>();
}
final String[] columnsNames = rows.remove(0).split(delimeter);
final List<T> valuesList = new ArrayList<>(rows.size());
for (final String v : rows) {
final String[] value = v.split(delimeter);
final Map<String, String> values = new HashMap(value.length);
for (int i = 0; i < value.length; i++) values.put(columnsNames[i], value[i]);
final T t = modelClass.getDeclaredConstructor(Map.class).newInstance(values);
valuesList.add(t);
}
return valuesList;
}
}
import java.util.Map;
public class Item {
private final String name;
private final Boolean isExpense;
private final Integer quantity;
private final Integer sumOfOne;
public Item(Map<String, String> values) {
this.name = values.get("item_name");
this.isExpense = Boolean.valueOf(values.get("is_expense"));
this.quantity = Integer.valueOf(values.get("quantity"));
this.sumOfOne = Integer.valueOf(values.get("sum_of_one"));
}
public String getName() {
return name;
}
public Boolean getIsExpense() {
return isExpense;
}
public Integer getQuantity() {
return quantity;
}
public Integer getSumOfOne() {
return sumOfOne;
}
@Override
public String toString() {
return "Item{" + "name=" + name + ", isExpense=" + isExpense + ", quantity=" + quantity + ", sumOfOne=" + sumOfOne + '}';
}
}
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
Parser parser = new Parser(",");
List <Item> items = parser.load(new FileInputStream(new File("xxx.csv")), Item.class);
System.out.println(items);
int quantitySum = items.stream().mapToInt(Item::getQuantity).sum();
int sum = items.stream().mapToInt(Item::getSumOfOne).sum();
System.out.println(quantitySum);
System.out.println(sum);
}
}
Предложу такой вариант, если не надо лезть в ООП.
Значения для каждой строки получаются в цикле for. В данном примере все пары имя=значиние выводятся на экран.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Main {
private static final int ITEM_NAME = 0;
private static final int IS_EXPENSE = 1;
private static final int QUANTITY = 2;
private static final int SUM_OF_ONE = 3;
public static void main(String[] args) throws IOException {
String[] lines = Files
.readString(Paths.get("file.csv"))
.split(System.lineSeparator());
for (int i = 1; i < lines.length; i++) {
String[] values = lines[i].split(",");
String name = values[ITEM_NAME];
boolean isExpense = Boolean.parseBoolean(values[IS_EXPENSE]);
int quantity = Integer.parseInt(values[QUANTITY]);
int sumOfOne = Integer.parseInt(values[SUM_OF_ONE]);
System.out.printf("{name=%s,isExpense=%s,quantity=%s,sumOfOne=%s\n",
name, isExpense, quantity, sumOfOne);
}
}
}