Swift - Regex для извлечения значения - PullRequest
0 голосов
/ 08 ноября 2018

Я хочу извлечь значение из строки, которая имеет уникальный начальный и конечный символ. В моем случае это em

"Fully <em>Furni<\/em>shed |Downtown and Canal Views",

результат

Мебель

Ответы [ 6 ]

0 голосов
/ 17 ноября 2018

Если вы просто хотите извлечь текст между <em> и <\/em> (обратите внимание, что это не обычные теги HTML, как тогда было бы тегами <em> и </em>), мы можем просто захватить этот шаблон и замените его на захваченное значение группы 1. И нам не нужно беспокоиться о том, что присутствует вокруг соответствующего текста, и просто замените его на то, что было захвачено между тем текстом, который может фактически быть пустой строкой, потому что OP не упомянул никаких ограничений для этого. Регулярное выражение для сопоставления с этим шаблоном будет следующим:

<em>(.*?)<\\\/em>

ИЛИ для того, чтобы быть технически более надежным в уходе за необязательными пробелами (как я видел, кто-то указывает на комментарии в других ответах), где бы они ни находились внутри тегов, мы можем использовать это регулярное выражение,

<\s*em\s*>(.*?)<\s*\\\/em\s*>

И замените его на \1 или $1 в зависимости от того, где вы это делаете. Теперь, если эти теги содержат пустую строку или содержат некоторую фактическую строку внутри, на самом деле не имеет значения, как показано в моей демонстрации regex101.

Вот демоверсия

Дайте мне знать, если это соответствует вашим требованиям, а также, если какое-либо из ваших требований останется неудовлетворенным.

0 голосов
/ 14 ноября 2018

Вот базовая реализация в PHP (да, я знаю, что вы спрашивали Swift, но это для демонстрации части регулярных выражений):

<?php

$in = "Fully <em>Furni</em>shed |Downtown and Canal Views";

$m = preg_match("/<([^>]+)>([^>]+)<\/\\1>([^ ]+|$)/i", $in, $t);    

$s = $t[2] . $t[3];

echo $s;

Выход:

ZC-MGMT-04:~ jv$ php -q regex.php
Furnished

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

0 голосов
/ 13 ноября 2018

Учитывая эту строку:

let str = "Fully <em>Furni<\\/em>shed |Downtown and Canal Views"

и соответствующий NSRange:

let range = NSRange(location: 0, length: (str as NSString).length)

Давайте создадим регулярное выражение, которое будет соответствовать буквам от <em> до </em> или перед </em>

let regex = try NSRegularExpression(pattern: "(?<=<em>)\\w+(?=<\\\\/em>)|(?<=<\\\\/em>)\\w+")

Что он делает:

  • искать 1 или более букв: \\w+,
  • , которым предшествуют <em>: (?<=<em>) (положительный lookbehind ),
  • и затем <\/em>: (?=<\\\\/em>) (положительный прогноз ),
  • или: |
  • буквы: \\w+,
  • , которому предшествует <\/em>: (?=<\\\\/em>) (положительный lookbehind )

Получим совпадения:

let matches = regex.matches(in: str, range: range)

которые мы можем превратить в подстроки:

let strings: [String] = matches.map { match in
    let start = str.index(str.startIndex, offsetBy: match.range.location)
    let end = str.index(start, offsetBy: match.range.length)
    return String(str[start..<end])
}

Теперь мы можем объединять строки в четные индексы, с нечетными:

let evenStride = stride(from: strings.startIndex,
               to: strings.index(strings.endIndex, offsetBy: -1),
               by: 2)
let result = evenStride.map { strings[$0] + strings[strings.index($0, offsetBy: 1)]}

print(result)  //["Furnished"]

Мы можем проверить это с другой строкой:

let str2 = "<em>Furni<\\/em>shed <em>balc<\\/em>ony <em>gard<\\/em>en"

результат будет:

["Furnished", "balcony", "garden"]
0 голосов
/ 11 ноября 2018

регулярное выражение:

Если вы хотите достичь этого с помощью регулярных выражений, вы можете использовать ответ Валекса :

public extension String {
    public func capturedGroups(withRegex pattern: String) -> [String] {
        var results = [String]()

        var regex: NSRegularExpression
        do {
            regex = try NSRegularExpression(pattern: pattern, options: [])
        } catch {
            return results
        }
        let matches = regex.matches(in: self, options: [], range: NSRange(location:0, length: self.count))

        guard let match = matches.first else { return results }

        let lastRangeIndex = match.numberOfRanges - 1
        guard lastRangeIndex >= 1 else { return results }

        for i in 1...lastRangeIndex {
            let capturedGroupIndex = match.range(at: i)
            let matchedString = (self as NSString).substring(with: capturedGroupIndex)
            results.append(matchedString)
        }

        return results
    }
}

как это:

let text = "Fully <em>Furni</em>shed |Downtown and Canal Views"
print(text.capturedGroups(withRegex: "<em>([a-zA-z]+)</em>"))

результат:

[ "Furni"]

NSAttributedString:

Если вы хотите сделать подсвечивание или вам нужно только избавиться от тегов или по любой другой причине, по которой вы не можете использовать первое решение, вы также можете сделать это с помощью NSAttributedString:

extension String {
    var attributedStringAsHTML: NSAttributedString? {
        do{
            return try NSAttributedString(data: Data(utf8),
                                          options: [
                                            .documentType: NSAttributedString.DocumentType.html,
                                            .characterEncoding: String.Encoding.utf8.rawValue],
                                          documentAttributes: nil)
        }
        catch {
            print("error: ", error)
            return nil
        }
    }

}

func getTextSections(_ text:String) -> [String] {
    guard let attributedText = text.attributedStringAsHTML else {
        return []
    }
    var sections:[String] = []
    let range = NSMakeRange(0, attributedText.length)

    // we don't need to enumerate any special attribute here,
    // but for example, if you want to just extract links you can use `NSAttributedString.Key.link` instead
    let attribute: NSAttributedString.Key = .init(rawValue: "")

    attributedText.enumerateAttribute(attribute,
                                      in: range,
                                      options: .longestEffectiveRangeNotRequired) {attribute, range, pointer in

                                        let text = attributedText.attributedSubstring(from: range).string
                                        sections.append(text)
    }
    return sections
}

let text = "Fully <em>Furni</em>shed |Downtown and Canal Views"
print(getTextSections(text))

результат:

["Полностью", "Фурни", "Сарай | Вид на центр города и канал"]

0 голосов
/ 11 ноября 2018

Не регулярное выражение, но для получения всех слов в тегах, например [Furni, sma]:

let text = "Fully <em>Furni<\\/em>shed <em>sma<\\/em>shed |Downtown and Canal Views"
let emphasizedParts = text.components(separatedBy: "<em>").filter { $0.contains("<\\/em>")}.flatMap { $0.components(separatedBy: "<\\/em>").first }

Для полных слов, например [меблировано, разбито]:

let emphasizedParts = text.components(separatedBy: " ").filter { $0.contains("<em>")}.map { $0.replacingOccurrences(of: "<\\/em>", with: "").replacingOccurrences(of: "<em>", with: "") }
0 голосов
/ 08 ноября 2018

Я думаю, вы хотите удалить теги.

Если обратная косая черта является только виртуальной, шаблон довольно прост: в основном <em> с дополнительной косой чертой /?

let trimmedString = string.replacingOccurrences(of: "</?em>", with: "", options: .regularExpression)

Учитывая также обратную косую черту, это

let trimmedString = string.replacingOccurrences(of: "<\\\\?/?em>", with: "", options: .regularExpression)

Если вы хотите извлечь только Furnished, вам нужно захватить группы: Строка между тегами и всем, что находится после закрывающего тега, до следующего символа пробела.

let string = "Fully <em>Furni<\\/em>shed |Downtown and Canal Views"
let pattern = "<em>(.*)<\\\\?/em>(\\S+)"
do {
    let regex = try NSRegularExpression(pattern: pattern)
    if let match = regex.firstMatch(in: string, range: NSRange(string.startIndex..., in: string)) {
        let part1 = string[Range(match.range(at: 1), in: string)!]
        let part2 = string[Range(match.range(at: 2), in: string)!]
        print(String(part1 + part2))
    }
} catch { print(error) }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...