业务的诞生
最近有些从大数据部门下载的基础数据,并已csv格式存储的,其中一些单个文件中数据量达到200W条。现在需要我把文件重新解析一次。因为其中一个表头对应的值是一个{key1=value1, key2=value2,key3=value3}类型。因为key的值都是定义的码值,所以要把key1、key2、key3等解析出来并翻译成对应的中文名称作为新的表头,然后生成新的csv文件。
优先思考解决思路
1.用Excel打开csv文件最大能展示出1048576行,想让领导能看到所有数据,当然需要拆分文件哦
2.我是将csv拆分成多个且最大900000条数据的csv文件,即便如此,单个文件也达到400M左右,程序处理过程中,要考虑下JVM内存,(我本地跑的程序,Xmx设置成8G),内存太小,肯定会内存溢出的。
3.接下来,当然是要做程序员最爱的事情,写逻辑!!!
csv大文件的拆分
网上找到了一个很好的文件处理工具:Data.olllo
?下载地址:http://olllo.top
操作都是傻瓜式的,就不细说:安装好工具-->打开工具-->打开文件-->拆分文件
代码实现过程
1.导入opencsv包
<!-- csv文件解析依赖 -->
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.4</version>
</dependency>
2.创建CsvUtils工具类
@Slf4j
public class CsvUtils {
private static final String FIELD = "\\w+=(\\-|\\+)?\\d+(\\.\\d+)?";
/**
* 解析csv文件并转成bean(方法二)
*
* @param file csv文件
* @return 数组
*/
public static List<String[]> getCsvDataMethod2(MultipartFile file) {
List<String[]> list = new ArrayList<String[]>();
int i = 0;
try {
CSVReader csvReader = new CSVReaderBuilder(
new BufferedReader(
new InputStreamReader(file.getInputStream(), "utf-8"))).build();
Iterator<String[]> iterator = csvReader.iterator();
while (iterator.hasNext()) {
String[] next = iterator.next();
//去除第一行的表头,从第二行开始
//if (i >= 1) {}
list.add(next);
i++;
}
return list;
} catch (Exception e) {
System.out.println("CSV文件读取异常");
return list;
}
}
/**
* 解析csv文件并转成bean(方法二) 方法的增强,解析某个表头字段(数据是多个key-value),分离成新的多个字段
*
* @param file csv文件
* @return 数组
*/
public static Map<String, Object> getCsvDataMethod2Strong(MultipartFile file) {
Map<String, Object> resultMap = new HashMap<>();
// 重新解析数据,将staticsdatamap字段中的数据,解析成各个不同的字段并重新命名中文名称
// 为了避免staticsdatamap中的各个字段数量不同导致表头不统一,先分析出所有的字段,其数据中没有的字段,则补空
List<String> header = new ArrayList<>();
int staticsdatamapIndex = 0;
// 新增的表头字段map
Map<String, String> staticsdatamapAttributeMap = new HashMap<>();
// 新增的表头字段属性值map
Map<Integer, Map<String, String>> staticsdatamapAttributeValueMap = new HashMap<>();
List<List<String>> list = new ArrayList<>();
int i = 0;
try {
CSVReader csvReader = new CSVReaderBuilder(
new BufferedReader(
new InputStreamReader(file.getInputStream(), "utf-8"))).build();
Iterator<String[]> iterator = csvReader.iterator();
while (iterator.hasNext()) {
List<String> next = new ArrayList(Arrays.asList(iterator.next()));
//第一行的表头,数据从第二行开始
if (i == 0) {
header = next;
// 分析表头字段是staticsdatamap的下标值
for (int j = 0; j < header.size(); j++) {
if ("staticsdatamap".equals(header.get(j))) {
staticsdatamapIndex = j;
}
}
} else {
// 获取staticsdatamap字段对应的值
String staticsdatamapValue = next.get(staticsdatamapIndex);
// 解析staticsdatamap字段对应的值,分析出各个字段值
analysisMap(staticsdatamapAttributeMap, staticsdatamapAttributeValueMap, staticsdatamapValue, i - 1);
list.add(next);
log.info("解析完第 {} 条数据", i);
}
i++;
}
// 根据staticsdatamap解析出来的表头字段以及各个数据对应的数值,重新组装成一个新的List
for (Map.Entry<String, String> entry : staticsdatamapAttributeMap.entrySet()) {
header.add(entry.getValue());
for (int j = 0; j < list.size(); j++) {
if (staticsdatamapAttributeValueMap.containsKey(j)) {
list.get(j).add(staticsdatamapAttributeValueMap.get(j).get(entry.getKey()));
} else {
list.get(j).add("");
}
}
}
resultMap.put("header", header);
resultMap.put("data", list);
return resultMap;
} catch (Exception e) {
System.out.println("CSV文件读取异常");
return resultMap;
}
}
private static void analysisMap(Map<String, String> staticsdatamapAttributeMap,
Map<Integer, Map<String, String>> staticsdatamapAttributeValueMap,
String staticsdatamapValue,
int index) {
// 正则匹配出所有的字段
Pattern pattern = Pattern.compile(FIELD);
Matcher matcher = pattern.matcher(staticsdatamapValue);
while (matcher.find()) {
// 获取单个匹配项,格式为:spn=value
String fieldValue = matcher.group();
// 将单个匹配项拆分成一个list
List<String> keyAndValueList = Arrays.asList(fieldValue.split("="));
// 从SPNMap中查找中文对照关系,如果找到了,则重新创建新的表头字段名: 中文名(SPN),如果没有,则剔除数据
if (SPNMap.getInstance().getMap().containsKey(keyAndValueList.get(0))) {
String newFieldName = SPNMap.getInstance().getMap().get(keyAndValueList.get(0)) + "(" + keyAndValueList.get(0) + ")";
staticsdatamapAttributeMap.put(keyAndValueList.get(0), newFieldName);
if (CollectionUtils.isEmpty(staticsdatamapAttributeValueMap.get(index))) {
Map<String, String> tempMap = new HashMap<>();
tempMap.put(keyAndValueList.get(0), keyAndValueList.get(1));
staticsdatamapAttributeValueMap.put(index, tempMap);
} else {
staticsdatamapAttributeValueMap.get(index).put(keyAndValueList.get(0), keyAndValueList.get(1));
}
}
}
}
/**
* CSV文件生成方法
* @param head 文件头
* @param dataList 数据列表
* @param outPutPath 文件输出路径
* @param filename 文件名
* @return
*/
public static File createCSVFile(List<Object> head, List<List<Object>> dataList,String outPutPath, String filename) {
File csvFile = null;
BufferedWriter csvWtriter = null;
try {
csvFile = new File(outPutPath + File.separator + filename + ".csv");
File parent = csvFile.getParentFile();
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
csvFile.createNewFile();
// GB2312使正确读取分隔符","
csvWtriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
csvFile), "GBK"), 1024);
// 写入文件头部
writeRow(head, csvWtriter);
int i = 0;
// 写入文件内容
for (List<Object> row : dataList) {
writeRow(row, csvWtriter);
i ++;
log.info("成功写入CSV第 {} 条数据", i);
}
csvWtriter.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
csvWtriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return csvFile;
}
/**
* 写一行数据方法
* @param row
* @param csvWriter
* @throws IOException
*/
private static void writeRow(List<Object> row, BufferedWriter csvWriter) throws IOException {
// 写入文件头部
for (Object data : row) {
StringBuffer sb = new StringBuffer();
String rowStr = sb.append("\"").append(data == null ? "" : data).append("\",").toString();
csvWriter.write(rowStr);
}
csvWriter.newLine();
}
}
3.创建一个饿汉式单利类初始化常量集合(涉及隐私安全,所以用key1、key2、key3......代替)
public class SPNMap {
private static final Map<String, String> map = new HashMap<>();
private static SPNMap spnMap = new SPNMap();
private SPNMap() {
}
public static SPNMap getInstance(){
return spnMap;
}
public static Map<String, String> getMap() {
return map;
}
static {
map.put("key1", "value1");
map.put("key1", "value2");
map.put("key1", "value3");
map.put("key1", "value4");
map.put("key1", "value5");
}
}
4.Controller代码
/**
* @program com.example.demo.controller
* @description 公共
* @auther Mr.Xiong
* @create 2020-03-07 12:36
*/
@RestController
@RequestMapping("/common")
public class CommonController {
@Autowired
private CommonService commonService;
@ResponseBody
@RequestMapping("/importCSV")
public void importCSV(MultipartFile file) {
commonService.importCSV(file);
}
}
5.Service
/**
* @program com.example.demo.service
* @description common service
* @auther Mr.Xiong
* @create 2020-03-07 13:10
*/
@Service
public interface CommonService {
void importCSV(MultipartFile file);
}
6.ServiceImpl
/**
* @program com.example.demo.service
* @description common service
* @auther Mr.Xiong
* @create 2020-03-07 13:10
*/
@Slf4j
@Service
public class CommonServiceImpl implements CommonService {
private static final String DIR = "C:\\Users\\50299\\Desktop\\csv";
public void importCSV(MultipartFile file) {
log.info("importCSV --- START");
log.info("获取CSV文件中的所有表头加数据");
Map<String, Object> resultMap = CsvUtils.getCsvDataMethod2Strong(file);
log.info("获取CSV文件中的所有表头");
List<Object> header = (List<Object>) resultMap.get("header");
log.info("获取CSV文件中的所有数据");
List<List<Object>> data = (List<List<Object>>) resultMap.get("data");
log.info("导出新的CSV文件");
CsvUtils.createCSVFile(header, data, DIR, file.getOriginalFilename().split("\\.")[0]);
log.info("importCSV --- END");
}
}
?7.postman调用。。。
?8.整个过程,大概用了一天时间。当一切搞定的时候,感觉还挺有成就感!!!
? ? ? ??
|