Как сопоставить secret_code_data в строке:
xeno://soundcloud/?code=secret_code_data#
я пробовал
val regex = Regex("""xeno://soundcloud/?code=(.*?)#""") field = regex.find(url)?.value ?: ""
без везения. Я подозреваю ? прежде чем код может быть проблемой, я должен каким-то образом избежать этого. Вы можете помочь?
Вот три варианта: первый, обеспечивающий хорошее Regex, который делает то, что вы хотите, а два других – для анализа URL-адресов, используя альтернативу Regex, которые правильно обрабатывают кодирование / декодирование URL-адреса.
ПРИМЕЧАНИЕ. Метод Regex небезопасен в большинстве случаев использования, поскольку он неправильно анализирует URL-адрес в компонентах, а затем декодирует каждый компонент отдельно. Обычно вы не можете декодировать весь URL-адрес в одну строку, а затем безопасно разбираться, потому что некоторые закодированные символы могут запутать Regex позже. Это похоже на разбор XHTML с использованием регулярного выражения (как описано здесь ). См. Альтернативы Regex ниже.
Вот очищенное регулярное выражение как единичный тестовый пример, который безопасно обрабатывает больше URL-адресов. В конце этого сообщения это единичный тест, который вы можете использовать для каждого метода.
private val SECRET_CODE_REGEX = """xeno://soundcloud[/]?.*[\?&]code=([^#&]+).*""".toRegex() fun findSecretCode(withinUrl: String): String? = SECRET_CODE_REGEX.matchEntire(withinUrl)?.groups?.get(1)?.value
Это регулярное выражение обрабатывает эти случаи:
/
в пути Обратите внимание, что идиоматическим способом создания регулярного выражения в Kotlin является someString.toRegex()
. Это и другие методы расширения можно найти в Справочнике API Kotlin .
Вот пример использования UriBuilder
из библиотеки Klutter для Kotlin . Эта версия обрабатывает кодирование / декодирование, включая более современные кодировки jQi-кода JavaScript, которые не обрабатываются стандартным классом URI
Java ( который имеет много проблем ). Это безопасно, легко, и вам не нужно беспокоиться о каких-либо особых случаях.
Реализация:
fun findSecretCode(withinUrl: String): String? { fun isValidUri(uri: UriBuilder): Boolean = uri.scheme == "xeno" && uri.host == "soundcloud" && (uri.encodedPath == "/" || uri.encodedPath.isNullOrBlank()) val parsed = buildUri(withinUrl) return if (isValidUri(parsed)) parsed.decodedQueryDeduped?.get("code") else null }
Clutter uy.klutter:klutter-core-jdk6:$klutter_version
артефакт uy.klutter:klutter-core-jdk6:$klutter_version
мал и включает в себя некоторые другие расширения, включая модернизированное кодирование / декодирование URL. (Для $klutter_version
используйте самую $klutter_version
версию ).
Эта версия немного длиннее и показывает, что вам нужно самостоятельно разобрать сырую строку запроса, декодировать после разбора, а затем найти параметр запроса:
fun findSecretCode(withinUrl: String): String? { fun isValidUri(uri: URI): Boolean = uri.scheme == "xeno" && uri.host == "soundcloud" && (uri.rawPath == "/" || uri.rawPath.isNullOrBlank()) val parsed = URI(withinUrl) return if (isValidUri(parsed)) { parsed.getRawQuery().split('&').map { val parts = it.split('=') val name = parts.firstOrNull() ?: "" val value = parts.drop(1).firstOrNull() ?: "" URLDecoder.decode(name, Charsets.UTF_8.name()) to URLDecoder.decode(value, Charsets.UTF_8.name()) }.firstOrNull { it.first == "code" }?.second } else null }
Это можно было бы написать как расширение для самого класса URI:
fun URI.findSecretCode(): String? { ... }
В теле удалить parsed
переменную и использовать this
поскольку у вас уже есть URI, хорошо вы являетесь URI. Затем позвоните, используя:
val secretCode = URI(myTestUrl).findSecretCode()
Для любой из вышеперечисленных функций выполните этот тест, чтобы убедиться, что он работает:
class TestSo34594605 { @Test fun testUriBuilderFindsCode() { // positive test cases val testUrls = listOf("xeno://soundcloud/?code=secret_code_data#", "xeno://soundcloud?code=secret_code_data#", "xeno://soundcloud/?code=secret_code_data", "xeno://soundcloud?code=secret_code_data", "xeno://soundcloud?code=secret_code_data&other=fish", "xeno://soundcloud?cat=hairless&code=secret_code_data&other=fish", "xeno://soundcloud/?cat=hairless&code=secret_code_data&other=fish", "xeno://soundcloud/?cat=hairless&code=secret_code_data", "xeno://soundcloud/?cat=hairless&code=secret_code_data&other=fish#fragment" ) testUrls.forEach { test -> assertEquals("secret_code_data", findSecretCode(test), "source URL: $test") } // negative test cases, don't get things on accident val badUrls = listOf("xeno://soundcloud/code/secret_code_data#", "xeno://soundcloud?hiddencode=secret_code_data#", "http://www.soundcloud.com/?code=secret_code_data") badUrls.forEach { test -> assertNotEquals("secret_code_data", findSecretCode(test), "source URL: $test") } }
Добавьте побег перед первым вопросительным знаком, поскольку он имеет особое значение
?
становится
\?
Вы также захватываете секретный код в первой группе. Не уверен, что код kotlin, который следует, – это извлечение первой группы.