Solutions et méthodes pour les caractères déformés lors de la lecture de données de fichier Hdfs en Java

Utilisez l'API JAVA pour lire les fichiers HDFS avec des caractères déformés et marcher sur les fosses

Je veux écrire une interface pour lire une partie des données du fichier sur le HFDS pour l'aperçu. Après l'avoir implémentée selon le blog en ligne, j'ai trouvé que parfois les informations de lecture semblaient déformées, par exemple, lors de la lecture d'un csv, les chaînes séparé par des virgules

La chaîne anglaise aaa peut afficher normalement la
chaîne chinoise "Hello",
et la chaîne mixte chinois-anglais telle que "aaaHello " peut être affichée normalement , et il y a des
caractères déformés . Après avoir consulté de nombreux blogs, la solution est probablement: utilisez le xxx jeu de caractères à décoder. Avec des pensées d'incrédulité, j'ai essayé un par un, mais cela n'a vraiment pas fonctionné.

Solutions

Étant donné que HDFS prend en charge 6 encodages de jeux de caractères, chaque méthode d'encodage de fichier local est très susceptible d'être différente. Lorsque nous importons un fichier local, nous encodons le fichier dans un flux d'octets et le téléchargeons dans le système de fichiers pour le stockage. Ainsi, lorsque GET fichier des données, face au flux d'octets de différents fichiers et à un codage de jeu de caractères différent, ce n'est certainement pas un décodage de jeu de caractères fixe qui peut être décodé correctement.

Il y a donc en fait deux solutions

Jeu de caractères du codec HDFS fixe. Par exemple, si je choisis UTF-8, lors du téléchargement de fichiers, l'encodage est unifié, c'est-à-dire que les flux d'octets de différents fichiers sont convertis en encodage UTF-8 puis stockés. De cette manière, lors de l'obtention de données de fichier, il n'y a aucun problème avec le décodage du jeu de caractères UTF-8. Mais cela posera encore de nombreux problèmes dans la partie transcodage, et c'est difficile à mettre en œuvre.
Décodage dynamique. Sélectionnez le jeu de caractères correspondant à décoder en fonction du jeu de caractères codés du fichier. De cette façon, le flux de caractères natif du fichier ne sera pas modifié et il n'y aura pratiquement pas de caractères déformés.
Après avoir choisi l'idée du décodage dynamique, la difficulté réside dans la manière de déterminer quel jeu de caractères utiliser pour le décodage. Reportez-vous au contenu suivant pour obtenir une solution

Java détecte l'encodage du texte (flux d'octets)

demande:

Un certain fichier ou un certain flux d'octets doit détecter son format d'encodage.

atteindre:

Basé sur jchardet

1
2
3
4
5

net.sourceforge.jchardet
jchardet
1.0

code est le suivant:

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);
        }
    }
}

test:

		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;
}

Je suppose que tu aimes

Origine blog.csdn.net/dcj19980805/article/details/115173143
conseillé
Classement