美文网首页
java获取inputstream输入流是什么编码格式

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

作者: 楼兰King | 来源:发表于2021-01-26 16:10 被阅读0次
    错误笔记:

    一开始使用的方法,正常运行没多久,出现一个情况,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<String> getLines(InputStream fileName) {
            List<String> 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<String> getLines(InputStream fileName) {
            List<String> 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<String> getLines(InputStream fileName, String encode) {
            List<String> lines = new ArrayList<String>();
            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<Map<String, Object>> parseList(List<String> list) {
            List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
            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<String, Object> map = new HashMap<String, Object>();
                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<Map<String, Object>> dataList= CSVFileUtil.getLines(xlsxinputStream);
            watch.stop();
            System.out.println("执行完毕,共耗时:"+watch.getTotalTimeSeconds()+"秒,数量"+dataList.size());
    
    如上代码,引入了一个工具包来获取流的编码格式:
    <dependency>
                <groupId>org.apache.any23</groupId>
                <artifactId>apache-any23-encoding</artifactId>
                <version>2.4</version>
            </dependency>
    public static Charset guessCharset(InputStream is)  {
            try {
                return Charset.forName(new TikaEncodingDetector().guessEncoding(is));
            }catch (Exception e){
                log.error("获取流格式异常:"+e.getMessage());
            }
            return null;
        }
    

    相关文章

      网友评论

          本文标题:java获取inputstream输入流是什么编码格式

          本文链接:https://www.haomeiwen.com/subject/eawgzktx.html