POI和EasyExcel区别和操作Excel

POI和EasyExcel操作Excel
常用场景

1、将用户信息导出为excel表格(导出数据… )

2、将Excel表中的信息录入到网站数据库(文件数据上传… )

开发中经常会设计到excel的处理,如导出Excel,导入Excel到数据库中!

操作Excel目前比较流行的就是Apache POI 和 阿里巴巴的 Easyexcel!
Apache POI

Apache POI 官网地址:https://poi.apache.org/会比较麻烦,比较消耗内存出现OOM(内存溢出异常)

在这里插入图片描述

在这里插入图片描述

EasyExcel

EasyExcel官网地址:https://github.com/alibaba/easyexcel

在这里插入图片描述

EasyExcel 是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。

EasyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

内存问题:POI = 100w先加载到内存 OOM。。写入文件 es = 1

下图是 EasyExcel 和 POI 在解析Excel时的对比图。
在这里插入图片描述

EasyExcel 使用POI的sax模式一行一行解析,并将一行的解析结果以观察者的模式通知处理。

在这里插入图片描述

官方文档:https://www.yuque.com/easyexcel/doc/easyexcel

POI操作Excel
POI-Excel写
创建项目

1、创建一个空项目test,创建普通Maven的Moudle demo-poi

2、引入依赖

<dependencies><!--xls(03)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency><!--xls(07)--><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.9</version></dependency><!--日期格式化工具--><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.1</version></dependency><!--test--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>
</dependencies>

03 | 07 版本的写,就是对象不同,方法一样的!

需要注意:2003版本和2007版本存在兼容性的问题!03最多只能有65536行!而07最多只能有1048576行!
在这里插入图片描述
1、工作簿:Workbook

2、工作表:Sheet

3、行:Row

4、列:Cell

03版本:

// 1、创建一个工作簿
Workbook workbook = new HSSFWorkbook();
// 2、创建一个工作表
Sheet sheet = workbook.createSheet("角色统计表");
// 3、创建一个行 (1,1)
Row row1 = sheet.createRow(0);
// 4、创建一个单元格
Cell cell1 = row1.createCell(0);
cell1.setCellValue("角色名称");
// (1,2)
Cell cell2 = row1.createCell(1);
cell2.setCellValue("角色性别");
// (1,3)
Cell cell3 = row1.createCell(2);
cell3.setCellValue("角色创建时间");// 创建第二行 (2,1)
Row row2 = sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("神里凌华");
// (2,2)
Cell cell22 = row2.createCell(1);
cell22.setCellValue("女");
// (2,3)
Cell cell23 = row2.createCell(2);
String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
cell23.setCellValue(time);String PATH = "D:/桌面/test/";
// 生成一张表 (IO 流) 03 版本就是使用 xls结尾!
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "角色统计表03.xls");
// 将IO流写入到Excel中
workbook.write(fileOutputStream);
// 刷新输出流
fileOutputStream.flush();
// 关闭输出流
fileOutputStream.close();
System.out.println("角色统计表03 生成完毕");

运行结果如下:

在这里插入图片描述

// 1、创建一个工作簿
Workbook workbook = new XSSFWorkbook();
// 2、创建一个工作表
Sheet sheet = workbook.createSheet("角色统计表");
// 3、创建一个行 (1,1)
Row row1 = sheet.createRow(0);
// 4、创建一个单元格
Cell cell1 = row1.createCell(0);
cell1.setCellValue("角色名称");
// (1,2)
Cell cell2 = row1.createCell(1);
cell2.setCellValue("角色性别");
// (1,3)
Cell cell3 = row1.createCell(2);
cell3.setCellValue("角色创建时间");// 创建第二行 (2,1)
Row row2 = sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("神里凌华");
// (2,2)
Cell cell22 = row2.createCell(1);
cell22.setCellValue("女");
// (2,3)
Cell cell23 = row2.createCell(2);
String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
cell23.setCellValue(time);String PATH = "D:/桌面/test/";
// 生成一张表 (IO 流) 07 版本就是使用 xlsx结尾!
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "角色统计表07.xlsx");
// 将IO流写入到Excel中
workbook.write(fileOutputStream);
// 刷新输出流
fileOutputStream.flush();
// 关闭输出流
fileOutputStream.close();
System.out.println("角色统计表07 生成完毕");

在这里插入图片描述
注意对象的区别,文件后缀!

大文件写HSSF:

// 开始时间
long begin = System.currentTimeMillis();
// 创建一个簿
Workbook workbook = new HSSFWorkbook();
// 创建表
Sheet sheet = workbook.createSheet();
//写入数据
for (int rowNum = 0; rowNum < 65536; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}
}
System.out.println("over");
String PATH = "D:/桌面/test/";
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData03.xls");
workbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
// 结束时间
long end = System.currentTimeMillis();
System.out.println((double) (end - begin) / 1000);

缺点:最多只能处理65536行,否则会抛出异常

java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)

优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度块
在这里插入图片描述
大文件写XSSF:
缺点:写数据时速度非常慢,非常消耗内存,也会发生内存溢出,如100万条

优点:可以写较大的数据量,如20万条

// 开始时间
long begin = System.currentTimeMillis();
// 创建一个簿
Workbook workbook = new XSSFWorkbook();
// 创建表
Sheet sheet = workbook.createSheet();
//写入数据
for (int rowNum = 0; rowNum < 100000; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}
}
System.out.println("over");
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData07.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
// 结束时间
long end = System.currentTimeMillis();
System.out.println((double) (end - begin) / 1000);

大文件写SXSSF:

优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少内存

注意:

过程中会产生临时文件,需要清理临时文件

默认由100条记录会被保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件

如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)

// 开始时间
long begin = System.currentTimeMillis();
// 创建一个簿
Workbook workbook = new SXSSFWorkbook();
// 创建表
Sheet sheet = workbook.createSheet();
//写入数据
for (int rowNum = 0; rowNum < 100000; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}
}
System.out.println("over");
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "BigData07S.xlsx");
workbook.write(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
// 清除临时文件
((SXSSFWorkbook) workbook).dispose();
// 结束时间
long end = System.currentTimeMillis();
System.out.println((double) (end - begin) / 1000);

SXSSFWorkbook-来至官方的解释:实现"BigGridDemo"策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。

请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释…仍然只存储在内存中,因此如果广泛使用,可能需要大量内存。

再使用 POI的时候!内存问题Jprofile !

POI-Excel读
03 | 07

String PATH = "D:/桌面/test/";
// 获取文件流
FileInputStream fileInputStream = new FileInputStream(PATH + "角色统计表03.xls");
// 1、创建一个工作簿。使用excel能操作的这边他都可以操作!
Workbook workbook = new HSSFWorkbook(fileInputStream);
// 2、通过工作簿获取工作表,表中的设置
Sheet sheet = workbook.getSheetAt(0);
// 获得行
Row row = sheet.getRow(0);
// 获得列
Cell cell = row.getCell(3);
// 读取值的时候,一定需要注意数据类型
// getStringCellValue 字符串类型
//        System.out.println(cell.getStringCellValue());
System.out.println(cell.getNumericCellValue());
// 关闭文件流
fileInputStream.close();

07版本

String PATH = "D:/桌面/test/";
// 获取文件流
FileInputStream fileInputStream = new FileInputStream(PATH + "角色统计表07.xlsx");
// 1、创建一个工作簿。使用excel能操作的这边他都可以操作!
Workbook workbook = new XSSFWorkbook(fileInputStream);
// 2、通过工作簿获取工作表,表中的设置
Sheet sheet = workbook.getSheetAt(0);
// 获得行
Row row = sheet.getRow(0);
// 获得列
Cell cell = row.getCell(2);
// 读取值的时候,一定需要注意数据类型
// getStringCellValue 字符串类型
System.out.println(cell.getStringCellValue());
// 关闭文件流
fileInputStream.close();

EasyExcel操作Excel
导入依赖

<dependencies><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.0-beta2</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
</dependencies>

写入测试
创建写入的实体类

@Data
public class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;/*** 忽略这个字段*/@ExcelIgnoreprivate String ignore;
}

创建写入的测试类

public class DemoDataWriteTest {private List<DemoData> data() {List<DemoData> list = new ArrayList<DemoData>();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();data.setString("字符串" + i);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;}/*** 最简单的写* <p>1. 创建excel对应的实体对象 参照{@link DemoData}* <p>2. 直接写即可*/@Testpublic void simpleWrite() {// 写法1String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());// 写法2fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();excelWriter.write(data(), writeSheet);// 千万别忘记finish 会帮忙关闭流excelWriter.finish();}private static class TestFileUtil {public static String getPath() {return "D:\\桌面\\test\\";}}
}

在这里插入图片描述
EasyExcel读
创建读取的实体类

@Data
public class DemoData {private String string;private Date date;private Double doubleData;
}

创建读取的监听器类

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);/*** 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 5;List<DemoData> list = new ArrayList<DemoData>();/*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/private DemoDAO demoDAO;public DemoDataListener() {// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数demoDAO = new DemoDAO();}/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来** @param demoDAO*/public DemoDataListener(DemoDAO demoDAO) {this.demoDAO = demoDAO;}/*** 这个每一条数据解析都会来调用** @param data*            one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));list.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (list.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listlist.clear();}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();LOGGER.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {LOGGER.info("{}条数据,开始存储数据库!", list.size());demoDAO.save(list);LOGGER.info("存储数据库成功!");}
}

创建读取的持久层类

/*** 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。**/
public class DemoDAO {public void save(List<DemoData> list) {// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入}
}

创建读取的测试类

public class DemoDataReadTest {/*** 最简单的读* <p>1. 创建excel对应的实体对象 参照{@link DemoData}* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}* <p>3. 直接读即可*/@Testpublic void simpleRead() {// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去// 写法1:String fileName = TestFileUtil.getPath() + "test" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();// 写法2:fileName = TestFileUtil.getPath() + "test" + File.separator + "demo.xlsx";ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();ReadSheet readSheet = EasyExcel.readSheet(0).build();excelReader.read(readSheet);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();}private static class TestFileUtil {public static String getPath() {return "D:\\桌面\\";}}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/280473.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙Harmony应用开发—ArkTS-转场动画(组件内隐式共享元素转场)

geometryTransition用于组件内隐式共享元素转场&#xff0c;在组件显示切换过程中提供平滑过渡效果。通用transition机制提供了opacity、scale等转场动效&#xff0c;geometryTransition通过id绑定in/out组件(in指入场组件、out指出场组件)&#xff0c;使得组件原本独立的trans…

Gateway新一代网关

Gateway新一代网关 1、概述 ​ Cloud全家桶中有个很重要的组件就是网关&#xff0c;在1.x版本中都是采用的Zuul网关&#xff1b; ​ 但在2.x版本中&#xff0c;zuul的升级一直跳票&#xff0c;SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul。 ​ 官网&…

手机运营商二要素检测:重塑信任基石,筑牢信息安全屏障

随着移动互联网的普及和数字经济的快速发展&#xff0c;用户信息安全的重要性日益凸显。运营商二要素检测作为一种强大的安全验证机制&#xff0c;以其精准匹配与实时验证的特性&#xff0c;为各类应用场景提供了一种可靠的身份识别解决方案&#xff0c;正在成为众多企业和服务…

C++:继承:面向对象编程的重要特性

(❁◡❁)(●◡●)╰(*▽*)╯(*/ω&#xff3c;*)(^///^)(❁◡❁)(❁◡❁)(●◡●)╰(*▽*)╯(*/ω&#xff3c;*)(❁◡❁)(●’◡’●)╰(▽)╯(/ω&#xff3c;)(///) C&#xff1a;继承&#xff1a;面向对象编程的重要特性 前言**继承**1.继承的概念及定义1.1继承的概念1.2继…

Redis6.0多线程的疑惑解答

1.Redis6.0之前的版本真的是单线程吗&#xff1f; Redis在处理客户端的请求是&#xff0c;包括获取(socket读)、解析、执行、内容返回(socket 写)等都有一个 顺序串行的主线程处理&#xff0c;这就是所谓的"单线程"。但如果严格来讲并不是单线程&#xff0c;除了主线…

SpringMVC学习笔记

SpringMVC 本篇笔记是基于尚硅谷学习资料的整理&#xff0c;涉及到其笔记的简化&#xff0c;补充&#xff0c;以及我在学习中遇到的与无法理解的问题及解决&#xff0c;如果想看完整及后续的笔记&#xff0c;可以去https://www.wolai.com/v5Kuct5ZtPeVBk4NBUGBWF查看官方笔记。…

演讲嘉宾公布 | 3D音频专题论坛将于3月27日举办

一、3D音频专题论坛 3D音频技术不仅能够提供更加真实、沉浸的虚拟世界体验&#xff0c;跨越时空的限制&#xff0c;探索未知的世界。同时&#xff0c;提供更加丰富、立体的情感表达和交流方式&#xff0c;让人类能够更加深入地理解彼此&#xff0c;建立更加紧密的联系。3D音频未…

MongoDB系列之查询计划

概述 一个查询具体如何被执行的过程&#xff0c;称为查询计划。MongoDB采用自底向上的方式来构造查询计划&#xff0c;每一个查询计划&#xff08;query plan&#xff09;都会被分解为若干个有层次的阶段&#xff08;stage&#xff09;。整个查询计划最终会呈现出一颗多叉树。…

Windows管理多版本nodejs方法

参考&#xff1a;window操作系统安装多个版本的nodejs——nodejs版本控制工具nvm_windows node多版本共存-CSDN博客 前排&#xff1a; 建议把原来电脑装的nodejs卸载干净&#xff0c;再干这个步骤&#xff0c;可以避免出现一些状况&#xff1b;&#xff1b;&#xff1b;另&…

免费PDF转换和编辑工具 PDFgear 2.1.4

PDFgear是一款功能强大的 PDF 阅读及转换软件。 它支持多种文件格式的转换和编辑&#xff0c;同时还提供了丰富的功能模块&#xff0c;如签名、表单填写等&#xff0c;方便用户进行多样化的操作。 该软件界面简洁美观&#xff0c;操作简单易懂&#xff0c;适合不同层次的用户…

【论文阅读】通过组件对齐评估和改进 text-to-SQL 的组合泛化

Measuring and Improving Compositional Generalization in Text-to-SQL via Component Alignment NAACL 2022| CCF B Abstract 在 text-to-SQL 任务中&#xff0c;正如在许多 NLP 中一样&#xff0c;组合泛化是一个重大挑战&#xff1a;神经网络在训练和测试分布不同的情况…

iostream、fstream、sstream、string、vector、unordered_map、stack

iostream 用于输入输出操作&#xff0c;包含了处理标准输入输出流的功能&#xff08;例如&#xff0c;cin, cout, cerr等&#xff09;。 #include <iostream>int main() {int number;std::cout << "Enter a number: ";std::cin >> number;std::…

基于Springboot的防疫物资管理信息系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的防疫物资管理信息系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

docker部署grafana graphite stastd实时监控告警配置_亲测成功

docker部署grafana graphite stastd实时监控告警配置_亲测成功 实时监控请求的次数和响应时间及异常报警 项目中需要监控API接口的调用情况,如:调用次数,每次调用耗时,调用高峰期,异常报警等 grafana graphite stastd这一套对代码没有侵入,也不会影响接口性能 安装docker环…

【C语言】【牛客】BC136 KiKi判断上三角矩阵

文章目录 题目 BC136 KiKi判断上三角矩阵思路代码呈现 题目 BC136 KiKi判断上三角矩阵 链接: link 思路 这题很简单但是再牛客中属于中等题 我们通过读题发现 2<n<10 &#xff0c;所以我们首先创建一个变量 n 以及一个 10*10 个元素数组 然后题目是判断该矩阵是否是…

Fetch、Axios 和 jQuery(Ajax) 三种常用的网络请求技术

Fetch、Axios 和 jQuery(Ajax) 是三种常用的网络请求技术&#xff0c;它们各自有着不同的特点和优势。本文将对这三种技术进行详细的介绍和比较&#xff0c;以帮助开发者更好地选择和使用合适的网络请求技术。 一、Fetch Fetch(浏览器自带) 是一种现代的网络请求 API&#xff…

强大的文本编辑器:Sublime Text for Mac注册激活版

Sublime Text for Mac是一款功能强大的文本编辑器&#xff0c;特别适合程序员和开发者使用。它提供了丰富的功能&#xff0c;如智能代码补全、语法高亮、自定义快捷键、项目管理、多行选择、自动保存等&#xff0c;以提高代码编写效率和舒适度。此外&#xff0c;Sublime Text还…

SQLiteC/C++接口详细介绍sqlite3_stmt类(四)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;三&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;五&#xff09; 7. sqlite3_bind_parameter_count函数 sqlite3_bind_param…

数据库设计三大范式

第一范式 确保每列保持原子性 即数据库表中的所有字段值都是不可分解的原子值 如果地址这个字段频繁访问&#xff0c; 则将地址这个属性重新划分为 省份 城市&#xff0c;详细地址等部分进行存储&#xff0c;这样才算是满足数据库的第一范式 第二范式 确保表中的每列都和主…

SpringCloud中的@EnableDiscoceryClient和@EnableFeignClients注解的作用解析、RPC远程过程调用

目录 EnableDiscoveryClient 服务发现的核心概念 服务注册中心 EnableDiscoveryClient注解的作用 服务心跳健康检查 使用示例 EnableFeignClients Feign简介 EnableFeignClients注解的作用 RPC&#xff08;Remote Procedure Call&#xff09; 参考链接 Spring Cloud…