2015-08-24 7 views
2

У меня есть строка с кавычками, например «Стоимость будет = C2 = A31,000». Как мне преобразовать это в «Стоимость составит 1000 фунтов стерлингов».Декодирование сообщений с кавычками для печати в Swift

Я просто конвертирую текст вручную в данный момент, и это не распространяется на все случаи. Я уверен, что есть только одна строка кода, которая поможет в этом.

Вот мой код:

func decodeUTF8(message: String) -> String 
{ 
    var newMessage = message.stringByReplacingOccurrencesOfString("=2E", withString: ".", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=E2=80=A2", withString: "•", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=C2=A3", withString: "£", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=A3", withString: "£", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=E2=80=9C", withString: "\"", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=E2=80=A6", withString: "…", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=E2=80=9D", withString: "\"", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=92", withString: "'", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=3D", withString: "=", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=20", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil) 
    newMessage = newMessage.stringByReplacingOccurrencesOfString("=E2=80=99", withString: "'", options: NSStringCompareOptions.LiteralSearch, range: nil) 

    return newMessage 
} 

Благодаря

+0

Это не полное решение, но я бы хотел, чтобы убедиться, что вы уже видели этот ответ на несколько иной проблема: http://stackoverflow.com/a/19088341/4323 –

+1

Базовая кодировка 64 Я хорош, это текст/plain; quoted-printable, с которыми у меня возникла проблема. Спасибо – iphaaw

ответ

2

Простым способом было бы использовать метод (NS)String stringByRemovingPercentEncoding для этой цели. Это наблюдалось в decoding quoted-printables, , поэтому первое решение - это, в основном, перевод ответов в , что нить для Swift.

Идея состоит в том, чтобы заменить кодировку «= NN» с кавычками на кодировку процентов «% NN», а затем использовать существующий метод для удаления процентной кодировки.

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

func decodeQuotedPrintable(message : String) -> String? { 
    return message 
     .stringByReplacingOccurrencesOfString("=\r\n", withString: "") 
     .stringByReplacingOccurrencesOfString("=\n", withString: "") 
     .stringByReplacingOccurrencesOfString("%", withString: "%25") 
     .stringByReplacingOccurrencesOfString("=", withString: "%") 
     .stringByRemovingPercentEncoding 
} 

Функция возвращает необязательную строку, которая является nil для недопустимого ввода. Неправильный ввод может быть:

  • А «=» символ, который не следуют два шестнадцатеричных цифр, например, "= XX".
  • Последовательность «= NN», которая не декодируется до действительной последовательности UTF-8, , например. "= Е2 = 64".

Примеры:

if let decoded = decodeQuotedPrintable("=C2=A31,000") { 
    print(decoded) // £1,000 
} 

if let decoded = decodeQuotedPrintable("=E2=80=9CHello =E2=80=A6 world!=E2=80=9D") { 
    print(decoded) // “Hello … world!” 
} 

Update 1: Приведенный выше код предполагает, что сообщение использует UTF-8 кодировку процитировать не-ASCII символы, как и в большинстве ваших примеров : C2 A3 - кодировка UTF-8 для «£», E2 80 A4 - кодировка UTF-8 для .

Если ввод "Rub=E9n", то сообщение использует кодировку Windows-1252. Для декодирования, что правильно, вы должны заменить

.stringByRemovingPercentEncoding 

по

.stringByReplacingPercentEscapesUsingEncoding(NSWindowsCP1252StringEncoding) 

Есть также способы обнаружения кодировки из поля заголовка «Content-Type», сравните, например, https://stackoverflow.com/a/32051684/1187415.


Update 2:stringByReplacingPercentEscapesUsingEncoding метод помечен как устаревший, поэтому приведенный выше код всегда будет генерировать предупреждение компилятора. К сожалению, похоже, что Apple не предоставил альтернативный метод .

Так вот новый, полностью автономный метод декодирования, который не вызывает предупреждения о компиляторе. На этот раз я написал в качестве метода расширения для String. Объяснение комментариев содержится в коде .

extension String { 

    /// Returns a new string made by removing in the `String` all "soft line 
    /// breaks" and replacing all quoted-printable escape sequences with the 
    /// matching characters as determined by a given encoding. 
    /// - parameter encoding:  A string encoding. The default is UTF-8. 
    /// - returns:    The decoded string, or `nil` for invalid input. 

    func decodeQuotedPrintable(encoding enc : NSStringEncoding = NSUTF8StringEncoding) -> String? { 

     // Handle soft line breaks, then replace quoted-printable escape sequences. 
     return self 
      .stringByReplacingOccurrencesOfString("=\r\n", withString: "") 
      .stringByReplacingOccurrencesOfString("=\n", withString: "") 
      .decodeQuotedPrintableSequences(enc) 
    } 

    /// Helper function doing the real work. 
    /// Decode all "=HH" sequences with respect to the given encoding. 

    private func decodeQuotedPrintableSequences(enc : NSStringEncoding) -> String? { 

     var result = "" 
     var position = startIndex 

     // Find the next "=" and copy characters preceding it to the result: 
     while let range = rangeOfString("=", range: position ..< endIndex) { 
      result.appendContentsOf(self[position ..< range.startIndex]) 
      position = range.startIndex 

      // Decode one or more successive "=HH" sequences to a byte array: 
      let bytes = NSMutableData() 
      repeat { 
       let hexCode = self[position.advancedBy(1) ..< position.advancedBy(3, limit: endIndex)] 
       if hexCode.characters.count < 2 { 
        return nil // Incomplete hex code 
       } 
       guard var byte = UInt8(hexCode, radix: 16) else { 
        return nil // Invalid hex code 
       } 
       bytes.appendBytes(&byte, length: 1) 
       position = position.advancedBy(3) 
      } while position != endIndex && self[position] == "=" 

      // Convert the byte array to a string, and append it to the result: 
      guard let dec = String(data: bytes, encoding: enc) else { 
       return nil // Decoded bytes not valid in the given encoding 
      } 
      result.appendContentsOf(dec) 
     } 

     // Copy remaining characters to the result: 
     result.appendContentsOf(self[position ..< endIndex]) 

     return result 
    } 
} 

Пример использования:

if let decoded = "=C2=A31,000".decodeQuotedPrintable() { 
    print(decoded) // £1,000 
} 

if let decoded = "=E2=80=9CHello =E2=80=A6 world!=E2=80=9D".decodeQuotedPrintable() { 
    print(decoded) // “Hello … world!” 
} 

if let decoded = "Rub=E9n".decodeQuotedPrintable(encoding: NSWindowsCP1252StringEncoding) { 
    print(decoded) // Rubén 
} 
+0

Это то, что я смотрел. Я поместил его в свой код, чтобы попробовать его, и сразу же попал в проблему. – iphaaw

+0

Извините, что возвращение слишком рано ... decodeQuotedPrintable ("Rub = E9n") должен вернуть Rubén. Я пробовал это на http://www.motobit.com/util/quoted-printable-decoder.asp, и этот сайт декодировал его в порядке. Есть предположения? – iphaaw

+0

@iphaaw: Это зависит от кодировки * (или набора символов), которая используется в сообщении. Этот онлайн-декодер, похоже, автоматически определяет кодировку, возможно, используя разные кодировки. Я добавил некоторую информацию в ответ, дайте мне знать, если это поможет. –

0

Эта кодировка называется «кавычко для печати», и то, что вам нужно сделать, это преобразовать строку в NSData кодировке ASCII, то просто перебрать данные, заменяющие все 3-символьные стороны типа «= A3» байтом/char 0xA3, а затем преобразование полученных данных в строку с использованием NSUTF8StringEncoding.

+1

Это будет какая-то работа, но из моего примера вы можете увидеть, что иногда я получаю два байтовых символа. Я бы подумал, что будет один метод линии, который я мог бы назвать, чтобы сделать это более эффективно. BTW Спасибо за указание правильного имени кодировки.Спасибо – iphaaw

+0

Вы получаете 2 байта для одиночного символа, потому что в кодировке UTF-8 это занимает два байта. Только английская буква/цифры/запятые и т. Д. Кодируются как один байт. –

0

Чтобы предоставить применимое решение, необходимо предоставить еще несколько данных. Итак, я сделаю некоторые предположения.

В сообщении HTML или Mail, например, вы можете применить одно или несколько кодировок к каким-то исходным данным. Например, вы можете кодировать двоичный файл, например. файл png с базой64, а затем застегните его. Порядок важен.

В вашем примере, как вы говорите, исходные данные являются строкой и кодируются через UTF-8.

В сообщении HTPP, ваш Content-Type, таким образом, text/plain; charset = UTF-8. В вашем примере, похоже, также применяется дополнительная кодировка, a «Кодировка содержимого-передачи»: возможно, Content-transfer-encoding - quoted-printable или base64 (но не уверен в этом).

Чтобы вернуть его обратно, вам необходимо применить соответствующие декодирования в обратном порядке.

Подсказка:

Вы можете просмотреть заголовки (Contente-type и Content-Transfer-Encoding) почтовое сообщение, при просмотре необработанного источника почты.

+0

Base 64 encoding У меня все хорошо, это текст/plain; quoted-printable, с которыми у меня возникла проблема. Спасибо – iphaaw

1

К сожалению, я немного опоздал с моим ответом. Это может быть полезно для других.

var string = "The cost would be =C2=A31,000" 

var finalString: String? = nil 

if let regEx = try? NSRegularExpression(pattern: "={1}?([a-f0-9]{2}?)", options: NSRegularExpressionOptions.CaseInsensitive) 
{ 
    let intermediatePercentEscapedString = regEx.stringByReplacingMatchesInString(string, options: NSMatchingOptions.WithTransparentBounds, range: NSMakeRange(0, string.characters.count), withTemplate: "%$1") 
    print(intermediatePercentEscapedString) 
    finalString = intermediatePercentEscapedString.stringByRemovingPercentEncoding 
    print(finalString) 
}