2015-11-17 22 views
8

Mam TextField w javaFX, gdzie kolor tła zmienia się odpowiednio, jeśli treść jest ważna, czy nie.Regex sprawdzania ciągi csv

Ważny:

987654321 1 
987654321 21 
0101 9 1 
1701 91 1 2 
4101 917 1 0 43 
0801 9 178 2 0 
0111 9 1 084 0 

Nieprawidłowy:

0101 9 1 0 1 0 
3124 
0314 9 

Zasadniczo:

  • Tylko cyfry
  • pierwszej grupie 4 lub 9 cyfr
  • W przypadku pierwszej grupy 9 cyfr - > jedynie dwie grupy w całości
  • Jeśli pierwsza grupa 4 cyfry -> trzy, cztery lub pięć grup w ogóle
  • grupy drugiej i trzeciej cyfry 1-9999
  • Grupa cztery i pięć cyfr 0-9999

Teraz pomyśl jedną z tych (ważnych) linii jako jedną "Tożsamość".

Obecny regex jest:

final String base = "(\\d+\\s+\\d+)|(\\d+\\s+\\d+\\s+\\d+(\\s+\\d+)?(\\s+\\d+)?)|(\\d+\\s+\\d+\\s+\\d+)|(\\d+\\s+\\d+\\s+\\d+\\s+\\d+)|(\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+)"; 

Wich działa świetnie do tej pory, ale teraz chcę to CSV. Mogę więc wpisać tylko jeden identyfikator taki, jakiego użyłem, lub wiele identyfikatorów oddzielonych przecinkiem (,), ale nie więcej niż pięć identyfikatorów łącznie.

Moja próba:

final String pattern = String.format("(%s,?\\s*){1,5}",base); 

To pozwala mi na wejście tego:

  • Wszystkie ważne linie powyżej
  • 0101 9 1, 0101 9 2, 0101 9 3
  • 0101 9 1, 987654321 21, 0101 9 3, 0101 9 4

A jeśli wprowadzę więcej niż 5 identyfikatorów, okaże się on nieprawidłowy. Ale jeśli wprowadzę niepoprawny identyfikator 0101 9 1 1 1 1 1 1 1 1 1, to nadal będzie on ważny.

Wszelkie sugestie? :)

EDIT: To jest logika dopasowanie:

private final Predicate<String> typingPredicate = new Predicate<String>() { 
     @Override 
     public boolean apply(String input) { 
      return input.matches(pattern); 
     } 
    }; 
textField.textProperty().addListener(new ChangeListener<String>() { 
      @Override 
      public void changed(ObservableValue<? extends String> observableValue, String previous, String current) { 
       if (current != null) { 
        if (StringUtils.isEmpty(current) || typingPredicate.apply(current.trim())) { 
         textField.getStyleClass().removeAll("invalid"); 
        } else { 
         textField.getStyleClass().add("invalid"); 
        } 
       } 
      } 
     }); 
+0

Używam com.google.common.base.Predicate dopasowywania. Updatet moje pytanie z pasującą logiką. –

+0

Dlaczego nie: (i) podzielić tekst na przecinki (ii) sprawdzić, czy każdy identyfikator jest ważny z oryginalnym wyrażeniem regularnym, a liczba identyfikatorów jest mniejsza niż 6? – assylias

+0

Sry, dwie ostatnie grupy mogą mieć 0, post aktualizacji. –

Odpowiedz

3

Przecinek w regexp jest opcjonalnie fakt ten pozwala "0101 9 1 1 1 1 1 1 1 1 1", aby być dowolnie przetwarzane jako dwa lub więcej rekordów.

Aby rozwiązać ten problem, można domagać się, że jest albo dokładnie jeden identyfikacyjny lub kilka z nich oddzielone przecinkami:

final String pattern = String.format("(%s\\s*,\\s*){0,4}%s",base,base); 

Również polecam aby baza sama bardziej rygorystyczne w odniesieniu do reguł wejściowych , choć wydaje się, że nie ma to bezpośredniego związku z tą kwestią.

+0

Dziękuję, to było wszystko, czego potrzebowałem! :) –

1

Here's rozwiązanie problemu. Zmodyfikowałem trochę wyrażenie regularne. Twój wzorzec również w jakiś sposób sprawił, że ostatni z nieważnych zdań był ważny, przynajmniej dla mnie. Podstawowym problemem, który napotykasz, jest to, że twoje regex nie jest otoczone nawiasami. Więc dodajesz tylko ,?\\s do ostatniej instrukcji, a nie do kompletnej instrukcji regex.

Oto zmodyfikowane rozwiązanie, które wymyśliło wszystko, co powinno.

public static void main(String[] args) { 
    String[] inputs = {"987654321 1", 
         "987654321 21", 
         "0101 9 1", 
         "1701 91 1 2", 
         "4101 917 1 0 43", 
         "0801 9 178 2 0", 
         "0111 9 1 084 0", 
         "0101 9 1 0 1 0", 
         "3124", 
         "0314 9"}; 
    String regex = "(((\\d{9}(\\s\\d*)))|(\\d{4}(\\s[1-9]\\d{0,3}){2}(\\s\\d{1,4}){0,2}))"; 
    String csvRegex = "("+ regex + ",\\s){0,4}"+regex; 
    for(String s : inputs) { 
     Matcher m = Pattern.compile(csvRegex).matcher(s); 
     System.out.println(m.matches()); 
    } 

    String falseCSVString = "987654321 1, 987654321 21, 1701 91 1 2, 0111 9 1 084 0, 0101 9 1 1 1 1 1 1 1 1 1"; 
    Matcher m = Pattern.compile(csvRegex).matcher(falseCSVString); 
    System.out.println(m.matches()); 

    String rightCSVString = "987654321 1, 987654321 21, 1701 91 1 2, 0111 9 1 084 0, 0101 9 1"; 
    m = Pattern.compile(csvRegex).matcher(rightCSVString); 
    System.out.println(m.matches()); 
} 
+0

Zauważyłem, że twój drugi numer w przypadku, gdy pierwsza cyfra składa się z 9 cyfr, nie jest zgodny z regułą "cyfry 1-9999", nie jestem pewien, czy ma ona zastosowanie tylko do liczb po pierwszym numerze złożonym z 4 cyfr . – Aaron

+0

@Aaron mnie nie, ale myślałem, że tak będzie. Sądzę, że OP będzie coś mówił. – SomeJavaGuy

1

Wierzę, że to wyrażenie regularne odpowiedzieć na wszystkie Twoje wymagania:

^\d{9} [1-9]\d{0,3}$|^\d{4}(?: [1-9]\d{0,3}){2}(?: \d{1,4}){0,2}$ 

Można go here spróbować.

+0

Przegrywa dla '987654321 11111111111' ponieważ twoje' $ 'pasuje tylko do drugiego przypadku (grupa 4 cyfr) – Cyrbil

+0

@Cyrbil dzięki, naprawiłem to. – Aaron

1

Złammy rzeczy w dół:

  • tylko cyfry:

    regex będą musiały dopasować cyfry i spacje i korzystające ^$ dopasować tylko że

  • pierwszej grupie 4 lub 9 cyfr :

    Straigh do przodu: \d{4}|\d{9}

  • Jeśli pierwsza grupa 9 cyfr -> tylko dwie grupy w całości

    \d{9}\s\d cyfry grupy 9 i drugi

  • przypadku pierwszej grupy 4 cyfry -> trzy, cztery lub pięć grup łącznie

    \d{4}(\s\d){2,4} grupa 4 cyfry i od 2 do 4 grup

  • grupy dwa i trzy cyfry 1-9999

    1-9999 ->[1-9]\d{0,3}

  • Grupa cztery i pięć cyfr 0-9999

    Łatwy jeden ...\d{1,4}

Następnie łącząc wszystko:

^ # match start of string 
    (\d{4} # group start with 4 digits 
    (\s[1-9]\d{0,3}){2} # group of 1-9999 twice 
    (\s\d{1,4}){0,2} # group of 0-9999 zero to two times 
)|(\d{9} # group start with 9 digits 
    \s[1-9]\d{0,3} # group of 1-9999 
)$ # end of string match 

Co daje:

^((\d{4}(\s[1-9]\d{0,3}){2}(\s\d{1,4}){0,2})|(\d{9}\s[1-9]\d{0,3}))$ 

Można spróbować live here

1

Spróbuj

String ident = "\\s*(([0-9]{9}\\s+[1-9][0-9]{0,3})|(\\d{4}(\\s+[1-9]\\d{0,3}){2}(\\s+\\d{1,4}){2}))\\s*"; 

    String regex = String.format("\\A%s(,%s){0,4}\\z", ident, ident); 

SSCCE:

import java.util.regex.Pattern; 

import javafx.application.Application; 
import javafx.beans.binding.Bindings; 
import javafx.beans.binding.BooleanBinding; 
import javafx.scene.Scene; 
import javafx.scene.control.TextField; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 

public class ValidatingTextFieldExample extends Application { 

    private BooleanBinding valid ; 

    @Override 
    public void start(Stage primaryStage) { 
     String ident = "\\s*(([0-9]{9}\\s+[1-9][0-9]{0,3})|(\\d{4}(\\s+[1-9]\\d{0,3}){2}(\\s+\\d{1,4}){2}))\\s*"; 

     String regex = String.format("\\A%s(,%s)*\\z", ident, ident); 

     Pattern pattern = Pattern.compile(regex); 

     TextField textField = new TextField(); 
     String INVALID_STYLE = "-fx-background-color: red;" ; 
     textField.setStyle(INVALID_STYLE); 

     valid = Bindings.createBooleanBinding(() -> 
      pattern.matcher(textField.getText()).matches(), 
      textField.textProperty()); 

     valid.addListener((obs, wasValid, isValid) -> { 
      if (isValid) { 
       textField.setStyle(""); 
      } else { 
       textField.setStyle(INVALID_STYLE); 
      } 
     }); 

     StackPane root = new StackPane(textField); 
     primaryStage.setScene(new Scene(root, 350, 120)); 
     primaryStage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
}