0 votes

Comment traiter une chaîne de caractères telle que celle-ci à l'aide d'expressions régulières ?

Comment puis-je créer une regex pour une chaîne de caractères telle que celle-ci :

<SERVER> <SERVERKEY> <COMMAND> <FOLDERPATH> <RETENTION> <TRANSFERMODE> <OUTPUTPATH> <LOGTO> <OPTIONAL-MAXSIZE> <OPTIONAL-OFFSET>

La plupart de ces champs sont de simples mots, mais certains d'entre eux peuvent être des chemins, tels que FOLDERPATH, OUTPUTPATH, ces chemins peuvent également être des chemins avec un nom de fichier et un caractère générique ajoutés.

La rétention est un nombre, et le mode de transfert peut être bin ou ascii. La question est LOGTO qui peut être un chemin avec le nom du fichier journal en annexe ou peut être NO, ce qui signifie pas de fichier journal.

Le problème principal, ce sont les arguments facultatifs, ce sont tous deux des nombres, et OFFSET ne peut pas exister sans MAXSIZE, mais MAXSIZE peut exister sans offset.

Voici quelques exemples :

loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256 300
loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256
loveserver love copy /hats* 300 ascii C:\Puppies\no\ C:\log\love.log 256

Le problème principal est que les chemins peuvent contenir des espaces, donc si j'utilise . pour tout faire correspondre, la regex finit par se casser, lors de l'analyse des arguments optionnels où la destination LOG finit par être attachée au chemin de sortie.

De plus, si je finis par utiliser . et que je commence à en retirer des parties, la regex commencera à mettre des choses là où elle ne devrait pas.

Voici ma combinaison :

^(\s+)?(?P<SRCHOST>.+)(\s+)(?P<SRCKEY>.+)(\s+)(?P<COMMAND>COPY)(\s+)(?P<SRCDIR>.+)(\s+)(?P<RETENTION>\d+)(\s+)(?P<TRANSFER_MODE>BIN|ASC|BINARY|ASCII)(\s+)(?P<DSTDIR>.+)(\s+)(?P<LOGFILE>.+)(\s+)?(?P<SIZE>\d+)?(\s+)?(?P<OFFSET>\d+)?$

4voto

Adam Rosenfield Points 176408

Le problème est que, comme vous autorisez les espaces dans les noms de fichiers et que vous utilisez des espaces pour séparer les champs, la solution est ambiguë. Vous devez soit utiliser un autre caractère de séparation des champs qui ne peut pas apparaître dans les noms de fichiers, soit utiliser une autre méthode pour représenter les noms de fichiers contenant des espaces, par exemple en les mettant entre guillemets.

3voto

Xetius Points 10445

C'est théoriquement possible, mais vous rendez les choses incroyablement difficiles pour vous. Vous avez un certain nombre de problèmes ici :

1) Vous utilisez un espace comme séparateur et vous autorisez également les espaces dans les noms de chemin. Vous pouvez éviter ce problème en forçant l'application à utiliser des chemins sans espace.

2) Vous avez 2 paramètres optionnels à la fin. Cela signifie qu'avec la ligne se terminant par " C:\LogTo Chemin 256 300" vous n'avez aucune idée si le chemin est C:\LogTo Chemin 256 300 sans paramètres optionnels ou C:\Log Vers le chemin 256 avec un paramètre facultatif ou C:\LogTo Chemin avec 2 paramètres facultatifs.

Il serait facile d'y remédier avec un algorithme de remplacement sur la sortie. Remplacer les espaces par des traits de soulignement et les traits de soulignement par des doubles traits de soulignement. Vous pourriez donc inverser cela de manière fiable après avoir divisé le fichier journal sur les espaces.

Même en tant qu'humain, vous ne pourriez pas remplir cette fonction de manière fiable à 100%.

Si vous présumez que tous les chemins se terminent par un astérisque, une barre oblique inversée ou un .log, vous pouvez utiliser un lookahead positif pour trouver la fin des chemins, mais sans règles à ce sujet, vous ne pouvez rien faire.

J'ai l'impression qu'une simple expression rationnelle serait trop difficile à mettre en œuvre et rendrait fou quiconque essaierait de maintenir le code. Je suis un adorateur des regex, que j'utilise chaque fois que c'est possible, et je n'essaierais pas de le faire.

1voto

Markus Jarderot Points 33893

Le fractionnement sur les espaces blancs ne fonctionnera jamais. Mais si vous pouvez faire des hypothèses sur les données, cela peut fonctionner.

Quelques hypothèses que j'avais en tête :

  • SERVER , SERVERKEY y COMMAND ne contenant pas d'espaces : \S+
  • FOLDERPATH commençant par une barre oblique : /.*?
  • RETENTION étant un nombre : \d+
  • TRANSFERMODE ne contenant pas d'espaces : \S+
  • OUTPUTPATH commençant par un lecteur et se terminant par une barre oblique : [A-Z]:\\.*?\\
  • LOGTO soit le mot " NO ", ou un chemin commençant par un lecteur : [A-Z]:\\.*?
  • MAXSIZE y OFFSET étant un nombre : \d+

Tout mettre en place :

^\s*
(?P<SERVER>\S+)\s+
(?P<SERVERKEY>\S+)\s+
(?P<COMMAND>\S+)\s+
(?P<FOLDERPATH>/.*?)\s+   # Slash not that important, but should start with non-whitespace
(?P<RETENTION>\d+)\s+
(?P<TRANSFERMODE>\S+)\s+
(?P<OUTPUTPATH>[A-Z]:\\.*?\\)\s+   # Could also support network paths
(?P<LOGTO>NO|[A-Z]:\\.*?)
(?:
  \s+(?P<MAXSIZE>\d+)
  (?:
    \s+(?P<OFFSET>\d+)
  )?
)?
\s*$

En une ligne :

^\s*(?P<SERVER>\S+)\s+(?P<SERVERKEY>\S+)\s+(?P<COMMAND>\S+)\s+(?P<FOLDERPATH>/.*?)\s+(?P<RETENTION>\d+)\s+(?P<TRANSFERMODE>\S+)\s+(?P<OUTPUTPATH>[A-Z]:\\.*?\\)\s+(?P<LOGTO>NO|[A-Z]:\\.*?)(?:\s+(?P<MAXSIZE>\d+)(?:\s+(?P<OFFSET>\d+))?)?\s*$

Test :

>>> import re
>>> p = re.compile(r'^(?P<SERVER>\S+)\s+(?P<SERVERKEY>\S+)\s+(?P<COMMAND>\S+)\s+(?P<FOLDERPATH>/.*?)\s+(?P<RETENTION>\d+)\s+(?P<TRANSFERMODE>\S+)\s+(?P<OUTPUTPATH>[A-Z]:\\.*?\\)\s+(?P<LOGTO>NO|[A-Z]:\\.*?)(?:\s+(?P<MAXSIZE>\d+)(?:\s+(?P<OFFSET>\d+))?)?\s*$',re.M)
>>> data = r"""loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256 300
... loveserver love copy /muffin* 20 bin C:\Puppies\ NO 256
... loveserver love copy /hats* 300 ascii C:\Puppies\no\ C:\log\love.log 256"""
>>> import pprint
>>> for match in p.finditer(data):
...   print pprint.pprint(match.groupdict())
...
{'COMMAND': 'copy',
 'FOLDERPATH': '/muffin*',
 'LOGTO': 'NO',
 'MAXSIZE': '256',
 'OFFSET': '300',
 'OUTPUTPATH': 'C:\\Puppies\\',
 'RETENTION': '20',
 'SERVER': 'loveserver',
 'SERVERKEY': 'love',
 'TRANSFERMODE': 'bin'}
{'COMMAND': 'copy',
 'FOLDERPATH': '/muffin*',
 'LOGTO': 'NO',
 'MAXSIZE': '256',
 'OFFSET': None,
 'OUTPUTPATH': 'C:\\Puppies\\',
 'RETENTION': '20',
 'SERVER': 'loveserver',
 'SERVERKEY': 'love',
 'TRANSFERMODE': 'bin'}
{'COMMAND': 'copy',
 'FOLDERPATH': '/hats*',
 'LOGTO': 'C:\\log\\love.log',
 'MAXSIZE': '256',
 'OFFSET': None,
 'OUTPUTPATH': 'C:\\Puppies\\no\\',
 'RETENTION': '300',
 'SERVER': 'loveserver',
 'SERVERKEY': 'love',
 'TRANSFERMODE': 'ascii'}
>>>

0voto

Stroboskop Points 2039

Vous devez restreindre les champs entre les chemins de manière à ce que le regexp puisse les distinguer des noms de chemin.

Donc, à moins que vous ne mettiez un séparateur spécial, la séquence

<OUTPUTPATH> <LOGTO>

avec des espaces facultatifs ne fonctionnera pas.

Et si un chemin peut ressembler à ces champs, vous pourriez obtenir des résultats surprenants. Par exemple

c:\ 12 bin \ 250 bin \output

pour

<FOLDERPATH> <RETENTION> <TRANSFERMODE> <OUTPUTPATH>

est indiscernable.

Essayons donc de restreindre un peu les caractères autorisés :

<SERVER>, <SERVERKEY>, <COMMAND> no spaces -> [^]+
<FOLDERPATH> allow anything -> .+
<RETENTION> integer -> [0-9]+
<TRANSFERMODE> allow only bin and ascii -> (bin|ascii)
<OUTPUTPATH> allow anything -> .+
<LOGTO> allow anything -> .+
<OPTIONAL-MAXSIZE>[0-9]*
<OPTIONAL-OFFSET>[0-9]*

Donc, j'opterais pour quelque chose du genre

[^]+ [^]+ [^]+ .+ [0-9]+ (bin|ascii) .+ \> .+( [0-9]* ( [0-9]*)?)?

Avec un ">" pour séparer les deux chemins. Vous pourriez vouloir citer les noms de chemin à la place.

NB : Ceci a été fait dans l'urgence.

-1voto

Joel Coehoorn Points 190579

Les valeurs peuvent-elles être inférieures ou supérieures à ? Parce que si non, vous avez une solution très simple :

Remplacez simplement toutes les occurrences de ">" par ">", séparez-les en "><", et supprimez tous les moins que/plus que de chaque élément. C'est probablement plus long que le code regex, mais ce qui se passe sera plus clair.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X