Soluciones y métodos para caracteres confusos al leer datos de archivos Hdfs en Java

Utilice la api de JAVA para leer archivos HDFS con caracteres confusos y pisar pozos

Quiero escribir una interfaz para leer parte de los datos del archivo en el HFDS para obtener una vista previa. Después de implementarlo de acuerdo con el blog en línea, descubrí que a veces la información de lectura aparece distorsionada, por ejemplo, al leer un csv, las cadenas son separado por comas

La cadena en inglés aaa puede mostrar normalmente la
cadena china "Hello",
y la cadena mixta chino-inglés como "aaaHello " se puede mostrar normalmente , y hay
caracteres confusos . Después de consultar muchos blogs, la solución probablemente sea: use el xxx juego de caracteres para decodificar. Con pensamientos de incredulidad, lo intenté uno por uno, pero realmente no funcionó.

Soluciones

Debido a que HDFS admite codificaciones de 6 juegos de caracteres, es muy probable que cada método de codificación de archivo local sea diferente.Cuando cargamos un archivo local, en realidad codificamos el archivo en una secuencia de bytes y lo cargamos en el sistema de archivos para su almacenamiento. Entonces, cuando GET datos de archivo, frente al flujo de bytes de diferentes archivos y diferentes codificaciones de juego de caracteres, definitivamente no es una decodificación de juego de caracteres fija que se pueda decodificar correctamente.

Entonces, en realidad, hay dos soluciones

Se corrigió el conjunto de caracteres del códec HDFS. Por ejemplo, si elijo UTF-8, al cargar archivos, la codificación se unifica, es decir, los flujos de bytes de diferentes archivos se convierten en codificación UTF-8 y luego se almacenan. De esta manera, al obtener datos de archivo, no hay problema con la decodificación del juego de caracteres UTF-8. Pero hacerlo todavía tendrá muchos problemas en la parte de transcodificación y es difícil de implementar.
Decodificación dinámica. Seleccione el juego de caracteres correspondiente para decodificar de acuerdo con el juego de caracteres codificados del archivo. De esta manera, el flujo de caracteres nativos del archivo no cambiará y básicamente no habrá caracteres confusos.
Después de que elegí la idea de la decodificación dinámica, la dificultad radica en cómo determinar qué juego de caracteres usar para la decodificación. Consulte el siguiente contenido para obtener una solución.

Java detecta la codificación de texto (flujo de bytes)

demanda:

Un determinado archivo o una determinada secuencia de bytes necesita detectar su formato de codificación.

lograr:

Basado en jchardet

1
2
3
4
5

net.sourceforge.jchardet
jchardet
1,0

código es el siguiente:

public class DetectorUtils {
    
    
    private DetectorUtils() {
    
    
    }
  
    static class ChineseCharsetDetectionObserver implements
            nsICharsetDetectionObserver {
    
    
        private boolean found = false;
        private String result;
  
        public void Notify(String charset) {
    
    
            found = true;
            result = charset;
        }
  
        public ChineseCharsetDetectionObserver(boolean found, String result) {
    
    
            super();
            this.found = found;
            this.result = result;
        }
  
        public boolean isFound() {
    
    
            return found;
        }
  
        public String getResult() {
    
    
            return result;
        }
  
    }
  
    public static String[] detectChineseCharset(InputStream in)
            throws Exception {
    
    
        String[] prob=null;
        BufferedInputStream imp = null;
        try {
    
    
            boolean found = false;
            String result = Charsets.UTF_8.toString();
            int lang = nsPSMDetector.CHINESE;
            nsDetector det = new nsDetector(lang);
            ChineseCharsetDetectionObserver detectionObserver = new ChineseCharsetDetectionObserver(
                    found, result);
            det.Init(detectionObserver);
            imp = new BufferedInputStream(in);
            byte[] buf = new byte[1024];
            int len;
            boolean isAscii = true;
            while ((len = imp.read(buf, 0, buf.length)) != -1) {
    
    
                if (isAscii)
                    isAscii = det.isAscii(buf, len);
                if (!isAscii) {
    
    
                    if (det.DoIt(buf, len, false))
                        break;
                }
            }
  
            det.DataEnd();
            boolean isFound = detectionObserver.isFound();
            if (isAscii) {
    
    
                isFound = true;
                prob = new String[] {
    
     "ASCII" };
            } else if (isFound) {
    
    
                prob = new String[] {
    
     detectionObserver.getResult() };
            } else {
    
    
                prob = det.getProbableCharsets();
            }
            return prob;
        } finally {
    
    
            IOUtils.closeQuietly(imp);
            IOUtils.closeQuietly(in);
        }
    }
}

prueba:

		String file = "C:/3737001.xml";
		String[] probableSet = DetectorUtils.detectChineseCharset(new FileInputStream(file));
		for (String charset : probableSet) {
    
    
			System.out.println(charset);
		}
		```
Google提供了检测字节流编码方式的包。那么方案就很明了了,先读一些文件字节流,用工具检测编码方式,再对应进行解码即可。

## 具体解决代码
```c
dependency>
    <groupId>net.sourceforge.jchardet</groupId>
    <artifactId>jchardet</artifactId>
    <version>1.0</version>
</dependency>
从HDFS读取部分文件做预览的逻辑
// 获取文件的部分数据做预览
public List<String> getFileDataWithLimitLines(String filePath, Integer limit) {
    
    
 FSDataInputStream fileStream = openFile(filePath);
 return readFileWithLimit(fileStream, limit);
}
 
// 获取文件的数据流
private FSDataInputStream openFile(String filePath) {
    
    
 FSDataInputStream fileStream = null;
 try {
    
    
  fileStream = fs.open(new Path(getHdfsPath(filePath)));
 } catch (IOException e) {
    
    
  logger.error("fail to open file:{}", filePath, e);
 }
 return fileStream;
}
 
// 读取最多limit行文件数据
private List<String> readFileWithLimit(FSDataInputStream fileStream, Integer limit) {
    
    
 byte[] bytes = readByteStream(fileStream);
 String data = decodeByteStream(bytes);
 if (data == null) {
    
    
  return null;
 }
 
 List<String> rows = Arrays.asList(data.split("\\r\\n"));
 return rows.stream().filter(StringUtils::isNotEmpty)
   .limit(limit)
   .collect(Collectors.toList());
}
 
// 从文件数据流中读取字节流
private byte[] readByteStream(FSDataInputStream fileStream) {
    
    
 byte[] bytes = new byte[1024*30];
 int len;
 ByteArrayOutputStream stream = new ByteArrayOutputStream();
 try {
    
    
  while ((len = fileStream.read(bytes)) != -1) {
    
    
   stream.write(bytes, 0, len);
  }
 } catch (IOException e) {
    
    
  logger.error("read file bytes stream failed.", e);
  return null;
 }
 return stream.toByteArray();
}
 
// 解码字节流
private String decodeByteStream(byte[] bytes) {
    
    
 if (bytes == null) {
    
    
  return null;
 }
 
 String encoding = guessEncoding(bytes);
 String data = null;
 try {
    
    
  data = new String(bytes, encoding);
 } catch (Exception e) {
    
    
  logger.error("decode byte stream failed.", e);
 }
 return data;
}
 
// 根据Google的工具判别编码
private String guessEncoding(byte[] bytes) {
    
    
 UniversalDetector detector = new UniversalDetector(null);
 detector.handleData(bytes, 0, bytes.length);
 detector.dataEnd();
 String encoding = detector.getDetectedCharset();
 detector.reset();
 
 if (StringUtils.isEmpty(encoding)) {
    
    
  encoding = "UTF-8";
 }
 return encoding;
}

Supongo que te gusta

Origin blog.csdn.net/dcj19980805/article/details/115173143
Recomendado
Clasificación