[Java] UTF-8 (avec BOM) est converti en 0xFFFD (REMPLACEMENT CHARACTER)

introduction

Dans la modification du système (Java 7), j'ai créé un processus pour compter le nombre de lignes dont le premier caractère est "K" dans le fichier reçu par FTP. L'expression régulière "^ K. *" est utilisée pour déterminer si le premier caractère de la ligne est "K".

Dans le test, le fichier reçu a été créé en tant que Shift-JIS, il n'y a donc pas eu de problème pour spécifier le code de caractère pour la lecture du fichier reçu FTP avec "Windows 31J", mais le fichier FTP reçu est UTF-8 (avec BOM). Par conséquent, la nomenclature n'a pas déterminé «K» uniquement pour la première ligne et le nombre total de lignes ne correspondait pas.

C'était un oubli des spécifications.

BOM Qu'est-ce que la nomenclature? "Byte Order Mark (https://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88%E3%82%AA" % E3% 83% BC% E3% 83% 80% E3% 83% BC% E3% 83% 9E% E3% 83% BC% E3% 82% AF)) "," Ce fichier est au format Unicode. C'est l'information pour la faire distinguer qu'elle est écrite.

Seuls les codes des caractères principaux et la nomenclature sont extraits.

Code de caractère Distinction Endian Code de nomenclature
UTF-8 0xEF 0xBB 0xBF
UTF-16 BE 0xFE 0xFF
LE 0xFF 0xFE

Ignorer la nomenclature

J'ai ajouté un processus pour ignorer la nomenclature en se référant au code sur le site suivant, mais le résultat ne change pas. [JAVA] Logique de suppression manuelle de la nomenclature (Byte Of Mark)

private static String excludeBOMString(String original_str) {
	if (original_str != null) {
		char c = original_str.charAt(0);
		if (Integer.toHexString(c).equals("feff")) {
			StringBuilder sb = new StringBuilder();
			for (int i=1; i < original_str.length(); i++) {
				sb.append(original_str.charAt(i));
			}
			return sb.toString();
		} else {
			return original_str;
		}
	} else {
		return "";
	}
}

Pourquoi? Quand je l'ai débogué sur Windows 7 Eclipse de mon PC, le premier caractère était "fffd". Quand j'ai examiné un peu plus le code de caractère, c'était comme suit, et j'ai trouvé que je devais sauter 3 caractères au premier caractère "K (0x4b)". C'est un code BOM UTF-8 (0xEF, 0xBB, 0xBF), et bien que je pense que le code interne Java est UTF-16LE, qui est différent du code BOM (0xfeff), je n'ai pas eu le temps avant le travail de vérification de l'après-midi, donc je l'ai clarifié pour le moment. Je l'ai remis.

Index Code de caractère
0 0xfffd
1 0xff7b
2 0xff7f
3 0x4b

Le jugement de la nomenclature a été défini sur "fffd" et le saut a été corrigé à 3 caractères. Vous pouvez maintenant sauter la nomenclature normalement et obtenir le nombre total de lignes avec "K" comme premier caractère.

private static String excludeBOMString(String original_str) {
	if (original_str != null) {
		char c = original_str.charAt(0);
		if (Integer.toHexString(c).equals("fffd")) {
			StringBuilder sb = new StringBuilder();
			for (int i=3; i < original_str.length(); i++) {
				sb.append(original_str.charAt(i));
			}
			return sb.toString();
		} else {
			return original_str;
		}
	} else {
		return "";
	}
}

Dans le travail de vérification de l'après-midi, j'ai remplacé les programmes modifiés, mais le nombre total ne correspondait pas. Pourquoi? Quand je pensais que je le déboguerais sur Windows 7 Eclipse de mon PC, le nombre total correspondrait. Étant donné que l'environnement de vérification est Windows Server 2012 R2, lorsque j'ai essayé de sortir la ligne de lecture en déboguant pour voir si quelque chose était différent en fonction de l'environnement, c'était du caractère après "K", donc si vous changez le saut de 3 caractères à 2 caractères, le nombre total Est venu pour s'adapter.

Cependant, il est effrayant que cela dépende de l'environnement.

0xFFFD(REPLACEMENT CHARACTER) Quoi qu'il en soit, le travail de vérification de l'après-midi était terminé, j'ai donc décidé d'enquêter sur la cause.

Pour "0xfffd", lorsque vous essayez de lire un fichier texte encodé en UTF-8 avec Shift-JIS, si le caractère correspondant n'existe pas, il sera converti en caractère "0xFFFD". À propos de la détection des caractères brouillés en Java

Dans mon environnement (Windows 7) ... Numéro de code 0xFFFD "CARACTÈRE DE REMPLACEMENT", M. �! Dans mon environnement, il s'agit d'un diamant noir avec une marque «?» Blanche, alors n'est-il pas affiché correctement? J'ai pensé, mais cela semble être "des caractères à afficher quand ils ne peuvent pas être affichés", donc cela semble être correct Le plus grand caractère en Unicode

Étant donné que le code de caractère pour la lecture du fichier a été spécifié comme "Windows 31J", cela signifie que la partie de code de nomenclature est convertie en caractère "0xfffd" en supposant que le caractère correspondant n'existe pas. J'étais convaincu de cela.

De plus, en ce qui concerne le cas où le nombre de caractères à sauter était différent sur Windows 7 et Windows Server 2012 R2, le deuxième «0xff7b» sur Windows Server 2012 R2 avait disparu, il s'est donc avéré que sauter 3 caractères irait trop loin.

Index Code de caractères Win 7 Index Code de caractère Win2012
0 0xfffd 0 0xfffd
1 0xff7b
2 0xff7f 1 0xff7f
3 0x4b 2 0x4b

J'ai cherché en ligne, mais je n'ai trouvé aucune documentation à ce sujet. Puisque les caractères dans le fichier ne sont que des caractères alphanumériques plutôt que de passer du temps sur cette enquête, j'ai décidé qu'il serait préférable de spécifier le code de caractère pour lire le fichier comme "UTF-8", et j'ai modifié le programme. ..

En conséquence, le jugement du code de nomenclature était "0xfeff" et le saut était d'un caractère, laissant la logique de suppression manuelle d'origine. Nous l'avons testé sur Windows 7 et Windows Server 2012 R2 et les deux ont donné les mêmes résultats.

finalement

Cette fois, c'était ma première expérience à être convertie en CARACTÈRE DE REMPLACEMENT (0xfffd), donc je l'ai écrit sous forme de mémorandum.

Lorsque nous parlons d'UTF-8 en Java, nous supposons UTF-8 sans BOM, et bien qu'il y ait diverses discussions, il semble que nous n'ayons pas l'intention de le traiter en raison de problèmes de compatibilité descendante. Voir aussi: [Que se passe-t-il lorsque je lis un fichier UTF8 avec Java SE BOM? ](Https://hondou.homedns.org/pukiwiki/pukiwiki.php?JavaSE%20BOM%C9%D5%A4%ADUTF8%A5%D5%A5%A1%A5%A4%A5%EB%A4%F2% C6% C9% A4% DF% B9% FE% A4% E0% A4% C8% A4% C9% A4% A6% A4% CA% A4% EB% A4% AB% A1% A9)

J'ai déjà utilisé PHP avec BOM, mais avec / sans BOM est gênant.

Recommended Posts

[Java] UTF-8 (avec BOM) est converti en 0xFFFD (REMPLACEMENT CHARACTER)
Comment supprimer une nomenclature (UTF-8)
Java pour jouer avec Function
Connectez-vous à DB avec Java
Connectez-vous à MySQL 8 avec Java
Est-il possible de générer automatiquement Getter / Setter avec l'interface Java?
Project facet Java version 13 n'est pas pris en charge. Comment faire avec
Java pour apprendre avec les ramen [Partie 1]
Comment entourer n'importe quel caractère avec "~"
Osez défier Kaggle avec Java (1)
J'ai essayé d'interagir avec Java
Comment compiler Java avec VsCode & Ant
[Java] Résumez comment comparer avec la méthode equals
[Java] Map # merge est difficile à comprendre.
3 points difficiles à gérer Java Realm
[Java] Précautions lors de la comparaison de chaînes de caractères avec des chaînes de caractères
Facile à parcourir avec les expressions régulières Java
Introduction aux algorithmes avec java --Search (recherche de priorité de largeur)
Initialiser le tableau Ruby avec 0 comme Java, c'est-à-dire définir la valeur par défaut sur 0