java 输入流编码格式_java获取inputstream输入流是什么编码格式

错误笔记:

一开始使用的方法,正常运行没多久,出现一个情况,GBK文件会导致csv文件的第一行第一列乱码,百思不得其解,最终发现是因为在源头,早已使用byte,inputstream。read读取几个字节,所以导致后面的乱码。

错误示例如下:

文件:12345.csv,格式为GBK

主键,密码,创建时间,创建人,修改时间,修改人,是否删除

中文,123456,null,null,null,null,false

错误部分代码:

/**

* CSV文件编码

*/

private static final String ENCODE = "UTF-8";

/**

* GBK编码

* */

private static final String ENCODE_GBK = "GBK";

public static List getLines(InputStream fileName) {

List stringList=null;

try {

//判断文件格式

byte[] bytes=new byte[3];

fileName.read(bytes);

if(bytes[0]==-17&&bytes[1]==-69&&bytes[2]==-65){

stringList=getLines(fileName, ENCODE);

}else{

stringList= getLines(fileName, ENCODE_GBK);

}

}catch (Exception e){

log.error("解析编码格式异常:"+e.getMessage());

}finally {

try {

if (fileName != null) {

fileName.close();

}

}catch (IOException e) {

log.error("解析编码格式异常Close stream failure :{}", e);

}

}

return stringList;

}

错误源头就在于,byte[] bytes=new byte[3];原本是想这样去判断bytes是什么编码格式,这样就会导致后面,丢失字节,造成乱码。后改为如下正确完整代码。

import lombok.extern.slf4j.Slf4j;

import org.apache.any23.encoding.TikaEncodingDetector;

import java.io.*;

import java.nio.charset.Charset;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

@Slf4j

public class CSVFileUtil {

/**

* CSV文件编码

*/

private static final String ENCODE = "UTF-8";

/**

* GBK编码

* */

private static final String ENCODE_GBK = "GBK";

/**

* 读取CSV文件得到List,默认使用UTF-8编码

* @param fileName 文件路径

* @return

* 针对编码格式做处理,谨记关闭流,所以在此处初始化变量list,在获取行数方法执行完毕后,finally关闭流。

* 谨记对于流处理,如在调用getLines方法前,采用byte或者其他方法read之后,传参数fileName,其实已经把流读取完毕,为空引起报错。

*/

public static List getLines(InputStream fileName) {

List stringList=null;

ByteArrayOutputStream arraystream=ToolExcelUtils.cloneInputStream(fileName);

InputStream inputStream=null;

InputStream stream=new ByteArrayInputStream(arraystream.toByteArray());

Charset charset= guessCharset(stream);

try {

if(charset!=null){

if(charset.name().equals(ENCODE)){

inputStream=new ByteArrayInputStream(arraystream.toByteArray());

stringList=getLines(inputStream, ENCODE);

}else{

inputStream=new ByteArrayInputStream(arraystream.toByteArray());

stringList= getLines(inputStream, ENCODE_GBK);

}

}

}catch (Exception e){

log.error("解析编码格式异常:"+e.getMessage());

}finally {

try {

if (fileName != null) {

fileName.close();

}

if(inputStream!=null){

inputStream.close();

}

if(stream!=null){

stream.close();

}

}catch (IOException e) {

log.error("解析编码格式异常Close stream failure :{}", e);

}

}

return stringList;

}

/**

* 读取CSV文件得到List

* @param fileName 文件路径

* @param encode 编码

* @return

*/

public static List getLines(InputStream fileName, String encode) {

List lines = new ArrayList();

BufferedReader br = null;

InputStreamReader isr = null;

try {

isr = new InputStreamReader(fileName, encode);

br = new BufferedReader(isr);

String line;

while ((line = br.readLine()) != null) {

StringBuilder sb = new StringBuilder();

sb.append(line);

boolean readNext = countChar(sb.toString(), '"', 0) % 2 == 1;

// 如果双引号是奇数的时候继续读取。考虑有换行的是情况

while (readNext) {

line = br.readLine();

if (line == null) {

return null;

}

sb.append(line);

readNext = countChar(sb.toString(), '"', 0) % 2 == 1;

}

lines.add(sb.toString());

System.out.println(sb.toString());

}

} catch (Exception e) {

log.error("Read CSV file failure :{}", e);

} finally {

try {

if (br != null) {

br.close();

}

if (isr != null) {

isr.close();

}

} catch (IOException e) {

log.error("Close stream failure :{}", e);

}

}

return lines;

}

public static String[] fromCSVLine(String source) {

return fromCSVLine(source, 0);

}

/**

* 把CSV文件的一行转换成字符串数组。指定数组长度,不够长度的部分设置为null

* @param source

* @param size

* @return

*/

public static String[] fromCSVLine(String source, int size) {

List list = fromCSVLineToArray(source);

if (size < list.size()) {

size = list.size();

}

String[] arr = new String[size];

list.toArray(arr);

return arr;

}

public static List fromCSVLineToArray(String source) {

if (source == null || source.length() == 0) {

return new ArrayList();

}

int currentPosition = 0;

int maxPosition = source.length();

int nextComa = 0;

List list = new ArrayList();

while (currentPosition < maxPosition) {

nextComa = nextComma(source, currentPosition);

list.add(nextToken(source, currentPosition, nextComa));

currentPosition = nextComa + 1;

if (currentPosition == maxPosition) {

list.add("");

}

}

return list;

}

/**

* 把字符串类型的数组转换成一个CSV行。(输出CSV文件的时候用)

*

* @param arr

* @return

*/

public static String toCSVLine(String[] arr) {

if (arr == null) {

return "";

}

StringBuilder sb = new StringBuilder();

for (int i = 0; i < arr.length; i++) {

String item = addQuote(arr[i]);

sb.append(item);

if (arr.length - 1 != i) {

sb.append(",");

}

}

return sb.toString();

}

/**

* 将list的第一行作为Map的key,下面的列作为Map的value

* @param list

* @return

*/

public static List> parseList(List list) {

List> resultList = new ArrayList>();

String firstLine = list.get(0);

String[] fields = firstLine.split(",");

for (int i = 1; i < list.size(); i++) {

String valueLine = list.get(i);

String[] valueItems = CSVFileUtil.fromCSVLine(valueLine);

Map map = new HashMap();

for (int j = 0; j < fields.length; j++) {

map.put(fields[j], valueItems[j]);

}

resultList.add(map);

}

return resultList;

}

/**

* 字符串类型的List转换成一个CSV行。(输出CSV文件的时候用)

*

* @param strArrList

* @return

*/

public static String toCSVLine(ArrayList strArrList) {

if (strArrList == null) {

return "";

}

String[] strArray = new String[strArrList.size()];

for (int idx = 0; idx < strArrList.size(); idx++) {

strArray[idx] = (String) strArrList.get(idx);

}

return toCSVLine(strArray);

}

/**

* 计算指定字符的个数

*

* @param str 文字列

* @param c 字符

* @param start 开始位置

* @return 个数

*/

private static int countChar(String str, char c, int start) {

int index = str.indexOf(c, start);

return index == -1 ? 0 : countChar(str, c, index + 1) + 1;

}

/**

* 查询下一个逗号的位置。

*

* @param source 文字列

* @param st 检索开始位置

* @return 下一个逗号的位置。

*/

private static int nextComma(String source, int st) {

int maxPosition = source.length();

boolean inquote = false;

while (st < maxPosition) {

char ch = source.charAt(st);

if (!inquote && ch == ',') {

break;

} else if ('"' == ch) {

inquote = !inquote;

}

st++;

}

return st;

}

/**

* 取得下一个字符串

*

* @param source

* @param st

* @param nextComma

* @return

*/

private static String nextToken(String source, int st, int nextComma) {

StringBuilder strb = new StringBuilder();

int next = st;

while (next < nextComma) {

char ch = source.charAt(next++);

if (ch == '"') {

if ((st + 1 < next && next < nextComma) && (source.charAt(next) == '"')) {

strb.append(ch);

next++;

}

} else {

strb.append(ch);

}

}

return strb.toString();

}

/**

* 在字符串的外侧加双引号。如果该字符串的内部有双引号的话,把"转换成""。

*

* @param item 字符串

* @return 处理过的字符串

*/

private static String addQuote(String item) {

if (item == null || item.length() == 0) {

return "\"\"";

}

StringBuilder sb = new StringBuilder();

sb.append('"');

for (int idx = 0; idx < item.length(); idx++) {

char ch = item.charAt(idx);

if ('"' == ch) {

sb.append("\"\"");

} else {

sb.append(ch);

}

}

sb.append('"');

return sb.toString();

}

public static Charset guessCharset(InputStream is) {

try {

return Charset.forName(new TikaEncodingDetector().guessEncoding(is));

}catch (Exception e){

log.error("获取流格式异常:"+e.getMessage());

}

return null;

}

}

测试例子:

File xlsxfile=new File("D:\\测试上传文件\\csv解析日期失败文件\\专用文件_20210125163437.csv");

InputStream xlsxinputStream= new FileInputStream(xlsxfile);

StopWatch watch=new StopWatch();

watch.start();

List> dataList= CSVFileUtil.getLines(xlsxinputStream);

watch.stop();

System.out.println("执行完毕,共耗时:"+watch.getTotalTimeSeconds()+"秒,数量"+dataList.size());

如上代码,引入了一个工具包来获取流的编码格式:

org.apache.any23

apache-any23-encoding

2.4

public static Charset guessCharset(InputStream is) {

try {

return Charset.forName(new TikaEncodingDetector().guessEncoding(is));

}catch (Exception e){

log.error("获取流格式异常:"+e.getMessage());

}

return null;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值