Я пытаюсь отделить все китайские иероглифы от 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.

11
pishen 27 Фев 2015 в 12:19

6 ответов

Лучший ответ

Для длины вы должны использовать

string.codePointCount(0, string.length());

Для замены лучше избегать регулярного выражения, основанного на символах. Вы можете написать цикл на основе String#offsetByCodePoints() и вручную удалять символы на основе String.codePointAt() и Character.isIdeographic().

8
Marko Topolnik 27 Фев 2015 в 09:47

Вероятно, что этот символ недопустим или не поддерживается в UTF-8, но поддерживается в UTF-16, что приводит к некоторой несовместимости между JVM и оболочкой Scala. У вашей системы прямой или обратный порядок байтов? Также вы можете попробовать получить кодовую точку Unicode символа и проверить, является ли это UTF-8 или UTF-16. Кроме того, в китайском языке есть составные буквы, такие как японские кандзи и фуригана, так что это тоже может быть частью вашей проблемы.

-3
Tamoghna Chowdhury 27 Фев 2015 в 10:00

Я думаю, вы хотите заменить / разделить строку. Это вы можете сделать, не зная длины строки. Потому что 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");
-2
Amaresh Pattanayak 27 Фев 2015 в 09:37

Основываясь на ответе @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)
0
pishen 27 Фев 2015 в 18:03

Поддержка Unicode в стандартной библиотеке Java появилась раньше текущего стандарта, и поэтому поддержка астральных (не BMP) символов ... ограничена; несколько API будут рассматривать их как отдельные суррогатные пары, как вы видели. Если вы выполняете обширные манипуляции со строками, лучше всего использовать ICU4J, который, как я понимаю, предлагает регулярные выражения с полной поддержкой Unicode.

1
lmm 27 Фев 2015 в 14:37

Вы встретили суррогатную пару. Этот символ - U + 2546E, что, как вы видите, намного больше, чем 2 ^ 16. Он представлен в строке Java или Scala как последовательность 0xD855 0xDC6E.

Если вам нужна библиотека регулярных выражений, которая прозрачно обрабатывает такие вещи, я знаю, где ее найти: Регулярное выражение TCL перенесено на Java. Если вы не хотите туда идти, вам нужно использовать методы Code Point String и Character в java для навигации.

3
bmargulies 27 Фев 2015 в 15:04