时间:2021-07-01 10:21:17 帮助过:8人阅读
题目描述:
笔试题: CSV 文件处理
给定一个 CSV 文件,其内容的展现规则如下:
- 每一行数据包含多个字段,字段间以 [,] 分割。
- 如果字段值不含有 [,] 和 ["] ,直接解析输出。
- 如果字段值内部含有逗号 [,],在在字段值两边加上双引号 ["] 将字段值括起来。
- 如果字段值内部含有双引号 ["],则字段值两边加上双引号 ["] 括起来,同时,将字段值内的一个双引号 ["] 替换为两个双引号 [""],例如: [下棋,"飞"] 在 CSV 文件中被表现为 ["下棋,""飞"""]。
处理要求:
读入文件 cvs.txt,根据上述 csv 文件的规则进行解析,重新格式化字段生成输出文件 output.txt
将
第一列转为整形(int)
第二列为字符串型
第三列为字符串型
第四列转为浮点数(float)
第五列转为日期类型(DateTime)
输出文件的字段以制表符 [TAB] 来分割字段,
字符串字段输出时用单引号[']括起来
日期字段显示成 YYYY/MM/DD 的格式
说明:
1、可以假设字段值只包含单行数据,即字段值本身不含有 [回车换行]
2、不能对文件 csv.txt 作任何修改
编程要求:
使用任何你熟悉的编程语言编写,时间为 1.5 小时。
题目意思:
这个题目意思描述的不是很清楚,就是给你一个格式化后的csv文件,重新还原到刚开始的文件
还有就是,字段值中同时有 [,] 和 ["] 只会在最外层加一次["]
思路:
1. 主要难点在于第二列和第三列如何区分开,因为第二列和第三列都是字符串
2. 区分的思路为,遍历所有[,],如果[,]左边["]的个数和右边["]的个数都为偶数,则这个[,]为分隔第二个字符串和
第三个字符串的分界点
3. 找到分界点后,格式化输出即可
代码:
package com.st.solution.main;
import com.st.solution.util.DateTimeUtil;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: lilimin
* @Date: 2019/6/28 20:09
*/
public class Solution {
public static void main(String[] args) {
String readFileName = "src/main/resources/csv.txt";
String writeFileName = "src/main/resources/output.txt";
readAndWriteFile(readFileName, writeFileName);
}
/**
* 读入文件,格式化
输出
* @param readFileName
* @param writeFileName
*/
public static void readAndWriteFile(String readFileName, String writeFileName) {
BufferedReader reader = null;
BufferedWriter writer = null;
try {
reader = new BufferedReader(new FileReader(new File(readFileName)));
writer = new BufferedWriter(new FileWriter(new File(writeFileName)));
String line = null;
while ((line = reader.readLine()) != null) {
String formatText = formatText(line);
writer.write(formatText);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 格式化每一行数据的输出
* @param text
* @return
*/
public static String formatText(String text) {
// 如果字段值不含有 [,] 和 ["] ,直接解析输出。
if (!text.contains(",") && !text.contains("\"")) {
return text;
}
String[] splitTextArray = text.split(",");
int arrayLength = splitTextArray.length;
List<String> tempList = new ArrayList<String>();
tempList.add(splitTextArray[0]);
// 如果split后的数组长度为4,说明字符中间没有,
if (arrayLength == 4) {
tempList.add(formatStrText(splitTextArray[1]));
tempList.add(formatStrText(splitTextArray[2]));
} else {
// 第2列 或者 第3列 字符中间有,
// 获取第二列和第三列合起来的字符串
StringBuilder middleText = new StringBuilder();
for (int i = 1; i <arrayLength - 3 ; i++) {
middleText.append(splitTextArray[i]).append(",");
}
middleText.append(splitTextArray[arrayLength- 3]);
// 获取第二列和第三列格式化后的字符串
List<String> middleTextList = formatMiddleText(middleText.toString());
tempList.addAll(middleTextList);
}
tempList.add(splitTextArray[arrayLength - 2]);
tempList.add(DateTimeUtil.transferDateformat(splitTextArray[arrayLength - 1]));
return String.join(" ", tempList) + "\n";
}
/**
* 格式化string类型的字符串
* @param text
* @return
*/
public static String formatStrText(String text) {
if (text.startsWith("\"") && text.endsWith("\"")) {
text = text.substring(1, text.length() - 1);
}
text = text.replaceAll("\"\"", "\"");
return "'" + text + "'";
}
/**
* 找出第二列和第三列的分界点[,],并且格式化第二列和第三列
* 原则如下,遍历所有[,],如果某个[,] 左边的["]个数和右边的["]个数都为偶数
* 则一定是第二三列的分界点
* @param text
* @return
*/
public static List<String> formatMiddleText(String text) {
List<String> list = new ArrayList<String>();
String tempText = text;
int index = -1;
while ((index = tempText.indexOf(",", index + 1)) != -1) {
int leftSum = 0;
int rightSum = 0;
for (int i = 0; i < index; i++) {
if (text.charAt(i) == '"') {
leftSum++;
}
}
for (int i = index + 1; i < text.length(); i++) {
if (text.charAt(i) == '"') {
rightSum++;
}
}
if ((leftSum & 1) == 0 && (rightSum & 1) == 0) {
break;
}
}
list.add(formatStrText(text.substring(0, index)));
list.add(formatStrText(text.substring(index + 1)));
return list;
}
}
/**
* @Author: lilimin
* @Date: 2019/6/28 20:09
*/
public class DateTimeUtil {
/**
* 将 yyyy-MM-dd 格式的日期字符串转为 yyyy/MM/dd 格式的日期字符串
* @param timestr
* @return
*/
public static String transferDateformat(String timestr) {
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdf2 = new SimpleDateFormat("YYYY/MM/DD");
Date parse = null;
try {
parse = sdf1.parse(timestr);
} catch (ParseException e) {
e.printStackTrace();
}
return sdf2.format(parse);
}
}
我代码最后的output.txt的内容为
1 'Jane' '下"棋,"飞"' 56.2 1976/08/236
2 'Kate' '购物' 49.6 1980/01/25
3 'Jerry' '羽毛球,爬山' 55.6 1980/05/147
作为一个完整的工程,单测必不可少
/**
* @Author: lilimin
* @Date: 2019/6/29 16:14
*/
public class SolutionTest {
@Test
public void readAndWriteFile() {
String readFileName = "src/main/resources/test1csv.txt";
String writeFileName = "src/main/resources/test1output.txt";
Solution.readAndWriteFile(readFileName, writeFileName);
readFileName = "src/main/resources/test2csv.txt";
writeFileName = "src/main/resources/test2output.txt";
Solution.readAndWriteFile(readFileName, writeFileName);
}
@Test
public void formatStrText() {
String text = Solution.formatStrText("\"\"\"text\"\"\"");
assertEquals("'\"text\"'", text);
}
@Test
public void formatMiddleText() {
List<String> textList = Solution.formatMiddleText("str1,str2");
assertEquals("'str1'", textList.get(0));
assertEquals("'str2'", textList.get(1));
textList = Solution.formatMiddleText("\"str1\",\"str2\"");
assertEquals("'str1'", textList.get(0));
assertEquals("'str2'", textList.get(1));
textList = Solution.formatMiddleText("\"\"\"str1\",\"str2\"");
assertEquals("'\"str1'", textList.get(0));
assertEquals("'str2'", textList.get(1));
}
}
单测的输入和输出如下
test1.csv
2,",Kate",购物,49.6,1979-12-56
3,Jerry,"羽毛球,爬山",55.6,1980-5-26
test1output.txt
2 ',Kate' '购物' 49.6 1980/01/25
3 'Jerry' '羽毛球,爬山' 55.6 1980/05/147
test2.csv
1,Jane,"下""棋,""飞""",56.2,1976-8-23
2,"Kate,Kate","购物,购物",49.6,1979-12-56
3,Jerry,"羽毛球,爬山",55.6,1980-5-26
test2output.txt
1 'Jane' '下"棋,"飞"' 56.2 1976/08/236
2 'Kate,Kate' '购物,购物' 49.6 1980/01/25
3 'Jerry' '羽毛球,爬山' 55.6 1980/05/147
推荐