Я пытаюсь отделить все китайские иероглифы от String, но столкнулся со странной ситуацией для символа 𥑮
scala> "𥑮"
res1: String = 𥑮
scala> res1.length
res2: Int = 2
scala> res1.getBytes
res3: Array[Byte] = Array(-16, -91, -111, -82)
scala> res1(0)
res4: Char = ?
scala> res1(1)
res5: Char = ?
Это один символ, но Java / Scala определяет его как два неизвестных символа. И обычно я вижу, что китайский символ занимает три байта в UTF-8, но этот символ занимает четыре.
Следовательно, я не могу разбить строку и найти этот единственный символ. Хуже того, при использовании myString.replaceAll("[^\\p{script=Han}]", "")
для удаления всех некитайских символов вторая часть 𥑮 заменяется и становится недопустимой строкой.
Есть ли решение этого? Я использую openjdk-8-jdk в Ubuntu.
6 ответов
Для длины вы должны использовать
string.codePointCount(0, string.length());
Для замены лучше избегать регулярного выражения, основанного на символах. Вы можете написать цикл на основе String#offsetByCodePoints()
и вручную удалять символы на основе String.codePointAt()
и Character.isIdeographic()
.
Вероятно, что этот символ недопустим или не поддерживается в UTF-8, но поддерживается в UTF-16, что приводит к некоторой несовместимости между JVM и оболочкой Scala. У вашей системы прямой или обратный порядок байтов? Также вы можете попробовать получить кодовую точку Unicode символа и проверить, является ли это UTF-8 или UTF-16. Кроме того, в китайском языке есть составные буквы, такие как японские кандзи и фуригана, так что это тоже может быть частью вашей проблемы.
Я думаю, вы хотите заменить / разделить строку. Это вы можете сделать, не зная длины строки. Потому что java принимает последовательность строк также для замены определенного символа или последовательности символов в строке. Например: -`public class Test {
public static void main(String[] args) {
String s="𥑮";
System.out.println(s.replace("𥑮", "k"));
}
}
`А если вы хотите разделить строку, используйте строковый токенизатор, например: -
StringTokenizer st= new StringTokenizer("your sentence or String","the problematic char/string");
Основываясь на ответе @Marko, вот пример разделения строки:
scala> val x = "硓𥑮abc"
x: String = 硓𥑮abc
scala> (0 to x.codePointCount(0, x.length)).map(c => x.offsetByCodePoints(0, c)).sliding(2).map(w => x.substring(w.head, w.last)).toList
res1: List[String] = List(硓, 𥑮, a, b, c)
И чтобы определить, является ли каждый символ CJKV:
scala> (0 until x.codePointCount(0, x.length)).map(c => x.offsetByCodePoints(0, c)).map(i => Character.isIdeographic(x.codePointAt(i))).toList
res2: List[Boolean] = List(true, true, false, false, false)
Поддержка Unicode в стандартной библиотеке Java появилась раньше текущего стандарта, и поэтому поддержка астральных (не BMP) символов ... ограничена; несколько API будут рассматривать их как отдельные суррогатные пары, как вы видели. Если вы выполняете обширные манипуляции со строками, лучше всего использовать ICU4J, который, как я понимаю, предлагает регулярные выражения с полной поддержкой Unicode.
Вы встретили суррогатную пару. Этот символ - U + 2546E, что, как вы видите, намного больше, чем 2 ^ 16. Он представлен в строке Java или Scala как последовательность 0xD855 0xDC6E.
Если вам нужна библиотека регулярных выражений, которая прозрачно обрабатывает такие вещи, я знаю, где ее найти: Регулярное выражение TCL перенесено на Java. Если вы не хотите туда идти, вам нужно использовать методы Code Point String и Character в java для навигации.
Похожие вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.