Nano Hash - криптовалюты, майнинг, программирование

Декодирование строки с экранированными специальными символами в проблеме Scala

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

{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}

Эта запись должна быть {"value":"ıarines Bintıç Ramuçlar"} , например. Символ '"' заменяется соответствующим шестнадцатеричным символом \x22, а другие специальные символы Unicode заменяются одним или двумя шестнадцатеричными символами (например, \xC3\xA7 кодирует ç и т. д.)

Мне нужно преобразовать похожие строки в обычную строку Unicode в Scala, чтобы при печати она выдавала {"value":"ıarines Bintıç Ramuçlar"} без шестнадцатеричных знаков.

В Python я могу легко расшифровать эти записи с помощью строки кода:

>>> a = "{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"
>>> a.decode("utf-8")
u'{"value":"\u0131arines Bint\u0131\xe7 Ramu\xe7lar"}'
>>> print a.decode("utf-8")
{"value":"ıarines Bintıç Ramuçlar"}

Но в Scala я не могу найти способ его декодировать. Я безуспешно пытался преобразовать его следующим образом:

scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(new String(a.getBytes(), "UTF-8"))
{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}

Я также попробовал URLDecoder, который нашел в решении аналогичной проблемы (но с URL):

scala> val a = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
scala> print(java.net.URLDecoder.decode(a.replace("\\x", "%"), "UTF-8"))
{"value":"ıarines Bintıç Ramuçlar"}

Это дало желаемый результат для этого примера, но кажется небезопасным для общих текстовых полей, поскольку он предназначен для работы с URL-адресами и требует замены всех \x на % в строке.

Есть ли в Scala лучший способ решить эту проблему?

Я новичок в Scala и буду благодарен за любую помощь.

ОБНОВЛЕНИЕ: я создал собственное решение с помощью javax.xml.bind.DatatypeConverter.parseHexBinary. Пока работает, но выглядит громоздко и совсем не изящно. Я думаю, что должен быть более простой способ сделать это.

Вот код:

import javax.xml.bind.DatatypeConverter
import scala.annotation.tailrec
import scala.util.matching.Regex

def decodeHexChars(string: String): String = {
  val regexHex: Regex = """\A\\[xX]([0-9a-fA-F]{1,2})(.*)""".r
  def purgeBuffer(buffer: String, acc: List[Char]): List[Char] = {
    if (buffer.isEmpty) acc
    else new String(DatatypeConverter.parseHexBinary(buffer)).reverse.toList ::: acc
  }
  @tailrec
  def traverse(s: String, acc: List[Char], buffer: String): String = s match {
    case "" =>
      val accUpdated = purgeBuffer(buffer, acc)
      accUpdated.foldRight("")((str, b) => b + str)
    case regexHex(chars, suffix) =>
      traverse(suffix, acc, buffer + chars)
    case _ =>
      val accUpdated = purgeBuffer(buffer, acc)
      traverse(s.tail, s.head :: accUpdated, "")
  }
  traverse(string, Nil, "")
}

Ответы:


1

Каждый \x?? кодирует один байт, например \x22 кодирует ", а \x5C кодирует \. Но в UTF-8 некоторые символы кодируются с использованием нескольких байтов, поэтому вам нужно преобразовать \xC4\xB1 в символ ı и так далее.

replaceAllIn действительно хорош, но он может съесть ваши косые черты. Таким образом, если вы не используете группы (например, \1) в заменяемой строке, quoteReplacement является рекомендуемым способом экранирования символов \ и $.

/** "22" -> 34, "AA" -> -86  */
def hex2byte(hex: String) = Integer.parseInt(hex, 16).toByte

/** decode strings like \x22 or \xC4\xB1\xC3\xA7 to specified encoding   */
def decodeHexadecimals(str: String, encoding: String="UTF-8") = 
  new String(str.split("""\\x""").tail.map(hex2byte), encoding)

/** fix weird strings */
def replaceHexadecimals(str: String, encoding: String="UTF-8") = 
  """(\\x[\dA-F]{2})+""".r.replaceAllIn(str, m => 
    util.matching.Regex.quoteReplacement(
      decodeHexadecimals(m.group(0), encoding)))

P.S. Кто-нибудь знает разницу между java.util.regex.Matcher.quoteReplacement и scala.util.matching.Regex.quoteReplacement?

30.08.2017
  • Есть варианты сделать это без regEx? 31.10.2017

  • 2

    Проблема в том, что кодировка действительно специфична для python (я думаю). Что-то вроде этого может сработать:

    val s = """{\x22value\x22:\x22\xC4\xB1arines Bint\xC4\xB1\xC3\xA7 Ramu\xC3\xA7lar\x22}"""
    
    """\\x([A-F0-9]{2})""".r.replaceAllIn(s, (x: Regex.Match) => 
      new String(BigInt(x.group(1), 16).toByteArray, "UTF-8")
    )
    
    19.07.2017
  • Спасибо за ответ! Однако это не работает. Проблема в том, что некоторые символы кодируются одним шестнадцатеричным символом, а другие кодируются комбинацией двух. Я опубликовал обновление к исходному сообщению, которое также основано на регулярном выражении. 20.07.2017
  • @HukoJack одна шестнадцатеричная цифра недействительна, их всегда должно быть ровно две. В противном случае, если у вас есть \xAA, как бы вы определили, является ли это \u013A или просто ɒ (\u252)? Соглашение состоит в том, что если вы столкнетесь с \x, за которым следует что-либо, кроме двух шестнадцатеричных цифр, вы либо ошибетесь, либо воспримете это буквально. 20.07.2017
  • @Dima, но в тех случаях, когда у вас есть что-то вроде "çtext, которое будет закодировано как \x22\xC3\xA7text, не будет ли этот жадный подход сначала пытаться декодировать \x22\xC3, а затем \xA7 и выдавать ошибку? 20.07.2017
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..