/ / Regex, aby sprawdzić pole ustalonej długości ze spakowaną spacją - wyrażenie regularne

Regex, aby sprawdzić pole długości poprawki z spakowaną przestrzenią - wyrażenie regularne

Powiedzmy, że mam plik tekstowy do przeanalizowania, który zawiera pewną treść o stałej długości:

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

Pierwsze trzy znaki to ID, następnie 16-znakowa nazwa użytkownika, a następnie 8-cyfrowy numer telefonu.

Chciałbym napisać wyrażenie regularne, aby dopasować i zweryfikować dane wejściowe dla każdego wiersza, tego, który wymyślę:

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

Nazwa użytkownika powinna zawierać 8-16 znaków. Ale ([A-Za-z ]{16}) dopasowałaby również wartość null lub spację. Myślę o ([A-Za-z]{8,16} {0,8}) ale wykryłby więcej niż 16 znaków. Jakieś sugestie?

Odpowiedzi:

7 dla odpowiedzi № 1

Nie nie nie nie! :-)

Dlaczego ludzie upierają się przy próbie spakowania tak wielu funkcji w jedną instrukcję RE lub SQL?

Moja sugestia, zrób coś takiego:

  • Upewnij się, że długość wynosi 27.
  • Wyodrębnij trzy składniki do oddzielnych ciągów (0-2, 3-18, 19-26).
  • Sprawdź, czy pierwsze pasują "d{3}".
  • Sprawdź, czy druga pasuje "[A-Za-z]{8,} *".
  • Sprawdź, czy trzeci pasuje "d{8}".

Jeśli chcesz, aby cały czek zmieścił się w jednej linii kodu źródłowego, umieść go w funkcji, isValidLine()i nazwij to.

Nawet coś takiego załatwiłoby sprawę:

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

Nie daj się zwieść myśleniu, że to czysty Pythonkod, to właściwie PaxLang, mój własny zastrzeżony pseudokod. Miejmy nadzieję, że jest to wystarczająco jasne, pierwsza linia sprawdza, czy długość wynosi 27, a druga, czy pasuje do podanego RE.

Środkowe pole ma automatycznie 16 znakówłącznie ze względu na pierwszą linię i fakt, że pozostałe dwa pola mają stałą długość w RE. RE zapewnia również, że jest to osiem lub więcej alf, po których następuje odpowiednia liczba spacji.

Zrobienie tego rodzaju rzeczy z pojedynczym RE byłoby potwornością, taką jak:

^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}$

Możesz to zrobić, upewniając się, że przechodzi przez dwa oddzielny RE:

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

ale ponieważ ta ostatnia jest po prostu sprawdzeniem długości, nie różni się od isValidLine() powyżej.


0 dla odpowiedzi nr 2

Użyłbym wyrażenia regularnego, które zasugerowałeś z małym dodatkiem:

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

który będzie pasował do rzeczy, które mająnazwa użytkownika niebędąca białymi znakami, ale nadal umożliwia dopełnianie spacji. Jedynym dodatkiem jest to, że musiałbyś wtedy sprawdzić długość każdego wejścia, aby zweryfikować poprawną liczbę znaków.


0 dla odpowiedzi № 3

Hmm ... W zależności od używanej dokładnej wersji Regex, rozważ:

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

Zwróć uwagę na 100% pewności, że to zadziała, a użyłem znaku ucieczki białych znaków zamiast rzeczywistej spacji - sam denerwuję się samym znakiem spacji, ale możesz chcieć być bardziej restrykcyjny.

Sprawdź, czy to działa. Sam jestem tylko średniozaawansowanym z RegEx, więc mogę się mylić.

Sprawdź składnię nazwanych grup dla swojej wersji wyrażenia regularnego a) istnieje i b) odpowiada standardowi, którego użyłem powyżej.

EDYTOWAĆ:

Aby rozszerzyć to, co próbuję zrobić (przepraszam, że krwawią ci oczy, Pax!) Dla tych, którzy nie mają dużego doświadczenia w RegEx:

(?P<id>d{3})

To spróbuje dopasować nazwaną grupę przechwytywania -„id” - czyli trzy cyfry długości. Większość wersji wyrażenia regularnego umożliwia użycie nazwanych grup przechwytywania w celu wyodrębnienia dopasowanych wartości. Umożliwia to sprawdzanie poprawności i przechwytywanie danych w tym samym czasie. Różne wersje RegEx mają do tego nieco inną składnię - sprawdź http://www.regular-expressions.info/named.html aby uzyskać więcej informacji na temat konkretnej implementacji.

(?=[A-Za-zs]{16}d)

? = Jest operatorem lookahead.Spowoduje to wyszukanie następnych szesnastu znaków i zwróci wartość true, jeśli wszystkie są literami lub białymi znakami ORAZ następują po nich cyfry. Operator lookahead ma zerową długość, więc w rzeczywistości niczego nie zwraca. Ciąg znaków wyrażenia regularnego jest kontynuowany od momentu rozpoczęcia wyszukiwania w przód. Sprawdź http://www.regular-expressions.info/lookaround.html aby uzyskać więcej informacji na temat wybiegania w przód.

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

Jeśli spojrzenie w przód minie, liczymy dalejod czwartego znaku w. Chcemy znaleźć od ośmiu do szesnastu znaków, po których następuje zero lub więcej białych znaków. Znak „lub więcej” jest w rzeczywistości bezpieczny, ponieważ już wcześniej upewniliśmy się, że przed następną cyfrą nie może być więcej niż szesnaście znaków.

Wreszcie,

(?P<phone>d{8})

Powinno to sprawdzić ośmiocyfrowy numer telefonu.

Trochę się denerwuję, że to nie zadziała - Twoja wersja wyrażenia regularnego może nie obsługiwać składni nazwanej grupy lub antycypacji, do której byłem przyzwyczajony.

Jestem też trochę zdenerwowany, że ten Regex z powodzeniem dopasuje pusty ciąg. Różne wersje Regex inaczej traktują puste ciągi.

Możesz również rozważyć zakotwiczenie tego wyrażenia regularnego między ^ a $, aby upewnić się, że dopasowujesz się do całej linii, a nie tylko do części większej linii.


0 dla odpowiedzi nr 4

Zakładając, że masz na myśli wyrażenie regularne perl i jeśli zezwolisz na „_” w nazwie użytkownika:

perl -ne "wyjście 1, chyba że / (d {3}) (w {8,16}) s + (d {8}) / && length == 28"

0 dla odpowiedzi № 5

@ OP, nie każdy problem wymaga wyrażenia regularnego.Twój problem jest dość prosty do sprawdzenia. w zależności od używanego języka miałyby wbudowane funkcje tekstowe. Użyj ich. poniższy minimalny przykład jest wykonany w języku 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 dla odpowiedzi № 6

Uważam też, że nie należy próbować spakować wszystkich funkcji do jednego wyrażenia regularnego. Oto jeden sposób, aby to zrobić:

#!/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=$idnNAME=$namenPHONE=$phonen";
}
else {
warn "Invalid line: $_n";
}
}

__DATA__
123jackysee       45678887
456charliewong    32145644
678sdjkfhsdjhksadkjfhsdjjh 12345678

A oto inny sposób:

#!/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=$idnNAME=$namenPHONE=$phonen";
}
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

Uogólniając:

#!/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 dla odpowiedzi № 7

Możesz użyć lookahead: ^(d{3})((?=[a-zA-Z]{8,})([a-zA-Z ]{16}))(d{8})$

Testowanie:

123jackysee 45678887Mecz 456charliewong 32145644 Dopasuj 789jop 12345678 Brak dopasowania - nazwa użytkownika jest za krótka 999abcdefghijabcde12345678 Brak dopasowania - „kolumna” nazwy użytkownika ma mniej niż 16 znaków 999abcdefghijabcdef12345678 Dopasuj 999abcdefghijabcdefg12345678 Brak dopasowania - kolumna nazwy użytkownika dłuższa niż 16 znaków