Comment faire correspondre secret_code_data dans une chaîne:

xeno://soundcloud/?code=secret_code_data#

J'ai essayé

val regex = Regex("""xeno://soundcloud/?code=(.*?)#""")
field = regex.find(url)?.value ?: ""

Sans chance. Je soupçonne ? avant que le code ne soit le problème, devrais-je y échapper d'une manière ou d'une autre. Pouvez-vous m'aider?

30
ssuukk 4 janv. 2016 à 18:35

2 réponses

Meilleure réponse

Voici trois options, la première fournissant un bon Regex qui fait ce que vous voulez, et les deux autres pour analyser les URL en utilisant une alternative à Regex qui gère correctement le codage / décodage des composants URL.

Analyse à l'aide de Regex

REMARQUE: La méthode Regex est dangereuse dans la plupart des cas d'utilisation car elle n'analyse pas correctement l'URL en composants, puis décode chaque composant séparément. Normalement, vous ne pouvez pas décoder l'URL entière en une seule chaîne, puis l'analyser en toute sécurité car certains caractères codés pourraient confondre le Regex plus tard. Ceci est similaire à l'analyse XHTML à l'aide de regex (comme décrit ici). Voir les alternatives à Regex ci-dessous.

Voici une regex nettoyée en tant que cas de test unitaire qui gère plus d'URL en toute sécurité. À la fin de cet article, vous trouverez un test unitaire que vous pouvez utiliser pour chaque méthode.

private val SECRET_CODE_REGEX = """xeno://soundcloud[/]?.*[\?&]code=([^#&]+).*""".toRegex()
fun findSecretCode(withinUrl: String): String? =
        SECRET_CODE_REGEX.matchEntire(withinUrl)?.groups?.get(1)?.value

Cette expression régulière gère ces cas:

  • avec et sans fin / dans le chemin
  • avec et sans fragment
  • paramètre comme premier, milieu ou dernier dans la liste des paramètres
  • paramètre comme seul paramètre

Notez que la manière idiomatique de créer une expression régulière dans Kotlin est someString.toRegex(). Elle et d'autres méthodes d'extension peuvent être trouvées dans la Kotlin API Reference.

Analyse à l'aide d'UriBuilder ou d'une classe similaire

Voici un exemple utilisant le UriBuilder de la bibliothèque Klutter pour Kotlin . Cette version gère encodage / décodage, y compris des encodages Unicode JavaScript plus modernes non gérés par la classe URI standard Java ( qui pose de nombreux problèmes ). C'est sûr, facile et vous n'avez pas à vous soucier de cas particuliers.

La mise en oeuvre:

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
}

L'artefact Klutter uy.klutter:klutter-core-jdk6:$klutter_version est petit et inclut d'autres extensions, notamment l'encodage / décodage d'URL modernisé. (Pour $klutter_version utilisez la version la plus récente).

Analyse avec la classe URI JDK

Cette version est un peu plus longue et montre que vous devez analyser vous-même la chaîne de requête brute, décoder après l'analyse, puis trouver le paramètre de requête:

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
}

Cela pourrait être écrit comme une extension sur la classe URI elle-même:

fun URI.findSecretCode(): String? { ... }

Dans le corps, supprimez la variable parsed et utilisez this puisque vous avez déjà l'URI, eh bien, vous êtes l'URI. Puis appelez en utilisant:

val secretCode = URI(myTestUrl).findSecretCode()

Tests unitaires

Étant donné l'une des fonctions ci-dessus, exécutez ce test pour prouver qu'il fonctionne:

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")
        }
    }
41
Community 23 mai 2017 à 10:31

Ajoutez un échappement avant le premier point d'interrogation car il a une signification particulière

? 

Devient

\?

Vous capturez également le code secret dans le premier groupe. Pas sûr que le code kotlin qui suit extrait le premier groupe.

0
buckley 4 janv. 2016 à 15:45