7
votes

Expression régulière très compliquée

J'ai été coincé à essayer d'écrire cette expression régulière dont j'ai besoin. Fondamentalement, j'ai une longue chaîne composée de deux types de données différents:

  1. [A-F0-9] {32}
  2. [a-za-z0-9 =] {x}

    Le truc est, X n'est que constant dans l'instance particulière: si dans un cas, il se trouve être 12, il sera 12 pour cet ensemble de données particulier, mais la prochaine fois que j'exécute l'expression régulière. besoin d'être 15, ou 45 par exemple. J'ai un nombre imprévisible de type (1) entre chaque type de type (2). Mon objectif est de "récolter" toutes les données de type (2).

    Par exemple, je pourrais avoir une chaîne de formulaire suivant: xxx

    (tous mis en place sans délimitation) . J'en ai besoin pour renvoyer une chaîne composée des 33 caractères du jeu de caractères [A-ZA-Z0-9 =]. Le fait que le nombre des caractères de chacune des sous-chaînes est constant dans l'instance (dans l'affaire ci-dessus, il aurait 11 ans, mais il aurait pu être facilement 13) car il contient le jeu de caractères plus petit, il serait autrement impossible de savoir où une La chaîne commence et l'autre extrémité.

    J'ai essayé de faire fonctionner cela pendant près d'un mois maintenant, et je suis proche de la déchirure de mes cheveux. Je ne suis pas particulièrement bon à des expressions régulières ...

    Exemple de données: xxx

    je voudrais extraire " SGVSBG8GBXKGBMFTZSBPCYBVBGL2ZXIGAG9VCMF5IQ == ".


24 commentaires

Pouvez-vous s'il vous plaît fournir des exemples concrets?


Cela ne doit pas être regexp ... Je dois juste avoir les données de manière fiable. Je viens de supposer queregexp serait plus facile


Pouvez-vous fournir un échantillon de texte pour la recherche?


Sonne impossible. Avez-vous savoir x avant de commencer? (1) est un sous-ensemble de (2), vous n'avez donc aucune limites claires. Vous pouvez facilement développer x dans les deux sens, comment savez-vous où arrêter?


Juste hors de curiosité: D'où vient ce problème? Est-ce un problème du monde réel, ou juste une question de devoirs?


@Kobi: Juste mon point, je ne vois pas comment cela est possible sans savoir x à l'avance.


Il semble que il y a un délimiteur dans vos données: les nouvelles lignes entre enregistrements ... Si c'est le cas, cela vous aiderait vraiment ...


Dupliquer d'une version antérieure de vos questions: Stackoverflow.com/questions/1760150/...


KOBI: Je ne sais pas x Avant de commencer Sleeske: Malheureusement, c'est un problème mondial réel. Je suis bloqué avec toutes ces données que j'ai besoin de filtrer


@Mark Byers: Légèrement différent, en fait. Notez que les cordes courtes de cette question varient en longueur


@ERIC: Je mets dans les nouvelles lignes pour plus de clarté. Ils ne sont pas dans les données


Et si (2), par modification, n'a pas de caractères spéciaux (par exemple, au lieu de AB34 , il est AB34 - Vous ne pouvez pas le raconter à part le (1) bloc! Votre problème n'est pas solvable , tel que défini.


Kobi: Je suis enclin à penser que c'est parce que je peux le faire à la main, essayant une longueur qui semble probable, et si les longueurs ne correspondent pas, en essayant un autre, etc.


@Mala: Kobi's Droit, cependant. En général, vous ne pouvez pas résoudre ce problème. Vous pouvez le résoudre dans Certains cas et dans la pratique peut-être tous les cas du monde réel, mais vous ne pouvez jamais être sûr que vous pouvez le résoudre dans chaque cas . Peu importe que cela n'a pas d'importance, si vous avez des données finies finies que vous devez traiter une fois ...


D'où vient ces données? Y a-t-il une documentation pour ce format de données? Il ressemble à des données de base64 entrecoupées dans des données hexagonales; Mais si tel est le cas, [a-za-z0-9 =] ne le coupera pas pour correspondre aux données de base64, car ce n'est que 63 valeurs (et dans MIME BASE64, = est utilisé comme caractère de rembourrage à la fin; vous auriez besoin de correspondre également à + / si cela est vraiment mime base64 données Outils.ietf.org/html/RFC2045#Section-6.8 ).


Vous ne pouvez même pas savoir si vous avez les bonnes parties en utilisant votre cerveau. Vous dites que c'est 11 caractères de longueur, mais comment avez-vous découvert? Cela pourrait facilement être 12 et encore plus, car chaque bloc est entouré de chiffres.


ah oui, c'est vrai. Je suppose que je suis à la recherche de quelque chose qui fonctionnera dans la pratique, c'est-à-dire que chaque sous-chaîne de données de base64 contiendra au moins un caractère en dehors de [A-F0-9]: - \


@Brian: ah merci bonne prise! Oui, je devrai inclure '+' et '/' là-bas aussi, j'ai complètement oublié ceux-ci. Malheureusement, je n'ai ni docs ni rien, juste les données


@Joostk: Si vous obtenez le numéro de numéro, vous vous retrouverez presque certainement avec le mauvais nombre de caractères sur la dernière ligne. Bien que je reconnaisse qu'il est possible de construire un exemple où il correspond à ce qu'il soit utile de toute façon, cela semble assez peu probable de ne pas avoir de la matière terriblement beaucoup (si je reçois un ou deux faux résultats, ce n'est pas une affaire énorme)


Il est clair que vous devez avoir une raison quelconque pour essayer d'accéder à ces données, à moins que vous ne puissiez passer à des fichiers qui ressemblent à cela et que vous essayez de faire la médecine légisistique. Quel est le contexte ici? Quels sont ces fichiers censés contenir? Existe-t-il plus de structure aux données codées Hex ou au contenu décodé des données codées de base64?


Vous devez connaître les longueurs de la chaîne hexagonale et la chaîne de base64 avant de pouvoir tenter cela. Si vous ne le faites pas, vous ne pouvez essayer que de le forcer de brute en essayant chaque combinaison de longueur possible. Malheureusement, puisque la chaîne de base64 n'est pas (à en juger par ces données) aléatoire / hachée, il est tout à fait possible qu'il ne contienne pas de caractères non valides hexals. Par exemple, si les données source étaient une chaîne d'octets zéro (qui peut être très fréquente en effet), la base64 sort comme aaaaa ... qui est indiscernable de Hex.


Je dois vraiment demander: quel système produisent des données dans un format tel totalement convolué et célébral? Juste en dehors de la curiosité morbide ...


Dans votre exemple de données, comment savez-vous que les 7, 6, 1 et 7 précédant vos blocs de base64 ne font pas partie de ces blocs de base64, mais font plutôt partie des blocs hexagonaux? Vous semblez deviner basé sur le premier caractère rencontré qui n'est pas un caractère hexadécimal valide, mais les blocs de base64 peuvent toujours être étendus, potentiellement à consommer toute l'entrée. Vous avez besoin d'un meilleur critère pour un match réussi que vous avez donné, tel qu'un motif dans les données hexagonales ou base64.


Pour renforcer le point de "sans solution générale": Notez que pour la taille d'entrée s , x = S-32 est toujours une solution correcte ( C'est-à-dire qu'un 32 bloc de [A-F0-9], le reste un "bloc X"). Qu'est-ce qui vous dit s'il s'agit ou non de la "bonne" solution? Votre problème est simplement sous-spécifié.


14 Réponses :


0
votes

Comment déterminez-vous cette magie x ?

  • Si vous savez X à l'avance pour chaque jeu de données, utilisez simplement votre regex et remplacez X avec la valeur réelle avant chaque invocation (dans la plupart des langues, vous pouvez composer une chaîne de caractères arbitraire et utiliser cela une regex).
  • Si vous ne connaissez pas x , je ne vois pas comment il y a une réponse, car elle ne peut pas être déterminée à partir des données d'entrée seules (comme vous le soulignez).

    éditer:

    de votre commentaire, 2) semble appliquer: x non connu à l'avance.

    Comme indiqué, il y aura en général plus d'une solution pour une pièce donnée de données d'entrée.

    Vous pouvez écrire un programme qui extraire toutes les sous-chaînes qui satisfont à vos critères. S'il n'y a qu'une seule solution pour une entrée donnée, vous êtes chanceux. Sinon, vous devrez décider que vous préférez le mieux.

    Pour extraire les sous-chaînes, une idée (éventuellement pas optimale) serait de simplement boucler à travers toutes les valeurs raisonnables pour x et essayez votre regex pour chaque x . Si cela correspond, vous avez trouvé une solution. Si plus d'un x correspond à des correspondances, il y a plus d'une solution.

    Il y a probablement un moyen plus performant de le faire, mais si vous avez une limite supérieure raisonnablement faible pour x cela devrait être faisable. (Évidemment, la taille des données - 32 est toujours une limite supérieure pour x , donc cela fonctionne en principe toujours).


1 commentaires

Lorsque vous le faites à la main, je détermine X en essayant une valeur «Semblant probable», et si je me retrouvais pas assez ou trop de caractères à la fin, essayant d'un X différent, etc. J'essaie de trouver un façon de faire cela par programme.



-1
votes

Pourquoi ne pas simplement faire cela:

^[a-zA-Z0-9]+[=]+$


2 commentaires

Cela ramasserait également toutes les choses [A-F0-9], non?


Non, pas aussi loin que je le vois, car le + indique que = y a-t-il au moins.



5
votes

Je ne crois pas que les expressions régulières sont le bon outil de ce problème.

Une chose qui me dérange est que la plage [A-F0-9] est incluse dans la plage [A-ZA-Z0-9 =] et, car il n'y a pas de délimiteurs et la longueur des enregistrements est variable, le La frontière entre deux enregistrements semble assez floue.

Vous pouvez avoir un Heuristic qui fonctionne pour déterminer où les enregistrements commencent et se terminent en trouvant Un motif dans les données et vous pouvez ensuite appliquer des expressions régulières en utilisant ce modèle, mais il est peu probable que des expressions régulières vous aident à découvrir ce modèle en premier lieu.


3 commentaires

Oui c'est précisément mon problème. Je pourrais écrire une regexp pour le cas où les chaînes courtes sont (x) des caractères longues et bouclés à travers tous les X raisonnables, mais cela semble être un bazooka à une moustique ..


Dans le pire des cas, X pourrait être 32 et tous les caractères de base64 pourraient tomber dans la gamme hexagonale: vous ne seriez pas capable de les distinguer, n'est-ce pas?


La méthode "Bazooka" que j'ai implémenté, cela fonctionne mieux et meilleur (moins de faux matchs) tels que x augmente. x = 32 ne causerait aucun problème particulier. Bien sûr, vous pouvez concevoir un exemple où il échouera, mais les chances de données réelles échouent accidentellement sont de plus en plus éloignées que X augmente de taille. x = 1 provoque le plus de problèmes.



0
votes

Comment est à propos de quelque chose sur les lignes de: xxx

puis concaténate les correspondances de ([a-za-z0-9 =] *) .

Pouvez-vous compter sur le [A-ZA-Z0-9 =] * partie étant la même longueur à chaque fois? Ou devez-vous le vérifier? Si vous devez vérifier la longueur à chaque fois, ce problème n'est pas résolu avec une regex (c'est-à-dire qu'il n'est pas une langue régulière, mais plutôt une langue sans contexte au moins ). < / p>


1 commentaires

Le [A-ZA-Z0-9 =] * est la même longueur à chaque fois dans une instance particulière, oui. Donc, dans un ensemble de données, ce sera toujours, par exemple, 11. Mais dans le prochain jeu de données, cela pourrait être 13



0
votes

est-ce une chance que la dernière chaîne que vous souhaitez correspondre aux finitions avec '=='?

Sinon, vous pouvez correspondre à la finition de la ligne avec '==' en premier, calculez sa taille, puis utilisez-la comme x pour saisir les autres lignes que vous souhaitez saisir.


2 commentaires

Parfois, il se terminera par ==, parfois avec =, et parfois sans aucun fichier. C'est fondamentalement une chaîne codée de base64


D'ACCORD. Ensuite, peut-être que vous pourriez essayer de filtrer les lignes contenant des caractères que vous ne trouveriez pas dans les autres lignes (lettres majuscules, signes égaux, etc.). Toutes ces lignes doivent avoir la même longueur x , puis vous pouvez l'appliquer à votre regex.



0
votes

Je pense vraiment que vous ne pouvez pas récolter toutes vos pièces de type (2) si vous ne savez pas combien de morceaux de type (1) vous aurez et la longueur d'entre eux.

La meilleure solution consisterait à analyser la ligne de chaîne par ligne et à appliquer une regexp pour chaque lignes. Si cela correspond au type (2), ensuite le concaténer dans votre chaîne de résultat.

Si votre chaîne n'est pas divisée par des lignes, faites un preg_replace avant de l'analyser.


0 commentaires

1
votes

Si vous connaissez la taille de chaque champ, j'utiliserais simplement substr. xxx

ou utilisez str_split et convertissez la ligne en une matrice et construisez les substrings des pièces de la matrice. < / p>


0 commentaires

3
votes

Je ne pense pas que vos "types" des données sont suffisamment définis pour rendre le problème résolu pour tous les cas, que vous utilisiez des expressions régulières du tout.

Depuis, en jugeant de votre exemple, le type 1 peut se produire plusieurs fois dans une rangée et le type 2 peut ressembler à de type 1 puisque les ensembles de caractères se chevauchent, je ne vois pas comment vous pouvez les distinguer pour tous les cas même lorsque Vous connaissez X (qui, à en juger de la question, je ne suis pas sûr de le faire).

comme exemple primitif, étant donné une chaîne de 2 000 répétitions de la lettre "A", comment pourriez-vous dire des types 1 et 2?

S'il y a une possibilité pour tout d'avoir tout ce qui vous donne que des données mises dans des délimiteurs explicites, faites cela. Sinon, vous devrez utiliser des heuristiques pour désambiguez, et je ne pense pas qu'un regexp est le bon outil pour cela.


0 commentaires

-1
votes

Il semble que vous ne vous souciez pas vraiment du contenu de la chaîne, cela devrait donc faire. Bien sûr, vous devez connaître le numéro à utiliser. De plus, je présume que les données sont toutes en une ligne (je présume que vous mettez la nouvelle ligne juste pour clarifier) ​​

^. {192} (. {11}). {96} (. {11}). {160} (. {11}). * $

Ensuite, vous devez simplement fusionner le dernier élément des matchs.

== Ajouté

OK car la majuscule semble être l'indicateur de l'endroit où vous devez extraire.

Ce que vous avez à faire est d'abord que tout se termine d'un caractère majuscule, obtenez le multiple de 32 plus petit que chaque position, puis utilisez une sous-chaîne pour extraire le contenu souhaité. Comment obtenez-vous le 11 à nouveau?


3 commentaires

Utilisation de Regexp pour faire ce que cette réégycle n'est qu'une perte de temps et de ressources.


Les 192, 96 et 160 pourraient être des multiples de 32 et il pourrait y en avoir plus. Le 11 n'est pas constant entre les jeux de données non plus. C'est seulement constant pour une course particulière


Je sais que c'est un gaspillage, je pense que la solution de sous-chaîne est la solution appropriée, mais il demande à la réégalité.



1
votes

Vous êtes sur le mauvais chemin imo. Le motif est une donnée codée hex-str comportant des pièces codées de base64. Ces données hexagonales doivent signifier quelque chose qui peut être utilisé pour déterminer lorsque les données "nécessaires" commencent. De plus, si les données d'origine que vous avez réactivé est divisée en rangées qui ont la même longueur, cela devrait également signifier quelque chose. Vous devez "comprendre" les données, ne pas utiliser un motif de Regexp sans cerveau pour le correspondre à ce qui ne semble pas possible d'ici.


1 commentaires

Les données hexagonales, autant que je puisse dire, est le résultat de chaînes aléatoires de MD5'ing, et comme telle ne veut rien dire. Ce sont les données de base64 qui signifie quelque chose, mais l'hexagone est entrecoupé de manière à briser des morceaux de base64 qui doivent être ensemble, donc je ne pouvais donc pas décoder et filtrer la poubelle.



2
votes

Il semble que les données que vous utilisez entre les chaînes hexagonales soient entre les chaînes hexagonales sont base64 . Le problème réel que vous décrivez semble insoluble avec les restrictions que vous avez données (ne peut assumer aucune longueur, etc.).

Mais la grande chose que vous devez être consciente est que le jeu de caractères de base64 contient également les caractères '+' et '/'. Les caractères '=' sont le rembourrage comme la longueur de l'ensemble (dans votre cas, Concaténated) BASE64 codé Bit est toujours un multiple de 4 caractères.


1 commentaires

Ah merci pour le conseil sur ces caractères supplémentaires. Tout ce que je savais sur la base64, c'était ce que j'avais vu de l'observation, et je suppose que je n'avais pas remarqué ceux-ci!



2
votes

Comme certaines autres réponses ont dit, je pense que les expressions régulières ne sont pas correctes ici, ou du moins pas au début. Vous devez commencer par une approche algorithmique. Voici pourquoi: Vous ne pouvez pas connaître la valeur de x à coup sûr. Le mieux que vous puissiez faire est de faire fonctionner les estimations de données de x pour chaque morceau de type 2. Ensuite, vous avez besoin d'un mécanisme permettant de deviner la valeur la plus probable de x basé sur toutes les estimations (éventuellement en utilisant quelque chose comme une montée en colline). Après cela, vous pouvez appliquer une expression régulière ou simplement sortir des morceaux de la longueur appropriée.


0 commentaires

0
votes

ou vous pouvez simplement vérifier les caractères autorisés via Regex, puis vérifiez la longueur de la chaîne via la propriété / la fonction. On dirait que vous rendez les choses plus compliquées qu'elles ne devraient l'être.


0 commentaires

8
votes

C'est ta journée chanceuse! Le problème n'est pas résolu en général, mais je pense que ce qui suit donnera presque toujours la bonne réponse aux données typiques de la vie réelle: xxx pré>

sortie dans ce cas: p>

SGVsbG8gbXkgbmFtZSBpcyBvbGl2ZXIgaG9vcmF5IQ==


9 commentaires

Notez que dans la question, il mentionne la partie [A-F0-9] ne sera pas toujours de 32 caractères (bien que ce soit le cas dans son exemple). Donc, votre solution pourrait ne pas fonctionner dans l'affaire Général (tout ce qui peut être ...).


@sleske: hmm ... j'ai raté ça. Où dit-il ça?


C'est en fait le problème principal, la durée de la première pièce peut changer tout le temps ...>. <


@dasilvj: 192 = 32 * 6, 96 = 32 * 3, 160 = 32 * 5. Donc, il n'y a pas de problème en fait. Tout multiple de 32 est traité correctement. Notez le + après (... {32})


@sleske il dit dans une question en double stackoverflow.com/questions/1760150/... que les données hexagonales seront toujours multiples de 32 caractères; Et il dit dans les commentaires sur une autre réponse ici que les données hexagonales sont composées de sommes de MD5, qui sont 128 bits, ou 32 chiffres hexagonaux de longue durée. Ainsi, correspondant à une ou plusieurs cordes de 32 caractères longues semble être correcte ici, même si elle n'est pas très bien spécifiée dans la question initiale.


Dans l'exemple de son autre thread, la longueur des chaînes de base-64 varie également. C'est pourquoi je n'ai pas suggéré cette solution à l'origine. Mais il semble assez clair cette fois-ci que les fragments de base-64 ont toujours la même longueur, et c'est pourquoi cette solution fonctionne.


Juste une modification rapide, le "[A-ZA-Z0-9 =]" S devrait être "[A-ZA-Z0-9 = + \ /]". Désolé, ce n'était pas dans ma spécification originale mais Naididble a souligné que la base64 contient également les caractères '/' et '+'. Mais merci beaucoup, cela fonctionne magnifiquement :) Je vous dois vraiment une bière!


Mala: En fait, je savais que la Regex de base-64 avait tort (il devrait bien sûr contenir 64 caractères plus la remplisseuse qui ne peut être qu'à la fin). Je me concentrais tout simplement sur la résolution de la question et j'ai complètement oublié de le corriger ou même de le mentionner. Il a de la chance que quelqu'un d'autre l'indique. Vous voudrez peut-être également régler les limites de la longueur des segments de base-64 que je suis codé. J'ai utilisé 1 à 20 dans l'exemple, mais vous pouvez augmenter 20 à quelque chose de plus élevé si vous avez besoin.


Merci, je l'ai déjà fait - je l'ai incrémentalement amené et effectivement fini par un taux de réussite de 100% lorsque je l'ai mis à 50. Donc, en bref, vous êtes officiellement mon héros