笔记汇总:《Java面向对象程序设计》学习笔记
笔记记录的不是非常详实,如果有补充的建议或纠错,请踊跃评论留言!!!
什么是CSV文件
CSV文件的定义
CSV 是英文 comma-separated values 的缩写,翻译为 "逗号分隔值"。
CSV 文件可以理解为以带逗号分隔(也可以是其他简单字符分割)的纯文本形式存储表格数据的文件。
CSV文件与Excel文件的关系
CSV文件和Excel文件有点像,但Excel是带格式的文件,且一个工作表里最多只可以存放 1048576 (2^10)行。
可以使用Excel打开CSV。但如果没有按照指定编码方式进行保存,用Excel打开CSV会乱码。
CSV文件的优点
-
方便数据交换
-
无最大行数的限制
CSV文件格式
格式规范
-
CSV文件第一行可以是字段名(也可以不写字段名直接写数据)
-
接下来每一行是字段所对应的值
-
不同 字段/数据 之间用 逗号 隔开
-
其他格式规范
比如:
// 通讯录.csv
姓名,手机,QQ,微信号
小新,13913000001,1819122001,xx9907
小亮,13913000002,1819122002,xiaoliang
小刚,13913000003,1819122003,gang1004
大刘,13913000004,1819122004,liu666
大王,13913000005,1819122005,jack_w
奇妙方程式,12345678910,229600398,qq229600398
// 统计demo.csv
下单,0,a,BCF
付款,0,a,BCF
发货,0,a,BCF
付款,0,a,BCF
收款,0,a,BCF
下单,0,a,BCF
付款,0,a,BCF
CSV文件的读写操作
注意事项
-
其实CSV文件有固定的规范,简单的用逗号分隔,并不是安全的解析方法
比如:有些值中有逗号,直接使用split分隔逗号会出问题
-
一般都是使用类库来对csv文件进行处理,比如python的csv库、java的open-csv库。
-
其实在读写时最好上锁。
-
但个人练习没啥大问题。
-
使用Java读写文件后记得调用方法关闭流。
读取方法
以下为Java中读取CSV文件的方法,程序打印所读内容
// test.csv
Id,SepalLengthCm,SepalWidthCm,PetalLengthCm,PetalWidthCm,Species
1,5.1,1.4,1.4,0.2,Iris-setosa
2,4.9,3,1.4,0.2,Iris-setosa
3,4.7,3.2,1.3,0.2,Iris-setosa
4,4.6,3.1,1.5,0.2,Iris-setosa
5,5,3.6,1.4,0.2,Iris-setosa
6,5.4,3.9,1.7,0.4,Iris-setosa
7,4.6,3.4,1.4,0.3,Iris-setosa
8,5,3.4,1.5,0.2,Iris-setosa
// Csv.java
package csv;
import java.io.BufferedReader;
import java.io.FileReader;
public class Csv {
public static void main(String[] args) {
String filepath = "D:\\CSV文件\\test.csv";
String[][] alldata = null;
try {
// 1. 创建流
// 创建FileReader和BufferedReader对象来读取文件内容
FileReader filereader = new FileReader(filepath);
BufferedReader buffereader = new BufferedReader(filereader);
// 当然写在一起也行
// BufferedReader buffereader = new BufferedReader(new FileReader(filepath));
// 2. 计算文件中的行数
int linenumber = 0;
while (buffereader.readLine() != null) {
linenumber++;
}
// 3. 将缓冲读取器重置为从文件开头读取
filereader = new FileReader(filepath);
buffereader = new BufferedReader(filereader);
// 4. 用适当的维度初始化AllData数组
alldata = new String[linenumber][];
// 5. 读取内容,存储到内存(数组、Map或其他数据类型)中。使用split分隔逗号,还可以使用正则表达式进一步处理。
String line;
linenumber = 0;
while ((line = buffereader.readLine()) != null) {
// 按逗号分隔每行数据
String[] data = line.split(",");
// 第一种方式
// if (data.length > 0) {
// // 初始化内部数组
// alldata[linenumber] = new String[data.length];
// // 存入二维字符数组中
// for (int i = 0; i < data.length; i++) {
// alldata[linenumber][i] = data[i];
// }
// }
// 第二种方式
alldata[linenumber] = data;
linenumber++;
}
// 6. 输出存储数据
for (int i = 0; i < linenumber; i++) {
System.out.print(alldata[i][0]);
for (int j = 1; j < alldata[0].length; j++) {
System.out.print(","+alldata[i][j]);
}
System.out.println();
}
// 7. 关闭FileReader流和BufferedReader流
filereader.close();
buffereader.close();
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
// 输出
Id,SepalLengthCm,SepalWidthCm,PetalLengthCm,PetalWidthCm,Species
1,5.1,1.4,1.4,0.2,Iris-setosa
2,4.9,3,1.4,0.2,Iris-setosa
3,4.7,3.2,1.3,0.2,Iris-setosa
4,4.6,3.1,1.5,0.2,Iris-setosa
5,5,3.6,1.4,0.2,Iris-setosa
6,5.4,3.9,1.7,0.4,Iris-setosa
7,4.6,3.4,1.4,0.3,Iris-setosa
8,5,3.4,1.5,0.2,Iris-setosa
写入方法
以下为Java中读取CSV文件,写入新CSV文件的方法(读取方法的代码和上面一致,就省略)。
// Csv.java
package csv;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class Csv {
public static void main(String[] args) {
String filepath = "D:\\CSV文件\\test.csv";
String[][] alldata = null;
try {
// 接上面的代码,省略
// 1. 创建流
// 创建FileWriter和BufferedWriter对象来读取文件内容
filepath = "D:\\CSV文件\\test1.csv";
FileWriter filewriter = new FileWriter(filepath);
BufferedWriter bufferwriter = new BufferedWriter(filewriter);
// 当然写在一起也行
// BufferedWriter bufferwriter = new BufferedWriter(new FileWriter(filepath));
// 2. 写入新文件
for(String[] str:alldata) {
String context = str[0];
for (int i = 1; i < alldata[0].length; i++) {
context = context + "," + str[i];
}
bufferwriter.write(context);
bufferwriter.newLine(); // 换行
}
// 3. 关闭BufferedWriter流和FileWriter流,顺序不要搞反
bufferwriter.close();
filewriter.close();
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
数据处理
数据处理其实不涉及IO流的知识,更多涉及正则表达式、字符串操作、循环、赋值等知识。
比如寻找最值、计算平均值、提取某个数据等。
正则表达式
匹配整数和小数
// 匹配整数或小数的字符串
// -? 表示可选的负号,匹配一个或零个负号。
// \\d+ 匹配一个或多个数字字符。
// (\\.\\d+)? 表示可选的小数部分,包括一个点号和一个或多个数字字符。
String regex = "-?(0|[1-9]\\d*)(\\.\\d+)?";
if (s.matches(regex)) {
···
}else {
···
}
其他匹配方式欢迎补充
字符串操作
split() 方法分隔逗号
result = s.split(",");
equals() 方法判断字符串是否相等
if (s.equals(num)) {
···
}else {
···
}
trim() 方法去除字符串两端的空格或指定字符
String s = " 1 23 4 ";
s = s.trim();
System.out.print(s);
// 输出
1 23 4
其他方法欢迎补充
缺值处理
- 使用正则表达式统计出现次数
- 排序后取最大最小值
- 使用平均值、中位数等填充缺失数据
其他方法欢迎补充
练习案例
1. 读取 CSV 文件并按第一列分组计数
// 统计demo.csv
下单,0,a,BCF
付款,0,a,BCF
发货,0,a,BCF
付款,0,a,BCF
收款,0,a,BCF
下单,0,a,BCF
付款,0,a,BCF
// CsvDataGrouping.java
package csv;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
public class CsvDataGrouping {
public static void main(String[] args) {
String filepath = "D:\\CSV文件\\统计demo.csv";
try {
// 创建一个BufferedReader对象来读取文件
BufferedReader reader = new BufferedReader(new FileReader(filepath));
// 创建一个Map来存储统计结果
Map<String, Integer> countMap = new HashMap<>();
String line;
while ((line = reader.readLine()) != null) {
// 按逗号分隔每行数据
String[] data = line.split(",");
if (data.length > 0) {
// 去除首尾空格并取得关键字
String key = data[0].trim();
// 统计关键字出现次数
countMap.put(key, countMap.getOrDefault(key, 0) + 1);
}
}
// 输出统计结果
for (Map.Entry<String, Integer> entry : countMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
} catch (Exception e) {
// 处理异常情况
e.printStackTrace();
}
}
}
// 输出
下单: 2
收款: 1
发货: 1
付款: 3
代码大致说明
在这段代码中,我们首先定义了一个文件路径 filepath
,它指向要读取的CSV文件。然后我们使用 BufferedReader
和 FileReader
来创建一个读取文件的对象 reader
。
接下来,我们创建了一个 Map<String, Integer>
类型的对象 countMap
,用于统计每个关键字出现的次数。
然后,我们使用 reader.readLine()
逐行读取文件内容,将每一行按逗号分隔成字符串数组 data
。如果数组长度大于0,说明该行有数据,我们提取关键字 key
。然后使用 countMap.getOrDefault(key, 0) + 1
统计关键字出现的次数,并将结果存入 countMap
中。
最后,我们遍历 countMap
中的键值对,并打印出每个关键字及其出现的次数。
代码详细说明
上述代码是一个Java程序,用于读取指定路径下的CSV文件并统计每个关键字出现的次数。让我们逐步分析代码:
main
方法是程序的入口点,使用了public static void
修饰符,表示它是一个公共静态方法,不返回任何值,接受一个String
类型的数组作为参数。String filepath = "D:\\CSV文件\\统计demo.csv";
定义了一个字符串变量filepath
,用于存储CSV文件的路径。- 在
try-catch
块中,代码通过创建BufferedReader
对象来读取文件内容。使用new BufferedReader(new FileReader(filepath))
,传入一个FileReader
对象,该对象负责读取指定路径下的文件。 - 创建了一个
Map<String, Integer>
类型的变量countMap
,用于存储统计结果。HashMap
类实现了Map
接口,可以存储键值对,这里键的类型是String
,值的类型是Integer
。 - 进入一个
while
循环,读取文件的每一行数据,将读取的结果存储在line
变量中。循环条件为line = reader.readLine()
,当读取到的行不为空时继续循环。 - 在循环中,通过
line.split(",")
使用逗号分隔每一行的数据,并将结果存储在data
数组中。 - 如果
data
数组的长度大于0,表示当前行有数据。代码通过data[0].trim()
去除首尾空格并获取关键字,将结果存储在key
变量中。 - 通过
countMap.getOrDefault(key, 0)
获取countMap
中关键字key
对应的值,如果不存在则返回默认值0。 - 使用
countMap.put(key, countMap.getOrDefault(key, 0) + 1)
将关键字key
的出现次数加1,并将结果保存回countMap
中。 - 循环结束后,通过遍历
countMap
的每个条目,使用entry.getKey()
获取关键字,使用entry.getValue()
获取关键字出现的次数,并将结果打印到控制台。 - 在
catch
块中,代码捕捉并处理可能发生的异常情况,使用e.printStackTrace()
打印异常堆栈信息。
总体而言,这段代码读取指定路径下的CSV文件,统计每个关键字出现的次数,并将结果输出到控制台。
2. 读数据的第一行,输出所有的值、最值、指定数值出现的次数
// iris.csv
Id,SepalLengthCm,SepalWidthCm,PetalLengthCm,PetalWidthCm,Species
1,5.1,1.4,1.4,0.2,Iris-setosa
2,4.9,3,1.4,0.2,Iris-setosa
3,4.7,3.2,1.3,0.2,Iris-setosa
4,4.6,3.1,1.5,0.2,Iris-setosa
5,5,3.6,1.4,0.2,Iris-setosa
// IrisData.java
package csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
public class IrisData {
public static void main(String[] args) {
// 创建文件对象,指向要读取的CSV文件
File file = new File("D:\\CSV文件\\Iris.csv");
String[] result = new String[10];
// 匹配整数或小数的字符串
// -? 表示可选的负号,匹配一个或零个负号。
// \\d+ 匹配一个或多个数字字符。
// (\\.\\d+)? 表示可选的小数部分,包括一个点号和一个或多个数字字符。
String regex = "-?(0|[1-9]\\d*)(\\.\\d+)?";
try {
// 创建FileReader和BufferedReader对象来读取文件内容
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
String string;
int line = 0;
// 逐行读取文件内容
while ((string = br.readLine()) != null) {
// 当行数为1时,将该行按逗号分割成字符串数组并存储在result中
if (line == 1) {
result = string.split(",");
// 数组拷贝方法。从源数组中指定的范围复制一部分元素到一个新的数组中。
// Arrays.copyOfRange(源数组, 起始位置, 终止位置)
// 相当于复制下标1->最后的数组给原字符串,同时改变了原数组的长度
result = Arrays.copyOfRange(result, 1, result.length);
break;
}
line ++;
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
// 创建ArrayList对象用于存储符合条件的数字
ArrayList<Double> numbers = new ArrayList<>();
// 如果34行附近没有对数组进行截取拷贝操作
// 则可以调整循环,使其在迭代结果数组时从索引1而不是0开始
// 这种方式更为简单粗暴
for (int i = 0; i < result.length; i++) {
// 判断字符串是否匹配正则表达式,如果匹配则将其转换为Double类型并添加到numbers中
if (result[i].matches(regex)) {
numbers.add(Double.parseDouble(result[i]));
}
}
// 打印列表中的所有数字
for (int i = 0; i < numbers.size(); i++) {
System.out.println(numbers.get(i));
}
// 输出指定数字在列表中出现的次数
System.out.println("1.4 出现了 " + getCount(numbers, 1.4) + " 次");
// 输出列表中的最大值和最小值
System.out.println("max: " + getMaxAndMin(numbers)[0] + "\nmin: " + getMaxAndMin(numbers)[1]);
}
/**
* 统计给定数值在列表中出现的次数
*
* @param numbers 列表
* @param num 给定数值
* @return 出现次数
*/
public static int getCount(ArrayList<Double> numbers, Double num) {
int count = 0;
for (int i = 0; i < numbers.size(); i++) {
if (numbers.get(i).equals(num)) {
count ++;
}
}
return count;
}
/**
* 获取列表中的最大值和最小值
*
* @param numbers 列表
* @return 包含最大值和最小值的数组,索引0为最大值,索引1为最小值
*/
public static Double[] getMaxAndMin(ArrayList<Double> numbers) {
Double[] max = {numbers.get(0), numbers.get(0)};
for (int i = 0; i < numbers.size(); i++) {
if (numbers.get(i) > max[0]) {
max[0] = numbers.get(i);
}
if (numbers.get(i) < max[1]) {
max[1] = numbers.get(i);
}
}
return max;
}
}
// 输出
5.1
1.4
1.4
0.2
1.4 出现了 2 次
max: 5.1
min: 0.2
代码大致说明
这段代码读取一个CSV文件,并对文件中的数据进行分析。首先,它读取文件的第二行数据(索引为1),将该行按逗号分割成字符串数组 result
,copyOfRange() 方法用于排除第一个元素,它表示ID列。然后存储在 numbers
列表中的元素必须满足正则表达式 regex
的匹配条件(即匹配一个浮点数),才会被添加到 numbers
列表中。
接下来,它使用两个辅助方法 getCount
和 getMaxAndMin
分别计算列表中指定数值出现的次数,以及列表中的最大值和最小值。
在 getCount
方法中,它遍历列表中的元素,如果元素与给定的数值相等,则计数器 count
自增。
在 getMaxAndMin
方法中,它首先将列表的第一个元素作为最大值和最小值的初始值。然后遍历列表中的元素,如果当前元素大于最大值,则更新最大值;如果当前元素小于最小值,则更新最小值。最后,它返回一个包含最大值和最小值的数组。
最后,它打印出列表中的所有数值,以及给定数值 1.4
在列表中出现的次数,以及列表中的最大值和最小值。
代码详细说明
给定的代码是一个Java程序,它从名为"Iris.csv"的CSV文件中读取数据,并对数据执行一些操作。让我们逐步分析代码:
- 代码开始导入必要的类和包。
- 定义了
IrisData
类,其中包含程序执行开始的主方法。 - 创建了一个
File
对象来表示CSV文件的位置。 - 初始化了一个字符串数组
result
,长度为10。该数组将存储从CSV文件中提取的值。 - 定义了一个正则表达式模式
regex
,用于匹配整数或小数字符串。 - 在try-catch块内,创建了
FileReader
和BufferedReader
来读取文件内容。 - 代码进入一个循环,使用
BufferedReader
的readLine()
方法逐行读取文件内容。 - 当行号为1时(行号从0开始),代码使用逗号分隔符拆分行,并将结果数组存储在
result
变量中。使用Arrays.copyOfRange()
方法来排除第一个元素,该元素表示ID列。 - 循环在读取第一行后中断。
- 关闭
BufferedReader
以释放系统资源。 - 创建一个名为
numbers
的ArrayList<Double>
,用于存储从result
数组中提取的数字。 - 循环遍历
result
数组,检查每个元素是否与正则表达式模式匹配。如果找到匹配项,将元素转换为Double
值并添加到numbers
列表中。 - 另一个循环用于打印
numbers
列表中的所有数字。 - 程序调用
getCount()
方法,将numbers
列表和值1.4作为参数传递,并打印1.4在列表中出现的次数。 - 程序调用
getMaxAndMin()
方法,将numbers
列表作为参数传递,并打印列表中的最大值和最小值。 getCount()
方法接受一个ArrayList<Double>
和一个Double
值作为参数,在列表中计算给定值出现的次数。getMaxAndMin()
方法接受一个ArrayList<Double>
作为参数,并返回一个包含列表中最大值和最小值的数组。
总体而言,该代码读取CSV文件,从特定行提取数值,将其存储在一个列表中,并执行一些操作,如计数出现次数和查找最大值和最小值。
3. 读第三个字段的所有值,输出所有的值、最值、指定数值出现的次数
其实就是读第三列的所有值,输出所有的值、最值、指定数值出现的次数。
CSV文件和上一题一样,省略
读文件方式和上一题差不多,省略相同代码,其实也就只是处理时稍微不一样而已
// 逐行读取文件内容
while ((string = br.readLine()) != null) {
// 当行数大于0时,将该行按逗号分割成字符串数组并存储在result中
if (line > 0) {
String[] r = string.split(",");
// 题目要求读第三列的所有值,即下标2的值
result[line-1] = r[2];
}
line ++;
}
// 数组拷贝方法。从源数组中指定的范围复制一部分元素到一个新的数组中。
// Arrays.copyOfRange(源数组, 起始位置, 终止位置)
// 相当于复制下标1->最后的数组给原字符串,同时改变了原数组的长度
result = Arrays.copyOfRange(result, 0, line-1);
br.close();
代码大致说明
这段代码读取一个CSV文件的行,跳过标题行,将每行按逗号分割,并将第三列的值存储在 result
数组中。然后,数组被重新调整大小以删除未使用的元素,并且关闭了 BufferedReader
。
代码详细说明
while
循环使用br.readLine()
逐行读取文件的内容,并将其赋值给变量string
。- 条件
string = br.readLine()
检查string
是否不为空,这意味着仍有待读取的行。 - 在循环内部,代码检查
line
是否大于 0。这确保跳过标题行(第 0 行),只处理数据行。 - 如果
line
大于 0,则代码使用逗号(,
)分隔string
,通过string.split(",")
得到一个值的数组。 - 将
r
数组中索引为 2 的值(表示第三列)赋值给result
数组的相应索引(result[line-1] = r[2]
)。 - 处理完每一行后,递增
line
变量的值。 - 一旦所有行都被处理完,代码使用
Arrays.copyOfRange()
方法创建一个名为result
的新数组,它的长度更小。 Arrays.copyOfRange()
方法从源数组中创建一个新数组,从指定的起始索引(0
)开始,到指定的结束索引(line-1
)结束。这有效地删除了result
数组中未使用的元素。- 更新后的
result
数组被重新赋值给result
变量。 - 最后,代码关闭了
BufferedReader
,以释放系统资源。
参考资料
b站视频
1分钟学java:统计csv文件中的数据:https://www.bilibili.com/video/BV1zX4y1Y7PJ
CSDN文章
java 操作文件及问题解决(csv):https://blog.csdn.net/weixin_43763430/article/details/125346370
致谢
感谢群成员 @电子鬼会吓到仿生人吗 提供的 部分代码 和 CSV文件