POI、Easy Excel操作Excel

news2024/10/7 12:24:51

文章目录

  • 1.常用的场景
  • 2.基本功能
  • 3.Excel在Java中是一个对象
  • 4. 简单的写(07版本(.xlsx)Excel)
    • 大文件写HSSF
    • 大文件写XSSF
    • 大文件写SXSSF
  • 5. Excel读
    • 5.1 读取遇到类型转化问题该怎么解决
    • 5.2 遇到Excel公式怎么办
  • 6. Easy Excel
    • 6.1简单的写
    • 6.2 简单的读

1.常用的场景

    1. 将用户信息导出为Excel表格(导出数据)
    1. 将Excel表中的信息录入到网站数据库中(习题上传)大大减轻网站录入量。开发中经常会用到设计处理Excel,如导入Excel和导出Excel
    1. 目前操作Excel比较流行的就是Apach的POI阿里巴巴的Easy Excel
    1. POI官网:https://poi.apache.org/
    1. Easy Excel官网:https://github.com/alibaba/easyexcel

2.基本功能

  • 1.POI的基本功能:(会比较麻烦,可能会出现OOM(Out Of Memory)异常)
    在这里插入图片描述
    其中:第一个HSSF和XSSF的区别(前者用03版本的Excel,后者用07年的Excel)
    • 1.1 内存问题:
      POI 当数据为100w的时候,先将100w的数据先加载到内存当中(OOM),再写入文件
    1. Easy Excel:
    • 2.1 简介,官方文档:(已经迁移到)https://easyexcel.opensource.alibaba.com/docs/current/
      在这里插入图片描述
    • 2.2 内存问题:
      相对于POI来说,上图反映了POI会把所有内容加载到内存当中,而Easy Excel会一行一行通过磁盘返回。

3.Excel在Java中是一个对象

在这里插入图片描述

    1. 工作簿
    1. 工作表

4. 简单的写(07版本(.xlsx)Excel)

03版本代码中有标注,生成文件时候,需要改后缀为:(.xls)

package com.dapeng;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.joda.time.DateTime;
import org.junit.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/13 9:49
 */
public class ExcelWriteTest {
    @Test
    public void testWrite() throws Exception {
        String PATH = "D:\\ExcelTest\\";
        // 1.创建一个工作簿(SXSSF优化版的07的工作簿,HSSF是03年的)
        // Workbook workbook = new HSSFWorkbook();03版本
        Workbook workbook = new SXSSFWorkbook();
        // 2. 创建一个工作表
        Sheet sheet = workbook.createSheet("我的工作表");
        // 3. 创建一行(0代表从第一行开始创建)
        Row row1 = sheet.createRow(0);
        // 4. 创建一列(0代表从第一列开始创建),Cell代表一个单元格
        Cell cell11 = row1.createCell(0);
        Cell cell12 = row1.createCell(1);
        // 5. 给单元格赋值
        cell11.setCellValue("测试数据1-1");
        cell12.setCellValue("Cell12数据");

        // 来个第二行
        Row row2 = sheet.createRow(1);
        Cell cell21 = row2.createCell(0);
        Cell cell22 = row2.createCell(1);
        // 来个时间
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell21.setCellValue(time);
        cell22.setCellValue("第二行第二个数据");

        // 生成一张表
        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "test.xlsx");
        workbook.write(fileOutputStream);

		fileOutputStream.close();
    }
}

效果:
在这里插入图片描述

大文件写HSSF

  • 优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快
  • 缺点:最多只能处理65535行,否则会抛出异常:
java.lang.IlleaglArgumentException:Invalid row number(65535) outside allowable rang (0..65535)
  • 代码截图:(没有自己写–自己没有03版本的excel)
    在这里插入图片描述
  • 当超过65536 的时候会报错
    在这里插入图片描述

大文件写XSSF

  • 优点:可以写较大的数据量,如20万条。
  • 缺点:写数据时非常慢,非常耗内存,也会发生内存溢出,如100万条。
  @Test
    public void testWrite07BigData() throws Exception {
        long startTime = System.currentTimeMillis();
        // 1.创建一个工作簿(SXSSF优化版的07的工作簿,HSSF是03年的)
        Workbook workbook = new XSSFWorkbook();
        // 2. 创建一个工作表
        Sheet sheet = workbook.createSheet("我的工作表");
        // 3.循环写入大数据
        for (int i = 0; i < 100000; i++) {
            // 创建行
            Row row = sheet.createRow(i);
            for (int j = 0; j < 10; j++) {
                // 创建列j
                Cell cell = row.createCell(j);
                // 给列赋值
                cell.setCellValue(j);
            }
        }
        System.out.println("完事了");

        // 生成一张表
        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07BigData.xlsx");
        workbook.write(fileOutputStream);
        fileOutputStream.close();

        long end = System.currentTimeMillis();
        System.out.println("花费了:" + (double)(end - startTime)/1000 + "秒");
    }

大文件写SXSSF

  • 优点:可以写非常大的数据量,如100万条甚至更多条,写数据快,占用更少的内存。
    在这里插入图片描述
  • Demo(没有多大变化,只用了SXSSFWorkbook类)
 @Test
    public void testWrite07BigData() throws Exception {
        long startTime = System.currentTimeMillis();
        // 1.创建一个工作簿(SXSSF优化版的07的工作簿,HSSF是03年的)
        Workbook workbook = new SXSSFWorkbook();
        // 2. 创建一个工作表
        Sheet sheet = workbook.createSheet("我的工作表");
        // 3.循环写入大数据
        for (int i = 0; i < 1000000; i++) {
            // 创建行
            Row row = sheet.createRow(i);
            for (int j = 0; j < 10; j++) {
                // 创建列j
                Cell cell = row.createCell(j);
                // 给列赋值
                cell.setCellValue(j);
            }
        }
        System.out.println("完事了");

        // 生成一张表
        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07BigData.xlsx");
        workbook.write(fileOutputStream);
        fileOutputStream.close();
  		 // 清除临时文件
        ((SXSSFWorkbook)workbook).dispose();
        long end = System.currentTimeMillis();
        System.out.println("花费了:" + (double)(end - startTime)/1000 + "秒");
    }

5. Excel读

  • Demo:
package com.dapeng;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;

import java.io.FileInputStream;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/13 10:43
 */
public class ExcelReadTest {
    String PATH = "D:\\ExcelTest\\";

    @Test
    public void testRead() throws Exception{
        // 1. 获取文件流读取文件
        FileInputStream inputStream = new FileInputStream(PATH + "test.xlsx");
        // 2. 创建一个工作簿
        Workbook workbook = new XSSFWorkbook(inputStream);
        // 3. 得到表(通过下标);
        Sheet sheet = workbook.getSheetAt(0);
        // 4. 得到行(第一行)
        Row row = sheet.getRow(0);
        // 5. 获取第一个数据
        Cell cell = row.getCell(0);
        // 6.输出数据
        System.out.println(cell.getStringCellValue());

        inputStream.close();

    }
}

5.1 读取遇到类型转化问题该怎么解决


    @Test
    public void testReadTeyp() throws Exception {
        // 1. 获取文件流读取文件
        FileInputStream inputStream = new FileInputStream(PATH + "test.xlsx");
        // 2. 创建一个工作簿
        Workbook workbook = new XSSFWorkbook(inputStream);
        // 3. 得到表(通过下标);
        Sheet sheet = workbook.getSheetAt(0);
        // 4. 获取第一行标题
        Row rowTitle = sheet.getRow(0);
        //      4.1 获取标题的数量
        if (rowTitle != null) {
            // 必须掌握,拿到所有的列
            int cells = rowTitle.getPhysicalNumberOfCells();
            for (int i = 0; i < cells; i++) {
                Cell cell = rowTitle.getCell(i);
                if (cell != null) {
                    int cellType = cell.getCellType();
                    String cellValue = cell.getStringCellValue();
                    System.out.print(cellValue + " | ");
                }
            }
        }
        // 获取行的总数
        int rowCounts = sheet.getPhysicalNumberOfRows();
        for (int rowNum = 1; rowNum < rowCounts; rowNum++) {
            // 获取每一行
            Row row = sheet.getRow(rowNum);
            if (row != null) {
                // 读取列
                int columns = rowTitle.getPhysicalNumberOfCells();
                for (int cellColumn = 0; cellColumn < columns; cellColumn++) {
                    System.out.print("[" + rowNum + "-" + cellColumn + "]");
                    // 获取每一个数据
                    Cell cell = row.getCell(cellColumn);
                    // 匹配列数据类型
                    if (cell != null) {
                        int cellType = cell.getCellType();
                        String cellValue = "";

                        switch (cellType) {
                            case HSSFCell.CELL_TYPE_STRING: // 字符串
                                System.out.print("[String]");
                                cellValue = cell.getStringCellValue();
                                break;
                            case HSSFCell.CELL_TYPE_BOOLEAN: // 布尔类型
                                System.out.print("[BOOLEAN]");
                                cellValue = String.valueOf(cell.getBooleanCellValue());
                                break;
                            case HSSFCell.CELL_TYPE_BLANK: // 空
                                System.out.print("[BLANK]");
                                break;
                            case HSSFCell.CELL_TYPE_NUMERIC: // 数字(日期和数字)
                                System.out.print("[NUMERIC]");
                                if (HSSFDateUtil.isCellDateFormatted(cell)) { // 如果是日期类型
                                    System.out.print("[日期]");
                                    Date time = cell.getDateCellValue();
                                    // 使用工具类把日期进行转换
                                    cellValue = new DateTime(time).toString("yyyy-MM-dd HH:mm:ss");
                                } else {
                                    // 如果不是日期格式,防止数字过长
                                    System.out.print("[转换为字符串输出]");
                                    cell.setCellType(HSSFCell.CELL_TYPE_STRING);
                                    cellValue = cell.toString();
                                }
                                break;
                            case HSSFCell.CELL_TYPE_ERROR: // 错误
                                System.out.print("[数据类型错误]");
                                break;
                        }
                        System.out.println(cellValue);
                    }
                }
            }
        }

        inputStream.close();

    }

5.2 遇到Excel公式怎么办

   @Test
    public void readMath() throws Exception{
        FileInputStream inputStream = new FileInputStream(PATH + "test.xlsx");

        // 读取
        Workbook workbook = new XSSFWorkbook(inputStream);
        Sheet sheet = workbook.getSheetAt(0);

        // 这个单元格前提有公式比如=SUM(A2:A4);
        Row row = sheet.getRow(4);
        Cell cell = row.getCell(0);

        // 1.拿到该工作簿的公式
        XSSFFormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);


        // 输出单元格内容,先获取类型
        int cellType = cell.getCellType();
        switch (cellType){
            case HSSFCell.CELL_TYPE_FORMULA: //公式
                // 先获取公式
                String formula = cell.getCellFormula();
                System.out.println(formula); // 输出公式

                // 计算得到值
                CellValue evaluate = formulaEvaluator.evaluate(cell);
                System.out.println(evaluate);// 输出计算好的值
                String value = evaluate.formatAsString();// 把值转化为String输出
                System.out.println(value);
                break;


        }
    }

6. Easy Excel

6.1简单的写

参考官方文档:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write

  1. 导入依赖
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>easyexcel</artifactId>
  <version>2.2.0-beta2</version>
</dependency>
  1. 数据来源:
  private List<Entity> data() {
        List<Entity> list = new ArrayList<Entity>();
        for (int i = 0; i < 10; i++) {
            Entity data = new Entity();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
  1. 一行代码导出Excel
 // 根据List写入值
    @Test
    public void simpleWrite(){
        // 1. 当前的位置
        String PATH = "D:\\ExcelTest\\";
        String fileName= PATH + "esayExcel.xlsx";
        EasyExcel.write(fileName,Entity.class).sheet("easyExcel").doWrite(data());
    }

6.2 简单的读

参考:

  1. 先写一个类继承AnalysisEventListener并重写invoke方法,数据的处理逻辑全在这里。
package com.dapeng.DemoEntity;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/13 15:31
 */
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener extends AnalysisEventListener<DemoData> {

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<DemoData> list = new ArrayList<>();

    private DemoDAO demoDAO;

    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }


    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        System.out.println("解析到一条数据:{}" +  JSON.toJSONString(data));
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }
    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", list.size());
        demoDAO.save(list);
        log.info("存储数据库成功!");
    }
}

  1. 测试:
    /**
     * 最简单的读
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
     * <p>
     * 3. 直接读即可
     */
    @Test
    public void simpleRead() {
        // 写法1:JDK8+ ,不用额外写一个DemoDataListener
        // since: 3.0.0-beta1
        String PATH = "D:\\ExcelTest\\";
        String fileName= PATH + "esayExcel.xlsx";
        // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
        // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();



    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Python]黑色背景白色块滑动视频

黑色背景白色块滑动视频&#xff0c;单帧效果如下&#xff1a; 配置参数 1920 1080 400 400 300 60 1920x1080.avi import numpy as np import cv2 as cv import os import syswidth 1920 height 1080 rect_szx 400 rect_szy 300 sz_y_init 400 fps 24width int(sys.a…

如何使用ChatPPT生成PPT文档

简介 ChatPPT是一个基于人工智能的PPT生成工具&#xff0c;可以帮助用户快速生成高质量的PPT文档。ChatPPT使用自然语言处理技术&#xff0c;可以根据用户的指令生成PPT内容、设计和排版。 使用方法 ChatPPT提供了两种使用方式&#xff1a;在线体验版和Office插件版。 在线…

pytest 之 pytest.ini配置文件

前言&#xff1a;pytest.ini全局配置文件&#xff0c;是pytest单元测试框架的核心配置文件&#xff0c;pytest.ini 可以改变 pytest 的默认行为 一、配置规则 1、优先级 如pytest.ini有该参数值&#xff0c;在执行的时候&#xff0c;优先读取配置文件中的参数如没有&#xf…

ADAS可视化系统,让自动驾驶更简单 -- 入门篇

随着车载芯片的升级、技术的更新迭代&#xff0c;可视化ADAS逐渐变成汽车的标配走入大家的生活中&#xff0c;为大家的驾车出行带来切实的便捷。那么你了解HMI端ADAS的实现过程吗&#xff1f;作为ADAS可视化系统的入门篇&#xff0c;就跟大家聊一聊目前较常见的低消耗的一种ADA…

Android---深入理解AQS和CAS原理

AQS AQS 全称是 Abstract Queued Synchronizer&#xff0c;一般翻译为同步器。是一套实现多线程同步功能的框架&#xff0c;由 Doug Lea 操刀设计并开发实现的。AQS 在源码中被广泛使用&#xff0c;尤其是在 JUC&#xff08;Java Util Concurrent&#xff09;中&#xff0c;比…

allure测试报告生成逻辑--解决在Jenkins里打开allure报告页面后空白显示无数据问题(以window环境为例)

前言 相信大家在用Jenkins持续集成+ant自动构建+jmeter接口测试+pytest代码.xml文件转化+allure测试报告为一体的接口自动化测试构建过程中,都会遇到Jenkins里打开allure报告页面后空白显示无数据问题这一现象级问题,今天Darren洋就给大家分享一下如何讲讲allure测试报告生成…

MySQL——源码安装教程

MySQL 一、MySQL的安装1、RPM2、二进制3、源码 二、源码安装方式三、安装过程1、上传源码包2、解压当前文件并安装更新依赖3、对MySQL进行编译安装 四、其他步骤 一、MySQL的安装 首先这里我来介绍下MySQL的几种安装方式&#xff1a; 一共三种&#xff0c;RPM安装包、二进制包…

通过okhttp调用SSE流式接口,并将消息返回给客户端

通过一个完整的java示例来演示如何通过okhttp来调用远程的sse流式接口 背景&#xff1a;我们有一个智能AI的聊天界面&#xff0c;需要调用三方厂商的大模型chat接口&#xff0c;返回答案&#xff08;因为AI去理解并检索你的问题的时候这个是比较耗时的&#xff0c;这个时候客户…

10-SRCNN-使用CNN实现超分辨成像

文章目录 utils_dataset.pymodel.pytrain.pyuse.py主要文件 utils_dataset.py 工具文件,主要用来制作dataset,便于加入dataloader,用于实现数据集的加载和并行读取 model.py 主要写入网络(模型) train.py 主要用于训练 use.py 加载训练好的模型,用于测试或使用 utils_dat…

Spring实战 | Spring AOP核心秘笈之葵花宝典

Spring实战系列文章&#xff1a; Spring实战 | Spring IOC不能说的秘密&#xff1f; 国庆中秋特辑系列文章&#xff1a; 国庆中秋特辑&#xff08;八&#xff09;Spring Boot项目如何使用JPA 国庆中秋特辑&#xff08;七&#xff09;Java软件工程师常见20道编程面试题 国庆…

IDEA的常用设置

【1】进入设置&#xff1a; 【2】设置主题&#xff1a; 【3】编辑区的字体变大或者变小&#xff1a; 【4】鼠标悬浮在代码上有提示&#xff1a; 【5】自动导包和优化多余的包&#xff1a; 手动导包&#xff1a;快捷键&#xff1a;altenter 自动导包和优化多余的包&#xf…

计算机网络第2章-HTTP和Web协议(2)

Web和HTTP 一个新型应用即万维网&#xff08;World Wide Web&#xff09;Web。 HTTP概况 Web的应用层协议是超文本传输协议&#xff08;HTPP&#xff09;&#xff0c;它是Web的核心。 HTTP由两个程序实现&#xff1a;一个用户程序和一个服务器程序。 Web页面&#xff08;W…

leetcode-518. 零钱兑换 II

1. 题目 链接: 零钱兑换II 2. 解决方案1 #include <stdio.h> #include <stdlib.h>int change(int amount, int* coins, int coinsSize){int dp[amount1];//确定dp大小memset(dp, 0, sizeof(int) * (amount1));dp[0] 1;//初始化为0for(int i 0 ; i < coins…

LED电子屏幕可以通过什么方式进行人屏互动

传统的LED大屏幕以单向传播的形式面向观众&#xff0c;不仅被动&#xff0c;而且逐渐缺乏动感和创新。随着LED显示技术的蓬勃发展&#xff0c;现在观众与LED电子大屏幕的方式越来越多。那么现阶段实现LED显示屏人屏互动的主要方式都有哪些呢&#xff1f;带你8分钟了解LED互动地…

KASan介绍

目录 概括介绍 配置说明 单独关闭读或写检查 操作使用 影响及注意事项 结果解读 使用注意 实现原理简介 KASAN原理 malloc原理 内容参考 概括介绍 KernelAddressSANitizer &#xff08;KASAN&#xff09; 是一个动态内存错误检测器。它提供了一个快速而全面的解决方…

D课堂 | 如何设置域名解析?解析记录类型选哪个?

上回&#xff0c;D妹和各位小伙伴们介绍了DNS的作用和原理——《什么是DNS&#xff1f;DNS是怎么运作的&#xff1f;》&#xff0c;相信大家对DNS已经有了一定的认识。 DNS是互联网不可或缺的基础服务&#xff0c;核心作用是将域名翻译成计算机可读取的IP地址&#xff0c;也就是…

VMware搭载linux出现的bugs

---------后续在实际Linux项目复盘过程中有遇到问题(解决办法)会不定时更新.......----------- ques: Linux自带的media目录用于挂载或可移动存储设备已满&#xff08;造成这一原因是由于我多次创建新的虚拟机并在同一虚拟目录下挂载同一镜象导致有些残存文件没有删除干净&…

【OpenCv光流法进行运动目标检测】

opencv系列文章目录 文章目录 opencv系列文章目录前言一、光流法是什么&#xff1f;二、光流法实例1.C的2.C版本3.python版本 总结 前言 随着计算机视觉技术的迅猛发展&#xff0c;运动目标检测在图像处理领域中扮演着至关重要的角色。在现实世界中&#xff0c;我们常常需要追…

JDK21要来了,协程对Java带来什么

目录 前言 协程是什么 多线程有什么问题&#xff1f; 协程的线程模型 Reactor模型 使用协程后 RPC并发 IO阻塞 网络IO 磁盘IO epoll为什么不支持磁盘io&#xff1f; Kotlin与Go的协程 Go 使用 Go的协程调度(GPM模型) Kotlin 使用 Kotlin协程调度 阿里Wisp协程…

Linux程序调试工具使用整理

Linux程序调试工具使用整理 GDB调试入门 GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许&#xff0c;各位比较喜欢那种图形界面方式的&#xff0c;像VC、BCB等IDE的调试&#xff0c;但如果你是在 UNIX平台下做软件&#xff0c;你会发现GDB这个调试工具有比VC、…