2009-08-11 5 views
3

ai dis-je un fichier texte pour analyser, qui contient une partie du contenu de longueur fixe:Regex pour vérifier champ de longueur fixe avec l'espace tassée

123jackysee  45678887 
456charliewong  32145644 
<3><------16------><--8---> # Not part of the data. 

Les trois premiers caractères est ID, puis le nom d'utilisateur 16 caractères, puis 8 numéro de téléphone

Je voudrais écrire une expression régulière pour correspondre et vérifier l'entrée pour chaque ligne, celui que je viens avec:

(\d{3})([A-Za-z ]{16})(\d{8}) 

Le nom d'utilisateur devrait contenir 8-16 caractères. Mais ([A-Za-z ]{16}) correspondrait également à une valeur nulle ou un espace. Je pense à ([A-Za-z]{8,16} {0,8}) mais il détecterait plus de 16 caractères. Aucune suggestion?

Répondre

7

Non, non, non, non! :-)

Pourquoi les gens insistent-ils pour essayer de regrouper autant de fonctionnalités dans une seule instruction RE ou SQL?

Ma suggestion, faire quelque chose comme:

  • Assurez-vous que la longueur est 27.
  • Extrait les trois composantes en chaînes séparées (0-2, 3-18, 19-26).
  • Vérifiez que les premiers correspondent "\d{3}". Vérifiez que la seconde correspond à "[A-Za-z]{8,} *". Vérifiez que le troisième correspond à "\d{8}".

Si vous voulez que la totalité du chèque s'ajuste sur une ligne de code source, placez-la dans une fonction, isValidLine(), et appelez-la.

Même quelque chose comme ça ferait l'affaire:

def isValidLine(s): 
    if s.len() != 27 return false 
    return s.match("^\d{3}[A-za-z]{8,} *\d{8}$"): 

Ne vous laissez pas berner en pensant que son code Python propre, il est en fait PaxLang, mon propre pseudo-code propriétaire. Heureusement, c'est assez clair, la première ligne vérifie que la longueur est de 27, la seconde qu'elle correspond à l'ER donnée.

Le champ du milieu est automatiquement composé de 16 caractères en raison de la première ligne et du fait que les deux autres champs sont de longueur fixe dans le RE. L'ER assure également qu'il y a huit alphas ou plus suivis par le bon nombre d'espaces.

Pour faire ce genre de chose avec un seul RE serait une monstruosité comme:

^\d{3}(([A-za-z]{8} {8}) 
     |([A-za-z]{9} {7}) 
     |([A-za-z]{10} {6}) 
     |([A-za-z]{11} {5}) 
     |([A-za-z]{12} ) 
     |([A-za-z]{13} ) 
     |([A-za-z]{14} ) 
     |([A-za-z]{15}) 
     |([A-za-z]{16})) 
     \d{8}$ 

Vous pourriez le faire en assurant qu'il passe deux séparés REs:

^\d{3}[A-za-z]{8,} *\d{8}$ 
^.{27}$ 

mais, Puisque ce dernier est simplement un contrôle de longueur, il n'est pas différent du isValidLine() ci-dessus.

+0

En effet. La première chose à laquelle vous pensez en face d'un problème est «Je sais, j'utiliserai des expressions régulières». Maintenant, vous avez deux problèmes. –

+0

+0

Je raisonne que je l'ai demandé, c'est qu'il semble 'être résoluble par une simple regex . Faites en sorte que ce ne soit pas si simple. Je suis d'accord pour une approche plus simple au lieu d'une longue regex. – jackysee

0

En supposant que vous voulez dire perl regex et si vous permettez '_' dans le nom d'utilisateur:

 
perl -ne 'exit 1 unless /(\d{3})(\w{8,16})\s+(\d{8})/ && length == 28' 
0

Hmm ... Selon la version exacte de Regex que vous utilisez, considère:

(?P<id>\d{3})(?=[A-Za-z\s]{16}\d)(?P<username>[A-Za-z]{8,16})\s*(?P<phone>\d{8}) 

note 100% que cela fonctionne, et je l'ai utilisé l'évasion des espaces caractère au lieu d'une réelle space - Je deviens nerveux avec juste le caractère spatial, mais vous voudrez peut-être être plus restrictif.

Voir si cela fonctionne. Je suis seulement intermédiaire avec RegEx moi-même, donc je pourrais être dans l'erreur.

Vérifiez la syntaxe des groupes nommés pour votre version de RegEx a) existe et b) correspond à la norme que j'ai utilisée ci-dessus.

EDIT:

Juste pour développer ce que je suis en train de faire (désolé de faire vos yeux saignent, Pax!) Pour ceux sans beaucoup d'expérience RegEx:

(?P<id>\d{3}) 

Cela va essayer pour correspondre à un groupe de capture nommé - 'id' - c'est trois chiffres de longueur. La plupart des versions de RegEx vous permettent d'utiliser des groupes de capture nommés pour extraire les valeurs que vous avez comparées. Cela vous permet de faire la validation et la capture de données en même temps. Différentes versions de RegEx ont des syntaxes légèrement différentes pour cela - consultez http://www.regular-expressions.info/named.html pour plus de détails concernant votre implémentation particulière.

(?=[A-Za-z\s]{16}\d) 

Le? = Est un opérateur de recherche anticipée. Cela regarde vers l'avant pour les seize caractères suivants, et retournera vrai s'ils sont tous des lettres ou des caractères d'espace ET sont suivis par un chiffre. L'opérateur lookahead a une longueur nulle, donc il ne retourne rien. Votre chaîne RegEx continue à partir du point que Lookahead a commencé. Consultez http://www.regular-expressions.info/lookaround.html pour plus de détails sur lookahead.

(?P<username>[A-Za-z]{8,16})\s* 

Si le passe préanalyse, nous continuons à compter du quatrième caractère. Nous voulons trouver huit à seize caractères, suivi par un ou plusieurs espaces blancs. Le 'ou plus' est en fait sûr, car nous avons déjà fait en sorte qu'il n'y ait pas plus de seize caractères au total avant le prochain chiffre.

Enfin,

(?P<phone>\d{8}) 

Cela devrait vérifier le numéro de téléphone à huit chiffres. Je suis un peu nerveux que cela ne fonctionne pas exactement - votre version de RegEx peut ne pas supporter la syntaxe de groupe nommé ou la syntaxe lookahead que je suis habitué.

Je suis aussi un peu nerveux que cette Regex réussisse à correspondre à une chaîne vide. Les différentes versions de Regex gèrent différemment les chaînes vides.

Vous pouvez également envisager d'ancrer cette Regex entre un^et un $ pour vous assurer que vous correspondez à toute la ligne, et pas seulement à une partie d'une ligne plus grande.

+0

Et maintenant, grâce à vous, mes yeux saignent :-) – paxdiablo

+0

Prenant la réponse de providermr ci-dessous, vous pouvez aussi essayer (? = [A-Za-z \ s] {17}) (\ d {3}) ([ A-Za-z] {3,16} {0,13}) (\ d {8}) - recherchez l'opérateur? = (Lookahead) RegEx à http://www.regular-expressions.info/lookaround. html –

+0

Heh. Ninja est moi comme je commentais. Oui, RegEx est douloureux, mais vous pouvez faire des choses cool avec ça si vous persévérez. –

0

J'utiliser la regex vous Suggéré avec un petit ajout:

(\d{3})([A-Za-z]{3,16} {0,13})(\d{8}) 

qui correspondent à des choses qui ont un nom d'utilisateur non-blancs, mais encore permettent Rembourrage l'espace.Le seul ajout est que vous devrez alors vérifier la longueur de chaque entrée pour vérifier le bon nombre de caractères.

0

@ OP, tous les problèmes ne nécessitent pas une regex. votre problème est assez simple à vérifier. selon la langue que vous utilisez, ils auraient une sorte de fonctions de chaîne intégrées. Utilise les. l'exemple minimal suivant est fait en Python.

import sys 
for line in open("file"): 
    line=line.strip() 
    # check first 3 char for digit 
    if not line[0:3].isdigit(): sys.exit() 
    # check length of username. 
    if len(line[3:18]) <8 or len(line[3:18]) > 16: sys.exit() 
    # check phone number length and whether they are digits. 
    if len(line[19:26]) == 8 and not line[19:26].isdigit(): sys.exit() 
    print line 
0

Je ne pense pas que vous devriez essayer d'emballer toutes les fonctionnalités dans une seule regex. Voici une façon de le faire:

#!/usr/bin/perl 

use strict; 
use warnings; 

while (<DATA>) { 
    chomp; 
    last unless /\S/; 
    my @fields = split; 
    if (
     (my ($id, $name) = $fields[0] =~ /^([0-9]{3})([A-Za-z]{8,16})$/) 
      and (my ($phone) = $fields[1] =~ /^([0-9]{8})$/) 
    ) { 
     print "ID=$id\nNAME=$name\nPHONE=$phone\n"; 
    } 
    else { 
     warn "Invalid line: $_\n"; 
    } 
} 

__DATA__ 
123jackysee  45678887 
456charliewong 32145644 
678sdjkfhsdjhksadkjfhsdjjh 12345678 

Et voici une autre façon:

#!/usr/bin/perl 

use strict; 
use warnings; 

while (<DATA>) { 
    chomp; 
    last unless /\S/; 
    my ($id, $name, $phone) = unpack 'A3A16A8'; 
    if (is_valid_id($id) 
      and is_valid_name($name) 
      and is_valid_phone($phone) 
    ) { 
     print "ID=$id\nNAME=$name\nPHONE=$phone\n"; 
    } 
    else { 
     warn "Invalid line: $_\n"; 
    } 
} 

sub is_valid_id { ($_[0]) = ($_[0] =~ /^([0-9]{3})$/) } 

sub is_valid_name { ($_[0]) = ($_[0] =~ /^([A-Za-z]{8,16})\s*$/) } 

sub is_valid_phone { ($_[0]) = ($_[0] =~ /^([0-9]{8})$/) } 

__DATA__ 
123jackysee  45678887 
456charliewong  32145644 
678sdjkfhsdjhksadkjfhsdjjh 12345678 

Généralisation:

#!/usr/bin/perl 

use strict; 
use warnings; 

my %validators = (
    id => make_validator(qr/^([0-9]{3})$/), 
    name => make_validator(qr/^([A-Za-z]{8,16})\s*$/), 
    phone => make_validator(qr/^([0-9]{8})$/), 
); 

INPUT: 
while (<DATA>) { 
    chomp; 
    last unless /\S/; 
    my %fields; 
    @fields{qw(id name phone)} = unpack 'A3A16A8'; 

    for my $field (keys %fields) { 
     unless ($validators{$field}->($fields{$field})) { 
      warn "Invalid line: $_\n"; 
      next INPUT; 
     } 
    } 

    print "$_ : $fields{$_}\n" for qw(id name phone); 
} 

sub make_validator { 
    my ($re) = @_; 
    return sub { ($_[0]) = ($_[0] =~ $re) }; 
} 

__DATA__ 
123jackysee  45678887 
456charliewong  32145644 
678sdjkfhsdjhksadkjfhsdjjh 12345678 
0

Vous pouvez utiliser préanalyse: ^(\d{3})((?=[a-zA-Z]{8,})([a-zA-Z ]{16}))(\d{8})$

Test:

 
    123jackysee  45678887  Match 
    456charliewong  32145644  Match 
    789jop    12345678  No Match - username too short 
    999abcdefghijabcde12345678  No Match - username 'column' is less that 16 characters 
    999abcdefghijabcdef12345678  Match 
    999abcdefghijabcdefg12345678  No Match - username column more that 16 characters 
Questions connexes