2009-07-28 2 views
13

Учитывая массив байтов, который является кодированной кодировкой UTF-8 или произвольными двоичными данными, какие подходы можно использовать в Java, чтобы определить, что это такое?Как проверить, содержит ли массив байтов строку Unicode в Java?

Массив может быть создан с помощью кода аналогично:

byte[] utf8 = "Hello World".getBytes("UTF-8"); 

В качестве альтернативы может быть сгенерирован с помощью кода аналогично:

byte[] messageContent = new byte[256]; 
for (int i = 0; i < messageContent.length; i++) { 
    messageContent[i] = (byte) i; 
} 

Ключевым моментом является то, что мы не знаем, что массив содержит, но необходимо выяснить, чтобы заполнить следующую функцию:

public final String getString(final byte[] dataToProcess) { 
    // Determine whether dataToProcess contains arbitrary data or a UTF-8 encoded string 
    // If dataToProcess contains arbitrary data then we will BASE64 encode it and return. 
    // If dataToProcess contains an encoded string then we will decode it and return. 
} 

Как это будет распространено на покрытие UTF-16 или других механизмов кодирования?

+1

Аналогичный вопрос имеет некоторые полезные ссылки от Edward Wilde - http://stackoverflow.com/questions/377294/howto-identify-utf- 8-кодированные строки – JonoW

ответ

-1

Попробуйте расшифровать его. Если вы не получите никаких ошибок, то это действительная строка UTF-8.

+2

-1: Фактическая ошибка. Возможно, что нетекстовый двоичный поток будет декодирован как допустимая строка UTF-8. Если декодирование UTF-8 терпит неудачу, это означает, что ваши двоичные данные не являются UTF-8; но если декодирование UTF-8 _doesn't_ терпит неудачу, это не означает _guarantee_, что двоичные данные _is_ UTF-8. –

+1

+1 Совершенно верно. Если он декодирует без ошибок, это действительные текстовые данные UTF-8. Это могут быть текстовые данные, которые не имеют абсолютно никакого смысла, такие как дикая смесь латинских, китайских, тайских и греческих символов, но это семантическое различие, а не техническое. –

+1

Справедливая точка Майкл. Думаю, в этом случае я должен был сказать: -1 Не отвечая на вопрос. Утверждение, что это допустимая строка UTF-8, не отвечает на вопрос, который пытался выяснить, является ли это строкой или двоичными данными. Просто потому, что это действительное представление UTF-8, не говорит вам о том, являются ли исходные данные двоичными (что просто случается с UTF-8 по совпадению), или же подлинник был подлинными текстовыми данными. –

10

Это не представляется возможным принять это решение с полной точностью во всех случаях, так как UTF-8 строка в кодировке является один видом произвольных двоичных данных, но вы можете посмотреть на последовательность байт, которые invalid in UTF-8. Если вы найдете что-нибудь, вы знаете, что это не UTF-8.

Если массив достаточно велик, это должно хорошо работать, так как очень вероятно, что такие последовательности появятся в «случайных» двоичных данных, таких как сжатые данные или файлы изображений.

Однако можно получить действительные данные UTF-8, которые декодируют абсолютно бессмысленную строку символов (возможно, из всех видов разных скриптов). Это более вероятно с короткими последовательностями. Если вы беспокоитесь об этом, вам, возможно, придется провести более тщательный анализ, чтобы увидеть, принадлежат ли все буквы, которые являются буквами, к одному и тому же code chart. Опять же, это может привести к ложным негативам, когда у вас есть правильный ввод текста, который смешивает скрипты.

0

Если массив байтов начинается с Byte Order Mark (BOM), тогда будет легко отличить, какая кодировка была использована. Стандартные классы Java для обработки текстовых потоков, вероятно, будут иметь дело с этим для вас автоматически.

Если у вас нет спецификации в ваших байтовых данных, это будет значительно сложнее - классы .NET могут выполнять статистический анализ, чтобы попытаться разработать кодировку, но я думаю, что это делается исходя из предположения, что вы знаете, что вы имеют дело с текстовыми данными (просто не знаю, какая кодировка была использована).

Если у вас есть какой-либо контроль над форматом для ваших входных данных, лучшим выбором будет обеспечение того, что он содержит знак байтового заказа.

+1

Java не вставляет спецификацию автоматически и не будет удалять ее при декодировании. – McDowell

+1

Erk, я должен сказать, что Java не обрабатывает спецификации для UTF-8. Будет ли это делать или нет для UTF-16/UTF-32, зависит от выбранного механизма кодирования: http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html – McDowell

3

Вопрос предполагает, что существует фундаментальное различие между строкой и двоичными данными. Хотя это интуитивно так, почти невозможно точно определить, что это за разница.

Строка Java представляет собой последовательность из 16 битовых величин, которые соответствуют одному из (почти) 2 ** 16 базовых кодов Unicode. Но если вы посмотрите на эти 16-битные «символы», каждый из них может в равной мере представлять целое число, пару байтов, пиксель и т. Д. Битовые шаблоны не имеют ничего внутреннего, что говорит о том, что они представляют.

Теперь предположим, что вы перефразировали свой вопрос как просящий способ отличить TEXT, кодированный UTF-8, от произвольных двоичных данных. Помогает ли это? Теоретически нет, потому что битовые шаблоны, которые кодируют любой письменный текст, также могут быть последовательностью чисел. (.? Трудно сказать, что «произвольное» на самом деле означает, что здесь Вы можете сказать мне, как проверить, является ли число «произвольное»)

Лучшее, что мы можем сделать здесь следующее:

  1. Проверьте, являются ли байты действительной кодировкой UTF-8.
  2. Проверка, если декодированные 16-битные величины являются законными, «назначены» кодовыми точками UTF-8. (Некоторые 16-битовые величины являются незаконными (например, 0xffff), а другим в настоящее время не назначено, чтобы соответствовать любому символу.) Но что, если текстовый документ действительно использует неназначенный код?
  3. Проверьте, соответствуют ли кодовые страницы Unicode «плоскостям», которые вы ожидаете, на основе предполагаемого языка документа. Но что, если вы не знаете, какого языка ожидать, или если документ, который использует несколько языков?
  4. Тестирование последовательности кодовых точек выглядит как слова, предложения или что-то еще. Но что, если бы у нас были некоторые «двоичные данные», которые включали встроенные текстовые последовательности?

Таким образом, вы можете сказать, что последовательность байтов определенно не является UTF-8, если декодирование завершается с ошибкой. Помимо этого, если вы сделаете предположения о языке, вы можете сказать, что последовательность байтов , вероятно, или , вероятно, не кодированный текстовый документ UTF-8.

ИМО, самое лучшее, что вы можете сделать, это не попасть в ситуацию, когда программа должна принять это решение. И если этого не избежать, узнайте, что ваша программа может ошибиться. С мыслью и напряженной работой вы можете сделать это маловероятным, но вероятность никогда не будет равна нулю.

4

Вот способ использовать UTF-8 «двоичный» регулярное выражение из W3C site

static boolean looksLikeUTF8(byte[] utf8) throws UnsupportedEncodingException 
{ 
    Pattern p = Pattern.compile("\\A(\n" + 
    " [\\x09\\x0A\\x0D\\x20-\\x7E]    # ASCII\\n" + 
    "| [\\xC2-\\xDF][\\x80-\\xBF]    # non-overlong 2-byte\n" + 
    "| \\xE0[\\xA0-\\xBF][\\x80-\\xBF]   # excluding overlongs\n" + 
    "| [\\xE1-\\xEC\\xEE\\xEF][\\x80-\\xBF]{2} # straight 3-byte\n" + 
    "| \\xED[\\x80-\\x9F][\\x80-\\xBF]   # excluding surrogates\n" + 
    "| \\xF0[\\x90-\\xBF][\\x80-\\xBF]{2}  # planes 1-3\n" + 
    "| [\\xF1-\\xF3][\\x80-\\xBF]{3}   # planes 4-15\n" + 
    "| \\xF4[\\x80-\\x8F][\\x80-\\xBF]{2}  # plane 16\n" + 
    ")*\\z", Pattern.COMMENTS); 

    String phonyString = new String(utf8, "ISO-8859-1"); 
    return p.matcher(phonyString).matches(); 
} 

Как первоначально написано регулярное выражение и предназначенные для использования в массиве байтов, но вы не можете сделать это с регулярными выражениями Java; цель должна быть чем-то, что реализует интерфейс CharSequence (поэтому тоже char[]). Декодируя byte[] как ISO-8859-1, вы создаете строку, в которой каждый char имеет такое же неподписанное числовое значение, что и соответствующий байт в исходном массиве.

Как уже отмечалось, тесты, как это может только сказать вам byte[]может содержать UTF-8 текст, не то, что он делает . Но регулярное выражение настолько исчерпывающее, что крайне маловероятно, что из него могут проскользнуть исходные двоичные данные. Даже массив из всех нулей не будет совпадать, так как регулярное выражение никогда не совпадает с NUL. Если единственные возможности - UTF-8 и двоичные, я бы хотел доверять этому тесту.

И пока вы на нем, вы можете лишить спецификацию UTF-8, если она есть; в противном случае UTF-8 CharsetDecoder передаст его, как если бы это был текст.

UTF-16 было бы намного сложнее, потому что существует очень мало байтовых последовательностей, которые всегда недействительны. Единственные, о которых я могу думать, - это суррогатные персонажи с низким уровнем суррогатов, или наоборот.Помимо этого, вам понадобится какой-то контекст, чтобы решить, действительна ли данная последовательность. У вас может быть кириллическое письмо, сопровождаемое китайской идеограммой, сопровождаемой дингбатом с смайликом, но это было бы совершенно верно UTF-16.

-1

Я думаю, что Майкл объяснил это хорошо in his answer Это может быть единственный способ узнать, содержит ли массив байтов все допустимые последовательности utf-8. Я использую следующий код в PHP

function is_utf8($string) { 

    return preg_match('%^(?: 
      [\x09\x0A\x0D\x20-\x7E]   # ASCII 
     | [\xC2-\xDF][\x80-\xBF]    # non-overlong 2-byte 
     | \xE0[\xA0-\xBF][\x80-\xBF]  # excluding overlongs 
     | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte 
     | \xED[\x80-\x9F][\x80-\xBF]  # excluding surrogates 
     | \xF0[\x90-\xBF][\x80-\xBF]{2}  # planes 1-3 
     | [\xF1-\xF3][\x80-\xBF]{3}   # planes 4-15 
     | \xF4[\x80-\x8F][\x80-\xBF]{2}  # plane 16 
    )*$%xs', $string); 

} 

Взятые его от W3.org

+0

«что подходы могут использоваться в Java " –

0

В первоначальный вопрос: Как я могу проверить, содержит ли массив строку Unicode в Java ?; Я обнаружил, что термин Java Unicode в основном относится к кодам Unicode Utf16. Я сам прошел эту проблему и создал код, который мог бы помочь любому, у кого возник такой вопрос, найти ответы.

Я создал 2 основных метода, на которых будут отображаться коды кода Utf-8, а другой будет создавать коды кода Utf-16. Единицы кода Utf-16 - это то, с чем вы столкнетесь с Java и JavaScript ... обычно видны в форме «\ ud83d»

Для получения дополнительной информации по кодовым единицам и конверсии используйте веб-сайт;

https://r12a.github.io/apps/conversion/

Вот код ...

byte[] array_bytes = text.toString().getBytes(); 
    char[] array_chars = text.toString().toCharArray(); 
    System.out.println(); 
    byteArrayToUtf8CodeUnits(array_bytes); 
    System.out.println(); 
    charArrayToUtf16CodeUnits(array_chars); 


public static void byteArrayToUtf8CodeUnits(byte[] byte_array) 
{ 
    /*for (int k = 0; k < array.length; k++) 
    { 
     System.out.println(name + "[" + k + "] = " + "0x" + byteToHex(array[k])); 
    }*/ 
    System.out.println("array.length: = " + byte_array.length); 
    //------------------------------------------------------------------------------------------ 
    for (int k = 0; k < byte_array.length; k++) 
    { 
     System.out.println("array byte: " + "[" + k + "]" + " converted to hex" + " = " + byteToHex(byte_array[k])); 
    } 
    //------------------------------------------------------------------------------------------ 
} 
public static void charArrayToUtf16CodeUnits(char[] char_array) 
{ 
    /*Utf16 code units are also known as Java Unicode*/ 
    System.out.println("array.length: = " + char_array.length); 
    //------------------------------------------------------------------------------------------ 
    for (int i = 0; i < char_array.length; i++) 
    { 
     System.out.println("array char: " + "[" + i + "]" + " converted to hex" + " = " + charToHex(char_array[i])); 
    } 
    //------------------------------------------------------------------------------------------ 
} 
static public String byteToHex(byte b) 
{ 
    //Returns hex String representation of byte b 
    char hexDigit[] = 
      { 
        '0', '1', '2', '3', '4', '5', '6', '7', 
        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 
      }; 
    char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] }; 
    return new String(array); 
} 
static public String charToHex(char c) 
{ 
    //Returns hex String representation of char c 
    byte hi = (byte) (c >>> 8); 
    byte lo = (byte) (c & 0xff); 

    return byteToHex(hi) + byteToHex(lo); 
} 
Смежные вопросы