I have a java binary search tree and I want to create a menu.
To this day I used StreamTokenizer to get the user input,
But now it doesn't seem to work with "+", "-", "?".
My code:
public void listen() throws IOException {
boolean stay = true;
System.out.println("Give me commands .. ");
while(stay) {
tokens.nextToken();
if(tokens.sval.equals("+")) {
tree.insert(new PositiveInt((int) tokens.nval));
} else if(tokens.sval.equals("?")) {
System.out.println(
tree.retrieve(new PositiveInt((int) tokens.nval)) == null ? "Not exist" : "exist");
} else if(tokens.sval.equals("-")) {
tree.remove(new PositiveInt((int) tokens.nval));
} else if(tokens.sval.equalsIgnoreCase("K")) {
tree.writeKeys();
} else if(tokens.sval.equalsIgnoreCase("E")) {
System.out.println("Empty = " + tree.isEmpty());
} else if(tokens.sval.equalsIgnoreCase("F")) {
System.out.println("Full = " + tree.isFull());
} else if(tokens.sval.equalsIgnoreCase("C")) {
tree.clear();
} else if(tokens.sval.equalsIgnoreCase("P")) {
tree.showStructure();
} else if(tokens.sval.equalsIgnoreCase("Q")) {
stay = false;
} else {
System.out.println("Unaccaptable input.");
}
}
}
When I enter "P" , for example, or any other character, everything's alright.
When I enter "?", "+", "-", I'm getting:
Exception in thread "main" java.lang.NullPointerException
at TestBSTree.listen(TestBSTree.java:27)
at TestBSTree.main(TestBSTree.java:54)
As Line 27 is :
if(tokens.sval.equals("+")) {
In other words, a non-charater is not accaptable with the tokenizer.
Why and how can I fix it?
Whole code:
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class TestBSTree {
// Test class variables
BSTree<PositiveInt> tree;
InputStreamReader reader;
StreamTokenizer tokens;
PositiveInt key;
int in;
public TestBSTree(PositiveInt root) {
tree = new BSTree<PositiveInt>(new BSTreeNode<>(root, null, null));
reader = new InputStreamReader(System.in);
tokens = new StreamTokenizer(reader);
key = null;
}
public void listen() throws IOException {
boolean stay = true;
System.out.println("Give me commands .. ");
while(stay) {
tokens.nextToken();
if(tokens.sval.equals("+")) {
tree.insert(new PositiveInt((int) tokens.nval));
} else if(tokens.sval.equals("?")) {
System.out.println(
tree.retrieve(new PositiveInt((int) tokens.nval)) == null ? "Not exist" : "exist");
} else if(tokens.sval.equals("-")) {
tree.remove(new PositiveInt((int) tokens.nval));
} else if(tokens.sval.equalsIgnoreCase("K")) {
tree.writeKeys();
} else if(tokens.sval.equalsIgnoreCase("E")) {
System.out.println("Empty = " + tree.isEmpty());
} else if(tokens.sval.equalsIgnoreCase("F")) {
System.out.println("Full = " + tree.isFull());
} else if(tokens.sval.equalsIgnoreCase("C")) {
tree.clear();
} else if(tokens.sval.equalsIgnoreCase("P")) {
tree.showStructure();
} else if(tokens.sval.equalsIgnoreCase("Q")) {
stay = false;
} else {
System.out.println("Unaccaptable input.");
}
}
}
public static void main(String[] args) throws IOException {
TestBSTree test = new TestBSTree(new PositiveInt(0));
test.listen();
}
}
It doesn't matter how does the tree or PositiveInt implemented, the main issue is the tokenizer.
if you want so split a string containg a '?' or a plus ('+'), you cannot simply use this symbol to split this String; they are reserved 'words' and need a exclude sign '\' which itself needs an exclusion sign ^^ (so you need two '\\' and the special sign)
try to use something like that:
StringTokenizer tokenizer = new StringTokenizer("ghj?klm", "\\?");
System.out.println(tokenizer.countTokens() );
-> result: the count is 2 !
you can also apply this method for String.split("\+");
//wont work!!
String str = "ghj?klm";
String[] s = str.split("?");
System.out.println(s.length );
but this code will work!
String str = "ghj?klm";
String[] s = str.split("\\?");
System.out.println(s.length );
it's the same 'problem' ^^ i hope this helped!
unfortunaltely i don't know which other symbols require a slahs... :-(
Related
I am trying to capture allowed and disallowed rules of robots.txt file in java using following code:-
package robotest;
public class RoboTest {
public static void main(String[] args) {
String robo="user-agent:hello user-agent:ppx user-agent:bot allow:/world disallow:/ajax disallow:/posts user-agent:abc allow:/myposts/like disallow:/none user-agent:* allow:/world";
String[] strarr=robo.split(" ");
String[] allowed={};
String[] disallowed={};
boolean new_block=false;
boolean a_or_d=false;
for (String line: strarr){
if(line!=""){
if(line.contains("user-agent:pp")==false && a_or_d){
break;
}
if (line.contains("user-agent:ppx")||(new_block )){
new_block=true;
System.out.println(line);
if(line.contains("allow") || line.contains("disallow")){
a_or_d=true;
}
if(line.contains("allow:")){
//append to allowed
}
if(line.contains("disallowed")) {
//append to disallowed
}
}
}
System.out.println(allowed);;
}
}
}
The code does not works properly as I expect. The rules of robots.txt string is separated by white space. I want to capture rules of user-agent ppx. The code should look for allow or disallow block after discovering user-agent:ppx and append them to list. But it is not working and is confusing too. I am also new to regex in java. What can be solution for this.
Some minimum modifications to your code:
String robo = "user-agent:hello user-agent:ppx user-agent:bot allow:/world disallow:/ajax disallow:/posts user-agent:abc allow:/myposts/like disallow:/none user-agent:* allow:/world";
String[] strarr = robo.split(" ");
Set<String> allowed = new HashSet<>();
Set<String> disallowed = new HashSet<>();
Pattern allowPattern = Pattern.compile("^allow:\\s*(.*)");
Pattern disallowPattern = Pattern.compile("^disallow:\\s*(.*)");
boolean isUserAgentPpx = false;
boolean a_or_d = false;
for (String line : strarr) {
line = line.trim();
// Skip empty lines
if (line.isEmpty()) continue;
if (line.startsWith("user-agent:")) {
// If previous lines were allowed/disallowed rules, then start a new user-agent block
if (a_or_d) {
a_or_d = false;
isUserAgentPpx = false;
}
// Skip block of user-agent if we already found 'user-agent: ppx' or 'user-agent: *'
if (isUserAgentPpx) continue;
if (line.matches("^user-agent:\\s*(ppx|\\*)$")) {
isUserAgentPpx = true;
}
continue;
}
// Process block of allow/disallow
a_or_d = true;
if (isUserAgentPpx) {
Matcher allowMatcher = allowPattern.matcher(line);
if (allowMatcher.find()) {
allowed.add(allowMatcher.group(1));
}
Matcher disallowMatcher = disallowPattern.matcher(line);
if (disallowMatcher.find()) {
disallowed.add(disallowMatcher.group(1));
}
}
}
System.out.println("Allowed rules for Ppx:");
for (String s : allowed) {
System.out.println(s);
}
System.out.println("Disallowed rules for Ppx:");
for (String s : disallowed) {
System.out.println(s);
}
I'm using Set<String> to store the rules to avoid duplicates.
I made it little easy. Beware of edge conditions though
public class RoboTest {
public void test() {
String robo = "user-agent:hello user-agent:ppx allow:/aellow disallow:/deasllow disallow:/posts user-agent:bot allow:/world disallow:/ajax disallow:/posts user-agent:abc allow:/myposts/like disallow:/none user-agent:* allow:/world";
String[] strarr = robo.split(" ");
List<String> allowed = new ArrayList<>();
List<String> disAllowed = new ArrayList<>();
boolean checkAllowed = false;
for (String line : strarr) {
if (line.contains("user-agent:ppx")) {
checkAllowed = true;
continue;
} else if (checkAllowed) {
if (line.contains("disallow:")) {
disAllowed.add(line.split(":")[1]);
continue;
}
if (line.contains("allow:")) {
allowed.add(line.split(":")[1]);
continue;
}
checkAllowed = false;
}
}
System.out.println("Allowed" + allowed);
System.out.println("DisAllowed" + disAllowed);
}
}
I need to create a parent child relationship for the following string:
((OPERATING_CARRIER='AB' OR OPERATING_CARRIER='EY' OR (OPERATING_CARRIER='VA' AND (FLIGHT_NO=604 OR FLIGHT_NO=603))))
I have to insert them into a database table as following
ID PARENT_ID ENTITY OPERATOR VALUE
1 OPERATING_CARRIER = AB
2 OPERATING_CARRIER = EY
3 OPERATING_CARRIER = VA
4 3 FLIGHT_NO = 604
5 3 FLIGHT_NO = 603
using the following code
package whereclause;
import java.util.Iterator;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class QueryMatcher {
public static void main(String[] args) {
// TODO Auto-generated method stub
String sa="((OPERATING_CARRIER='AB' OR OPERATING_CARRIER='AB' OR (OPERATING_CARRIER='VA' AND (FLIGHT_NO=604 OR FLIGHT_NO=603))))";
Matcher m = Pattern.compile("\\w+\\s*=\\s*(?:'[^']+'|\\d+)").matcher(sa);
System.out.println("contains "+sa.contains("((("));
Stack<String> in_cond = new Stack<String>();
Iterator<String> iter = in_cond.iterator();
String new_sa=sa;
while(m.find()) {
String aMatch = m.group();
// add aMatch to match list...
System.out.println(aMatch);
in_cond.push(aMatch);
}
System.out.println("string stack is "+in_cond);
int i=0;
for (String new_sa1:in_cond)
{
if(new_sa.contains(in_cond.get(i)))
{
new_sa=new_sa.replace(in_cond.get(i),"&"+i);
System.out.println("String Contains "+in_cond.get(i));
}
i++;
}
System.out.println("new String is "+new_sa);
}
}
i have got to the following output
contains false
OPERATING_CARRIER='AB'
OPERATING_CARRIER='AB'
OPERATING_CARRIER='VA'
FLIGHT_NO=604
FLIGHT_NO=603
string stack is [OPERATING_CARRIER='AB', OPERATING_CARRIER='AB', OPERATING_CARRIER='VA', FLIGHT_NO=604, FLIGHT_NO=603]
String Contains OPERATING_CARRIER='AB'
String Contains OPERATING_CARRIER='VA'
String Contains FLIGHT_NO=604
String Contains FLIGHT_NO=603
new String is ((&0 OR &0 OR (&2 AND (&3 OR &4))))
But now I am clueless on how to proceed, need help.
I have managed to solve it using following code for splitting the string
and to build the parent child relationship:
String input="name = 'name_1' AND in_stock IN {'in_stock_1','in_stock_2'} AND ( price BETWEEN '01-jan-2015' and '31-may-2015' OR price = 'price_3' )";
String sa =input;
String[] arr = sa.replaceAll("[()]+","").split("\\s*(\\sOR|\\sAND)\\s*");
for(int i=0;i<arr.length;i++)
{
System.out.println(arr[i]);
}
String og_st=orig_input;
Stack<String> temp_bool=new Stack<String>();
String[] bool_arr = og_st.split("\\s+");
String[] bool_op=new String[inout.length-1];
for(String bool:bool_arr)
{
if(bool.equals("AND") || bool.equals("OR"))
{
temp_bool.push(bool);
}
else
{
//nothing here
}
}
for (int i=0;i<temp_bool.size();i++)
{
bool_op[i]=temp_bool.get(i);
}
Conditions c=new Conditions();
String[] arr=null;
arr=inout;
//Stack<String> arr2 =new Stack<String>();
String[] atr=null;
if(arr[l].contains(" BETWEEN "))
{
atr=arr[l].split(" BETWEEN ");
c.id=l+1;
c.entity=atr[0];
c.operator=" BETWEEN ";
String c_value=atr[1];
//c_value=c_value.replace("'","");
c.value=c_value;
}
else
{
atr=arr[l].split(" ");
c.id=l+1;
c.entity=atr[0];
c.operator=atr[1];
String c_value=atr[2];
//c_value=c_value.replace("'","");
c.value=c_value;
}
/*for(int k=0;k<arr2.size();k++)
{
if(arr[l].contains(" BETWEEN "))
{
System.out.println("inside if");
atr=arr[l].split(" BETWEEN ");
c.id=l+1;
c.entity=atr[0];
c.operator=" BETWEEN ";
String c_value=atr[1];
c_value=c_value.replace("'","");
c.value=c_value;
System.out.println(c.entity+" "+c.operator+" "+c.value );
}
else
{
System.out.println("inside else");
atr=arr[l].split(" ");
for(int o=0;o<atr.length;o++)
{
arr2.push(atr[o].toString());
}
c.id=l+1;
c.entity=atr[0];
c.operator=atr[1];
String c_value=atr[2];
c_value=c_value.replace("'","");
c.value=c_value;
}
}*/
c.enopva=arr[l];
int c_id=getDecompressedString(arr,orig_input,l);
if (c_id==0)
{
c.parent_id=c_id;
}
else if(c_id>0)
{
c.parent_id=c_id;
}
if(l>=bool_op.length)
{
c.bool_op=null;
}
else if(l<bool_op.length)
{
c.bool_op=bool_op[l].toString();
}
IncentiveProLog.insertLog(" Class has been generated as "+c.toString(),id);
try
{
insertData(c.id,c_id,c.entity,c.operator,c.value,c.bool_op);
}
catch (SQLException e)
{
e.printStackTrace();
}
I am taking an input file with various infix expressions, calculating them, and printing them back to another output file with each line formatted as:
THE MODULO 10 VALUE OF %%%%% IS %
The output text and modulo 10 answer are both correct; however, I cannot get the program to reprint the entire expression in between "OF" and "IS."
I tried putting output.write(token) in the getToken() method, but I got a "cannot find symbol" error. So I understand that I can't access the BufferedWriter from another method since it is declared in main, but how can I get around that?
import java.io.*;
public class Lab1
{
public static char token;
public static String expr;
public static int k = 0;
public static void main (String[] args)
{
int exprValue;
String line;
try
{
BufferedReader input = new BufferedReader(new FileReader("inputfile.txt"));
BufferedWriter output = new BufferedWriter(new FileWriter("outputfile.txt"));
while ((line = input.readLine()) != null)
{
output.write("THE MODULO 10 VALUE OF ");
expr = line;
getToken();
output.write(token);
exprValue = expression();
output.write(" IS " + exprValue);
output.newLine();
output.newLine();
k = 0;
}
input.close();
output.close();
}
catch (IOException ex)
{
System.err.println("Exception:" + ex);
}
}
public static void getToken()
{
k++;
int count = k-1;
if(count < expr.length())
{
token = expr.charAt(count);
}
}
public static int expression()
{
int termValue;
int exprValue;
exprValue = term();
while(token == '+')
{
getToken();
termValue = term();
exprValue = (exprValue + termValue)%10;
}
return exprValue;
}
public static int factor()
{
int factorValue = token;
if(Character.isDigit(token))
{
factorValue = Character.getNumericValue(token);
getToken();
}
else if(token == '(')
{
getToken();
factorValue = expression();
if(token == ')')
{
getToken();
}
}
return factorValue;
}
public static int term()
{
int factorValue;
int termValue;
termValue = factor();
while(token == '*')
{
getToken();
factorValue = factor();
termValue = (termValue * factorValue)%10;
}
return termValue;
}
}
Currently my input is:
(3*6+4)*(4+5*7)
3*((4+5*(1+6)+2))
My output is:
THE MODULO 10 VALUE OF ( IS 8
THE MODULO 10 VALUE OF 3 IS 3
Solved the problem. In the while loop in the main method, replace output.write(token) with output.write(expr)
My project need to parse two type of text data into database.
one format is like this:
<lineNumber>19</lineNumber>
<begin>
2013-08-15,2013-08-15,pek001,123456,08654071,CANX,,,,,,011
<end>
one is like that
<lineNumber>27</lineNumber>
<begin>
2012-11-02,08683683,pek001,00001234,vvip,1
<end>
the difference of the two text is between the begin and end tag.
so our parsing code come out:
first one is:
inputStreamReader = new InputStreamReader(new FileInputStream(FileOne),"gbk"); --different place
br=new BufferedReader(inputStreamReader);
lineNumber = 0;
boolean isDataContent = false;
while (br.ready()) {
String line = br.readLine();
if(line == null){
continue;
}
if(line.contains("<lineNumber>"))
{
try {
lineNumber = Integer.parseInt(StringTools.getDigitalInString(line));
} catch (NumberFormatException e) {
log.error("there is no lineNumber。");
}
continue;
}
if(line.trim().equals("<begin>"))
{
isDataContent = true;
continue;
}
if(line.trim().equals("<end>"))
{
break;
}
if(isDataContent)
{
insertFirstToDatabase(line,vo); --just this is different.
}
}
second one is :
inputStreamReader = new InputStreamReader(new FileInputStream(FileTwo),"gbk");
--different place
br=new BufferedReader(inputStreamReader);
lineNumber = 0;
boolean isDataContent = false;
while (br.ready()) {
String line = br.readLine();
if(line == null){
continue;
}
if(line.contains("<lineNumber>"))
{
try {
lineNumber = Integer.parseInt( StringTools.getDigitalInString(line));
} catch (NumberFormatException e) {
log.error("there is no lineNumber");
}
continue;
}
if(line.trim().equals("<begin>"))
{
isDataContent = true;
continue;
}
if(line.trim().equals("<end>"))
{
break;
}
if(isDataContent)
{
insertSecondToDatabase(line,vo); --only this is different.
}
}
The two piece of code is in two different service code. How can I refactor this reduplicate code? so that each place Just only call one same function to check the lineNumber.
Have the duplicated code in a class that both the other classes either inherit (inheritance) or include a copy of (composition). Alternatively you could even make it a static method in a utility class.
Your code is identical until a single statement, and it's not shown how you determined which of these sequences of code you should be executing, but just move that branching into the if (isDataContent):
// copy/paste from your own, change the if to:
if(isDataContent) {
if (flagFirst) {
insertFirstToDatabase(line,vo); --just this is different.
} else {
insertSecondToDatabase(line,vo); --only this is different.
}
}
Where flagFirst is either a boolean variable or a boolean expression to determine which of the inserts should be done.
You can add 'kind' parameter for selecting usded inserting method as following:
public void process(int kind) {
....
while (br.ready()) {
String line = br.readLine();
if(line == null){
continue;
}
if(line.contains("<lineNumber>"))
{
try {
lineNumber = Integer.parseInt( StringTools.getDigitalInString(line));
} catch (NumberFormatException e) {
log.error("there is no lineNumber");
}
continue;
}
if(line.trim().equals("<begin>"))
{
isDataContent = true;
continue;
}
if(line.trim().equals("<end>"))
{
break;
}
if(isDataContent)
{
if (kind == 1) {
insertFirstToDatabase(line,vo); --just this is different.
}
if (kind == 2) {
insertSecondToDatabase(line,vo); --only this is different.
}
}
}
}
2 things:
duplicated code? - put in static method in utility class
how to differentiate dataContent? -
i. this can be determined while parsing the line depending on the order of fields
(or)
ii. the callee of the static method can determine the same by sending a flag. But this is not good design. You are placing too much implementation i.e. 2 behaviors in a utility method.
(or)
iii. Let the static method parse the XML and return just the line details to the callee. Let the callee handle however it likes. First callee might just want to print, second callee might want to put into db.
So, here it goes,
public static LineDetails parseXML(String filename)
{
inputStreamReader = new InputStreamReader(new FileInputStream(new File(filename));
br=new BufferedReader(inputStreamReader);
lineNumber = 0;
boolean isDataContent = false;
LineDetails lineDetails = new LineDetails();
while (br.ready()) {
String line = br.readLine();
if(line == null){
continue;
}
if(line.contains("<lineNumber>"))
{
try {
lineNumber = Integer.parseInt( StringTools.getDigitalInString(line));
} catch (NumberFormatException e) {
log.error("there is no lineNumber");
}
lineDetails.setLineNumber(lineNumber);
continue;
}
if(line.trim().equals("<begin>"))
{
isDataContent = true;
continue;
}
if(line.trim().equals("<end>"))
{
break;
}
if(isDataContent)
{
// parse line
lineDetails.setLine(line);
}
}
return lineDetails;
}
public class LineDetails
{
private int lineNumber=0;
private String line="";
// getters setters
}
//First callee
methodA()
{
LineDetails lineDetails = parseXML(filename);
if(lineDetails!=null && lineDetails.getLineNumber==19 && lineDetails.getLine()!=null && !lineDetails.getLine.equals(""))
{
insertFirstToDatabase(line);
}
}
//Second callee
methodB()
{
LineDetails lineDetails = parseXML(filename);
if(lineDetails!=null && lineDetails.getLineNumber==27 && lineDetails.getLine()!=null && !lineDetails.getLine.equals(""))
{
insertSecondToDatabase(line);
}
}
I'm using a regular expression to validate a certain format in a string. This string will become a rule for a game.
Example: "DX 3" is OK according to the rule, but "DX 14" could be OK too... I know how to look at the string and find one or more "numbers", so the problem is that the regex will match 34 too, and this number is out of "range" for the rule...
Am I missing something about the regex to do this? Or is this not possible at all?
Unfortunately there's no easy way to define ranges in regex. If you are to use the range 1-23 you'll end up with a regex like this:
([1-9]|1[0-9]|2[0-3])
Explanation:
Either the value is 1-9
or the value starts with 1 and is followed with a 0-9
or the value starts with 2 and is followed with a 0-3
It is not that short, and not flexible.
If you search for 1 to 19, you can search for "DX 1?[0-9]", for example, but if it doesn't end at a number boundary, it get's ugly pretty soon, and changing the rules is not flexible.
Splitting the String at the blank, and then using x > 0 and x < 24 is better to understand and more flexible.
You can use following format for writing a regular expression solving your problem.
Suppose your range is 0-15.
"^DX [0-9]|1[0-5]$"
You can even make it dynamic depending on your range by appending strings.
package dev.dump;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Collections;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by IntelliJ IDEA. User: User Date: 28.09.2007 Time: 9:46:47 To change this template use
* File | Settings | File Templates.
*/
class NumberDiapasone2RegExp {
private static final String invalidArgumentEmpty="Invalid argument \"{0}\" was found! {1}";
private static final Pattern pattern=Pattern.compile("^(\\d+)-(\\d+)?$");
private String src;
private String result="";
private Long left;
private Long right;
private boolean transform09ToD;
public NumberDiapasone2RegExp(final String src) {
this(src, false);
}
public NumberDiapasone2RegExp(final String src, final boolean transform09ToD) {
this.transform09ToD=transform09ToD;
if (src==null || src.trim().length()==0)
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It cannot be empty."));
if (src.indexOf("-")<0)
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It is supposed to have \"-\"."));
if (src.indexOf("-")!=src.lastIndexOf("-"))
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It is supposed to have only one \"-\"."));
Matcher syntaxChecker=pattern.matcher(src);
if (!syntaxChecker.find()){
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"It is supposed to be in format \"##-##\"."));
}
this.src=src;
parseAndCheck();
String theSameDigits="";
//the same digit goes towards result
if (left.toString().length()==right.toString().length()){
for (int i=0; i<left.toString().length(); i++){
if (i<right.toString().length() &&
left.toString().charAt(i)==right.toString().charAt(i)){
theSameDigits+=left.toString().charAt(i);
}
}
if (theSameDigits.length()>0){
this.src=this.src.replaceFirst(Pattern.quote(theSameDigits),"");
this.src=this.src.replaceFirst(Pattern.quote("-"+theSameDigits),"-");
parseAndCheck();
}
}
result=glueParts(compact(transform09ToD, toParts()));
Matcher m=secondCompact.matcher(result);
while (m.find()){
result=m.group(1).replace("(","").replace(")","")+"["+m.group(2).replaceAll("[\\[\\]]","")+m.group(3).replaceAll("[\\[\\]]","")+"][0-9]";
m.reset(result);
}
//compact squares again
StringBuffer sb=new StringBuffer();
Pattern squresP=Pattern.compile("(\\[(\\d|-)+\\])");
m=squresP.matcher(result);
while (m.find()) {
m.appendReplacement(sb, Matcher.quoteReplacement(compactSquares(m.group(1))));
}
m.appendTail(sb);
result=sb.toString();
result=result.replaceAll("\\[(\\d)-\\1\\]","$1");
result=result.replaceAll("\\[(\\d)\\]","$1");
result=result.replace("{1}","").replace("{0,1}","?");
if (result.indexOf("|")>=0) result=theSameDigits+"("+result+")";
else result=theSameDigits+result;
if (result.startsWith("(") && result.endsWith(")")) result=result.substring(1, result.length()-1);
}
private static Pattern secondCompact=Pattern.compile("(.*)(\\[\\d-?\\d\\]|\\d)\\[0-9\\]\\|(\\[\\d-?\\d\\]|\\d)\\[0-9\\]");
static List<String> compact(boolean transform09ToD, String... parts) {
Set<String> unique=new HashSet<String>();
List<String> result=new ArrayList<String>();
for (String part : parts){
if (part==null || part.length()==0) continue;
part=compactSquares(part);
part=part.replaceAll("\\[(\\d)\\]","$1");
if (part.indexOf("[0-9]")>=0){
if (transform09ToD) part=part.replace("[0-9]","\\d");
}
//[0-3][0-9]|4[0-9]=>[0-34][0-9]
//[023][0-9]|4[0-9]=>[0234][0-9]
//[02345789]=>[02-57-9]
Matcher m=secondCompact.matcher(part);
if (m.find()){
part=m.group(1).replace("(","").replace(")","")+"["+m.group(2).replaceAll("[\\[\\]]","")+m.group(3).replaceAll("[\\[\\]]","")+"][0-9]";
}
part=part.replaceAll("\\[(\\d)-\\1\\]","$1");
if (unique.add(part)) result.add(part);
}
return result;
}
static String compactSquares(String src){
boolean inSquares=false;
if (src.startsWith("[") && src.endsWith("]")){
inSquares=true;
src=src.substring(1,src.length()-1);
}
StringBuffer sb=new StringBuffer();
if (!src.contains("-")) {
List<Integer> digits=new ArrayList<Integer>();
for (int i=0; i<src.length();i++){
digits.add(Integer.parseInt(""+src.charAt(i)));
}
Collections.sort(digits);
for (Integer s : digits){
sb.append(s);
}
src=sb.toString();
sb.setLength(0);
}
int firstChar = -2;
int lastChar = -2;
int currentChar;
for (int i=0; i<src.length(); i++) {
currentChar=src.charAt(i);
if (currentChar == lastChar + 1) {
lastChar = currentChar;
continue;
}
if (currentChar == '-' && i+1 < src.length()) {
lastChar = src.charAt(i + 1) - 1;
continue;
}
flush(sb, firstChar, lastChar);
firstChar = currentChar;
lastChar = currentChar;
}
flush(sb, firstChar, lastChar);
return (inSquares?"[":"")+sb.toString()+(inSquares?"]":"");
}
private static void flush(StringBuffer sb, int firstChar, int lastChar) {
if (lastChar<=0) return;
if (firstChar==lastChar) {
sb.append((char)firstChar);
return;
}
if (firstChar+1==lastChar){
sb.append((char)firstChar);
sb.append((char)lastChar);
return;
}
sb.append((char)firstChar);
sb.append('-');
sb.append((char)lastChar);
}
static String glueParts(List<String> parts) {
if (parts==null || parts.isEmpty()) return "";
if (parts.size()==1) return parts.get(0);
StringBuilder result=new StringBuilder(128);
for (String part : parts){
result.append(part);
result.append("|");
}
result.deleteCharAt(result.length()-1);
return result.toString();
}
private String[] toParts() {
List<String> result=new ArrayList<String>();
if (getNumberOfDigits(left)>2 || getNumberOfDigits(right)>2) {
result.add(startPart(left));
}
long leftPart=left;
long rightPart=right;
if (!String.valueOf(left).matches("10*")) leftPart=toPower(left);
if (!String.valueOf(right).matches("10*")) rightPart=toPower(right)/10;
if (rightPart/leftPart>=10) {
result.add(speedUpPart(left, right));
}
//for 1-2 digit process
if (getNumberOfDigits(left)==1 && getNumberOfDigits(right)==1){
result.add("["+left+"-"+right+"]");
}
else if (getNumberOfDigits(left)==1 && getNumberOfDigits(right)==2){
if (0==Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (1==Integer.parseInt(getMajorDigit(right))) {
result.add("["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (2<=Integer.parseInt(getMajorDigit(right))) {
result.add("["+
getMajorDigit(left)+
"-9]");
result.add("[1-"+
(Integer.parseInt(getMajorDigit(right))-1)+
"][0-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else throw new IllegalStateException();
}
else if (getNumberOfDigits(left)==2 && getNumberOfDigits(right)==2){
if (Integer.parseInt(getMajorDigit(left))==Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (Integer.parseInt(getMajorDigit(left))+1==Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else if (Integer.parseInt(getMajorDigit(left))+2<=Integer.parseInt(getMajorDigit(right))) {
result.add(getMajorDigit(left)+
"["+
getMajorDigit(getNumberWithoutMajorDigit(left))+
"-9]");
result.add("["+(Integer.parseInt(getMajorDigit(left))+1)+
"-"+(Integer.parseInt(getMajorDigit(right))-1)+
"][0-9]");
result.add(getMajorDigit(right)+
"[0-"+
getMajorDigit(getNumberWithoutMajorDigit(right))+
"]");
}
else throw new IllegalStateException();
}
else result.add(staticPart(right));
result.add(breakPart(right));
return result.toArray(new String[0]);
}
static String breakPart(final Long number) {
if (getNumberOfDigits(number)<=2) {
return "";
}
StringBuilder result=new StringBuilder(256);
StringBuilder staticSection=new StringBuilder(32);
staticSection.append(getMajorDigit(number));
for (int i=1; i<getNumberOfDigits(number)-1; i++){
if (i!=1) result.append("|");
result.append(staticSection.toString());
staticSection.append(String.valueOf(number).charAt(i));
final long nextDigit=Long.parseLong(""+String.valueOf(number).charAt(i))-1;
if (nextDigit<0) {
result.setLength(0);
result.append("|");
continue;
}
if (nextDigit==0) result.append("0");
else if (nextDigit==1) result.append("[01]");
else result.append("[0-"+(nextDigit)+"]");
final int numberOfRepeats=(getNumberOfDigits(number)-i-1);
if (numberOfRepeats==1) result.append("[0-9]");
else result.append("[0-9]{"+numberOfRepeats+"}");
}
//остаток - 2 последние цифры числа
if (result.length()>0) {
result.append("|");
result.append(staticSection.toString());
//последнюю цифру от 0 до нее
result.append("[0-"+Long.parseLong(number.toString().replaceFirst("\\d+(\\d)","$1"))+"]");
}
if (result.length()>0) return result.toString().replace("||","|").replaceAll("^\\|","");
return "";
}
static String staticPart(final Long number) {
final long majorDigitMinus1=(Long.parseLong(getMajorDigit(number))-1);
if (majorDigitMinus1<=0) return "";
if (majorDigitMinus1==2) return "[1"+majorDigitMinus1+"][0-9]{"+(getNumberOfDigits(number)-1)+"}";
else if (majorDigitMinus1==1) return "1[0-9]{"+(getNumberOfDigits(number)-1)+"}";
return "[1-"+majorDigitMinus1+"][0-9]{"+(getNumberOfDigits(number)-1)+"}";
}
/**
* [1-9][0-9]{<X-1>,<Y-1>}, where X-number of digits of less number, Y-number of digits of greater number
*/
static String speedUpPart(Long left, Long right) {
//найти ближайшее до 0 то есть для 23 найти 100 для 777 найти 1000
//округленные до ближайшего 0
if (!String.valueOf(left).matches("10*")) left=toPower(left);
if (!String.valueOf(right).matches("10*")) right=toPower(right)/10;
final int leftPartRepeat=getNumberOfDigits(left)+(String.valueOf(left).matches("10*")?0:1)-1;
final int rightPartRepeat=getNumberOfDigits(right)+(String.valueOf(right).matches("10*")?0:1)-2;
if (getNumberOfDigits(left)==1 && getNumberOfDigits(right)==2)
return "[1-9]";
else if (leftPartRepeat>=rightPartRepeat)
return "[1-9][0-9]{"+rightPartRepeat+"}";
else
return "[1-9][0-9]{"+leftPartRepeat+","+rightPartRepeat+"}";
}
private static long toPower(final Long number) {
final double dValue=Math.pow(10, getNumberOfDigits(number));
final String value=String.format(Locale.US,"%24.0f",dValue);
return Long.parseLong(value.replaceFirst("\\s*(\\d+)(\\D\\d+)?","$1"));
}
private static int getNumberOfDigits(long number){
return (String.valueOf(number).length());
}
private static String getMajorDigit(long number){
return (String.valueOf(number).substring(0,1));
}
private static long getNumberWithoutMajorDigit(long number){
return Long.parseLong(String.valueOf(number).replaceFirst("\\d(\\d+)","$1"));
}
/**
* f(<n>>2)=<major digit>(f(<n-1>)|[<major digit+1>-9][0-9]{<n-1>})
*/
static String startPart(long number) {
int i=getNumberOfDigits(number);
if (i==1) {
if (number==9) return "9";
else if (number==8) return "[89]";
return "["+number+"-9]";
}
final long majorPlusOne=Long.parseLong(getMajorDigit(number))+1;
final int numberOfDigitsMinusOne=getNumberOfDigits(number)-1;
String result = (majorPlusOne < 10 ? "(" : "");
result+=getMajorDigit(number);
result+=startPart(getNumberWithoutMajorDigit(number));
result+=result.indexOf("|")<0 && majorPlusOne<10 && majorPlusOne!=numberOfDigitsMinusOne && numberOfDigitsMinusOne>1?"{"+numberOfDigitsMinusOne+"}":"";
result+=(majorPlusOne < 10
? "|[" + majorPlusOne + "-9][0-9]"+(numberOfDigitsMinusOne > 1 ? "{" + numberOfDigitsMinusOne + "}" : "")
: "");
result+=(majorPlusOne < 10 ? ")" : "");
return result;
}
private void parseAndCheck() {
Matcher matcher=pattern.matcher(src);
matcher.find();
try{
left=Long.parseLong(matcher.group(1));
right=Long.parseLong(matcher.group(2));
}
catch(Exception ex){
left=right+1;
}
if (left>right){
throw new IllegalArgumentException(MessageFormat.format(invalidArgumentEmpty,
src,
"Left part must be less than right one."));
}
}
public String getPattern() {
return result;
}
public static void main(String[] args) {
System.err.println(new NumberDiapasone2RegExp(args[0]).getPattern());
}
}
I was also trying to find a range of valid range for minutes
[0-60] worked for me.
I am using jdk 1.8 though