2015-08-29 23 views
6

Mam fragment tekstu:Regex: zamów ignorując grup

randomtext 1150,25 USD randomtext

i prostego wyrażenia regularnego wyodrębnić kwotę pieniędzy w różnych walutach:

(((\d+)(,?\s?|.)(\d{1,2}))\s?(PLN|EUR|USD|CHF|GBP))

Który daje mi te grupy:

  1. 1150,25 USD
  2. 1150,25
  3. ,
  4. USD

Jednak liczba i walutą mogą się zamienić ich pozycji:

losowy tekst 1150,25 USD randomtex t

lub

randomtext USD1150,25 randomtext

Jak należy poprawić mój regex spełniają ten warunek bez powtarzania całych grup (AB | BA) utrzymując aktualny zgrupowanie?

+0

użyj nazwanej grupy –

+0

Niestety nie można tego zrobić z nazwanymi grupami, myliłem się. Aby użyć nazwanych grup jako referencji, grupa musi wcześniej istnieć. Zobacz ten https://regex101.com/r/qI9xV1/1 –

+0

Jakiego języka używasz? –

Odpowiedz

4

Można korzystać z tego rodzaju wzoru:

String p = "\\b (?=[\\dPEUCG]) # to jump quickly at interesting positions  \n" + 
      "(?=  # open a lookahead           \n" + 
      " (?> [\\d,]+ \\s*)? # perhaps the value is before    \n" + 
      " (?<currency> PLN|EUR|USD|CHF|GBP) # capture the currency  \n" + 
      " (?:\\b|\\d) # a word boundary or a digit       \n" + 
      ")  # close the lookahead          \n" + 
      "(?> [PLNEURSDCHFGB]{3} \\s*)? (?<value> \\d+(?:,\\d+)?)    "; 

Pattern RegComp = Pattern.compile(p, Pattern.COMMENTS); 

String s = "USD 1150,25 randomtext \n" + 
      "Non works randomtext 1150,25 USD randomtext\n" + 
      "Works randomtextUSD 1150,25 USD randomtext\n" + 
      "Works randomtext USD 1150,25 randomtext\n" + 
      "Works randomtext USD1150,25 randomtext\n" + 
      "Non work randomtext 1150,25 USD randomtext"; 

Matcher m = RegComp.matcher(s); 

while(m.find()) { 
    System.out.println(m.group("value") + " : " + m.group("currency")); 
} 

Chodzi o to, aby uchwycić waluty w uprzedzona (czyli zero-width twierdzenie). Przegrana jest tylko potwierdzeniem i nie pochłania znaków, a podtytuł wewnątrz opisuje wcześniejszą wartość. Tak więc pozycja waluty niczego nie zmienia. Wartość jest przechwytywana poza wyprzedzeniem.

O \\b (?=[\\dPEUCG]): Celem tego podciąg wzorca jest filtrowanie pozycji w łańcuchu, które nie są na początku wyrazu, który zaczyna się od cyfry lub jednego z pierwszych liter innej walucie bez przetestować cały wzór.

+3

W tym momencie, dla zachowania zdrowia, należy po prostu użyć wielu wyrażeń regularnych lub odejść od nich całkowicie. –

0

To nie jest zbyt eleganckie, ale można również osiągnąć to:

(?<!\d|\d,)(?=(?:[\d,]+\s)*(PLN|EUR|USD|CHF|GBP)(?:\s*[\d,]+\s)*)(?=(?:PLN|EUR|USD|CHF|GBP)*\s*((\d+)(,?\s?|.)(\d{1,2}))\s?(?:PLN|EUR|USD|CHF|GBP)*)[\d,\sPLNEURUSDCHFGB]+(?=\b\s) 

DEMO

Jednak jeśli nie trzeba dopasować tę część (i na przykład zastąpić itp), tylko uchwycić istotne części łańcucha, to powinno wystarczyć, aby użyć:

(?<!\d|\d,)(?=(?:[\d,]+\s)*(PLN|EUR|USD|CHF|GBP)(?:\s*[\d,]+\s)*)(?=(?:PLN|EUR|USD|CHF|GBP)*\s*((\d+)(,?\s?|.)(\d{1,2}))\s?(?:PLN|EUR|USD|CHF|GBP)*) 

DEMO

on użyć do poistive uprzedzona:

  • (?=(?:[\d,]+\s)*(PLN|EUR|USD|CHF|GBP)(?:\s*[\d,]+\s)*) - waluta poprzedza lub następuje numer,
  • (?=((?:PLN|EUR|USD|CHF|GBP)*\s*(\d+)(,?\s?|.)(\d{1,2})(?:\s*(?:PLN|EUR|USD|CHF|GBP))*))
  • numer przed lub za walutę

Example in Java

0

skonstruowania regexp programowo:

BigDecimal amount = null; 
String currency = null; 
String currencyRegex = "(PLN|EUR|USD|CHF|GBP)"; 
String amountRegex = "(\\d+)(?:,?\\s?|.)(\\d{1,2})"; 
Pattern currencyAmountPattern = Pattern.compile(
    currencyRegex + "\\s?" + amountRegex 
    + "|" 
    + amountRegex + "\\s?" + currencyRegex); 
Matcher matcher = currencyAmountPattern.matcher(input); 
if (matcher.find()) { 
    if (matcher.group(1) != null) { 
     currency = matcher.group(1); 
     amount = new BigDecimal(matcher.group(2) + "." + matcher.group(3)); 
    } else { 
     currency = matcher.group(6); 
     amount = new BigDecimal(matcher.group(4) + "." + matcher.group(5)); 
    } 
}