将区间表示法解析为番石榴范围
Parsing Interval Notation to Guava Range
我需要将包含标准间隔符号(即 (8,100)、[6,10] 等)的字符串解析为 Guava Range 对象。我将如何在 Java 中做到这一点?是否有实用程序包可以将字符串解析为构建 Guava Range 对象所需的组件?
如果我们查看模式,间隔以 '['
或 '('
开头,然后至少跟一个数字,再跟一个逗号,再跟一个或多个数字并以 ']'
或 ')'
.
结尾
因此正则表达式将如下所示:
^[\(|\[](\d+),(\d+)[\)|\]]$
分解到这里:
^
[\(|\[] -> start either with `'['` or `'('` (we need to escape the special characters with `\`)
(\d+) -> followed by one or more digit that we capture in a group
, -> followed by a comma
(\d+) -> followed again by one or more digit that we capture in another group
[\)|\]] -> and that finishes either with `']'` or `')'`
$
^
和 $
断言所有字符串都匹配表达式,而不仅仅是它的一部分。
所以我们有了正则表达式,耶!
现在我们需要从中创建一个 Pattern
实例,以便能够从中获取匹配器。最后我们检查字符串是否与模式匹配,然后我们抓取相应的组
Pattern p = Pattern.compile("^[\(|\[](\d+),(\d+)[\)|\]]$");
Matcher m = p.matcher("(0,100)");
if(matcher.matches()) {
int lowerBound = Integer.parseInt(matcher.group(1));
int upperBound = Integer.parseInt(matcher.group(2));
System.out.println(lowerBound + "_" + upperBound);
}
以下输出0_100
.
现在是最后一步,获取第一个和最后一个字符并从中创建适当的范围;把它们放在一起:
class RangeFactory {
private static final Pattern p = Pattern.compile("^[\(|\[](\d+),(\d+)[\)|\]]$");
public static Range from(String range) {
Matcher m = p.matcher(range);
if(m.matches()) {
int length = range.length();
int lowerBound = Integer.parseInt(m.group(1));
int upperBound = Integer.parseInt(m.group(2));
if(range.charAt(0) == '(') {
if(range.charAt(length - 1) == ')') {
return Range.open(lowerBound, upperBound);
}
return Range.openClosed(lowerBound, upperBound);
} else {
if(range.charAt(length - 1) == ')') {
return Range.closedOpen(lowerBound, upperBound);
}
return Range.closed(lowerBound, upperBound);
}
}
throw new IllegalArgumentException("Range " + range + " is not valid.");
}
}
这是一些测试用例:
List<String> ranges =
Arrays.asList("(0,100)", "[0,100]", "[0,100)", "(0,100]", "", "()", "(0,100", "[,100]", "[100]");
for(String range : ranges) {
try {
System.out.println(RangeFactory.from(range));
} catch (IllegalArgumentException ex) {
System.out.println(ex);
}
}
输出:
(0‥100)
[0‥100]
[0‥100)
(0‥100]
java.lang.IllegalArgumentException: Range is not valid.
java.lang.IllegalArgumentException: Range () is not valid.
java.lang.IllegalArgumentException: Range (0,100 is not valid.
java.lang.IllegalArgumentException: Range [,100] is not valid.
java.lang.IllegalArgumentException: Range [100] is not valid.
您可以改进正则表达式(接受无限边界的范围等),但它应该给您一个好的起点。
希望对您有所帮助! :)
遇到了类似的问题并提出了这个解决方案:
private static final Pattern INTERVAL_PATTERN = Pattern.compile("([\[\(])(-?∞?\d*)(?:\,|\.\.)(-?∞?\d*)([\]\)])");
/**
* Parses integer ranges of format (2,5], (2..5], (2,), [2..), [2..∞), [2,∞)
*
* @param notaiton The range notation to parse
* @throws IllegalArgumentException if the interval is not in the defined notation format.
*/
public static Range<Integer> parseIntRange(@NonNull String notaiton) {
Matcher matcher = INTERVAL_PATTERN.matcher(notaiton);
if (matcher.matches()) {
Integer lowerBoundEndpoint = Ints.tryParse(matcher.group(2));
Integer upperBoundEndpoint = Ints.tryParse(matcher.group(3));
if (lowerBoundEndpoint == null && upperBoundEndpoint == null) {
return Range.all();
}
boolean lowerBoundInclusive = matcher.group(1).equals("[");
boolean upperBoundInclusive = matcher.group(4).equals("]");
//lower infinity case
if (lowerBoundEndpoint == null) {
if (upperBoundInclusive) {
return Range.atMost(upperBoundEndpoint);
} else {
return Range.lessThan(upperBoundEndpoint);
}
} //upper infinity case
else if (upperBoundEndpoint == null) {
if (lowerBoundInclusive) {
return Range.atLeast(lowerBoundEndpoint);
} else {
return Range.greaterThan(lowerBoundEndpoint);
}
}
//non infinity cases
if (lowerBoundInclusive) {
if (upperBoundInclusive) {
return Range.closed(lowerBoundEndpoint, upperBoundEndpoint);
} else {
return Range.closedOpen(lowerBoundEndpoint, upperBoundEndpoint);
}
} else {
if (upperBoundInclusive) {
return Range.openClosed(lowerBoundEndpoint, upperBoundEndpoint);
} else {
return Range.open(lowerBoundEndpoint, upperBoundEndpoint);
}
}
} else {
throw new IllegalArgumentException(notaiton + " is not a valid range notation");
}
}
单元测试:
@Test
public void testParseIntRange_infinites_parsesOK() {
assertThat(NumberUtils.parseIntRange("(,2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2,)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(,2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2,)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(..2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2..)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(..2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2..)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(∞,2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2,∞)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(∞,2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2,∞)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(∞..2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2..∞)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(∞..2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2..∞)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(-∞,2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(-∞,2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("(-∞,]"), is(Range.all()));
}
@Test
public void testParseIntRange_parsesOK() {
assertThat(NumberUtils.parseIntRange("(-2,3)"), is(Range.open(-2, 3)));
assertThat(NumberUtils.parseIntRange("(-2,-1)"), is(Range.open(-2, -1)));
assertThat(NumberUtils.parseIntRange("(2,3)"), is(Range.open(2, 3)));
assertThat(NumberUtils.parseIntRange("[2,3)"), is(Range.closedOpen(2, 3)));
assertThat(NumberUtils.parseIntRange("(2,3]"), is(Range.openClosed(2, 3)));
assertThat(NumberUtils.parseIntRange("[2,3]"), is(Range.closed(2, 3)));
assertThat(NumberUtils.parseIntRange("(2..3)"), is(Range.open(2, 3)));
assertThat(NumberUtils.parseIntRange("[2..3)"), is(Range.closedOpen(2, 3)));
assertThat(NumberUtils.parseIntRange("(2..3]"), is(Range.openClosed(2, 3)));
assertThat(NumberUtils.parseIntRange("[2..3]"), is(Range.closed(2, 3)));
}
@Test
public void testParseIntRange_WithInvalidStrings_failsAccordingly() {
String[] invalidParams = {
null, "", "(4 5", "[2,3] ", " [2,3]", "[2,3][2,3]", "[a,b]", " [2..3]", "[2.3]",
"[3...4]", "(3 4)", "[2]", "(5,1)", "ab[2,4]", "[2,4]cd", "(2,-2)", "(2,2)"
};
for (String invalidParam : invalidParams) {
try {
NumberUtils.parseIntRange(invalidParam);
fail("Parsing '" + invalidParam + "' did not fail");
} catch (IllegalArgumentException ex) {
}
}
}
我需要将包含标准间隔符号(即 (8,100)、[6,10] 等)的字符串解析为 Guava Range 对象。我将如何在 Java 中做到这一点?是否有实用程序包可以将字符串解析为构建 Guava Range 对象所需的组件?
如果我们查看模式,间隔以 '['
或 '('
开头,然后至少跟一个数字,再跟一个逗号,再跟一个或多个数字并以 ']'
或 ')'
.
因此正则表达式将如下所示:
^[\(|\[](\d+),(\d+)[\)|\]]$
分解到这里:
^
[\(|\[] -> start either with `'['` or `'('` (we need to escape the special characters with `\`)
(\d+) -> followed by one or more digit that we capture in a group
, -> followed by a comma
(\d+) -> followed again by one or more digit that we capture in another group
[\)|\]] -> and that finishes either with `']'` or `')'`
$
^
和 $
断言所有字符串都匹配表达式,而不仅仅是它的一部分。
所以我们有了正则表达式,耶!
现在我们需要从中创建一个 Pattern
实例,以便能够从中获取匹配器。最后我们检查字符串是否与模式匹配,然后我们抓取相应的组
Pattern p = Pattern.compile("^[\(|\[](\d+),(\d+)[\)|\]]$");
Matcher m = p.matcher("(0,100)");
if(matcher.matches()) {
int lowerBound = Integer.parseInt(matcher.group(1));
int upperBound = Integer.parseInt(matcher.group(2));
System.out.println(lowerBound + "_" + upperBound);
}
以下输出0_100
.
现在是最后一步,获取第一个和最后一个字符并从中创建适当的范围;把它们放在一起:
class RangeFactory {
private static final Pattern p = Pattern.compile("^[\(|\[](\d+),(\d+)[\)|\]]$");
public static Range from(String range) {
Matcher m = p.matcher(range);
if(m.matches()) {
int length = range.length();
int lowerBound = Integer.parseInt(m.group(1));
int upperBound = Integer.parseInt(m.group(2));
if(range.charAt(0) == '(') {
if(range.charAt(length - 1) == ')') {
return Range.open(lowerBound, upperBound);
}
return Range.openClosed(lowerBound, upperBound);
} else {
if(range.charAt(length - 1) == ')') {
return Range.closedOpen(lowerBound, upperBound);
}
return Range.closed(lowerBound, upperBound);
}
}
throw new IllegalArgumentException("Range " + range + " is not valid.");
}
}
这是一些测试用例:
List<String> ranges =
Arrays.asList("(0,100)", "[0,100]", "[0,100)", "(0,100]", "", "()", "(0,100", "[,100]", "[100]");
for(String range : ranges) {
try {
System.out.println(RangeFactory.from(range));
} catch (IllegalArgumentException ex) {
System.out.println(ex);
}
}
输出:
(0‥100)
[0‥100]
[0‥100)
(0‥100]
java.lang.IllegalArgumentException: Range is not valid.
java.lang.IllegalArgumentException: Range () is not valid.
java.lang.IllegalArgumentException: Range (0,100 is not valid.
java.lang.IllegalArgumentException: Range [,100] is not valid.
java.lang.IllegalArgumentException: Range [100] is not valid.
您可以改进正则表达式(接受无限边界的范围等),但它应该给您一个好的起点。
希望对您有所帮助! :)
遇到了类似的问题并提出了这个解决方案:
private static final Pattern INTERVAL_PATTERN = Pattern.compile("([\[\(])(-?∞?\d*)(?:\,|\.\.)(-?∞?\d*)([\]\)])");
/**
* Parses integer ranges of format (2,5], (2..5], (2,), [2..), [2..∞), [2,∞)
*
* @param notaiton The range notation to parse
* @throws IllegalArgumentException if the interval is not in the defined notation format.
*/
public static Range<Integer> parseIntRange(@NonNull String notaiton) {
Matcher matcher = INTERVAL_PATTERN.matcher(notaiton);
if (matcher.matches()) {
Integer lowerBoundEndpoint = Ints.tryParse(matcher.group(2));
Integer upperBoundEndpoint = Ints.tryParse(matcher.group(3));
if (lowerBoundEndpoint == null && upperBoundEndpoint == null) {
return Range.all();
}
boolean lowerBoundInclusive = matcher.group(1).equals("[");
boolean upperBoundInclusive = matcher.group(4).equals("]");
//lower infinity case
if (lowerBoundEndpoint == null) {
if (upperBoundInclusive) {
return Range.atMost(upperBoundEndpoint);
} else {
return Range.lessThan(upperBoundEndpoint);
}
} //upper infinity case
else if (upperBoundEndpoint == null) {
if (lowerBoundInclusive) {
return Range.atLeast(lowerBoundEndpoint);
} else {
return Range.greaterThan(lowerBoundEndpoint);
}
}
//non infinity cases
if (lowerBoundInclusive) {
if (upperBoundInclusive) {
return Range.closed(lowerBoundEndpoint, upperBoundEndpoint);
} else {
return Range.closedOpen(lowerBoundEndpoint, upperBoundEndpoint);
}
} else {
if (upperBoundInclusive) {
return Range.openClosed(lowerBoundEndpoint, upperBoundEndpoint);
} else {
return Range.open(lowerBoundEndpoint, upperBoundEndpoint);
}
}
} else {
throw new IllegalArgumentException(notaiton + " is not a valid range notation");
}
}
单元测试:
@Test
public void testParseIntRange_infinites_parsesOK() {
assertThat(NumberUtils.parseIntRange("(,2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2,)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(,2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2,)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(..2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2..)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(..2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2..)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(∞,2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2,∞)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(∞,2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2,∞)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(∞..2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(2..∞)"), is(Range.greaterThan(2)));
assertThat(NumberUtils.parseIntRange("(∞..2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("[2..∞)"), is(Range.atLeast(2)));
assertThat(NumberUtils.parseIntRange("(-∞,2)"), is(Range.lessThan(2)));
assertThat(NumberUtils.parseIntRange("(-∞,2]"), is(Range.atMost(2)));
assertThat(NumberUtils.parseIntRange("(-∞,]"), is(Range.all()));
}
@Test
public void testParseIntRange_parsesOK() {
assertThat(NumberUtils.parseIntRange("(-2,3)"), is(Range.open(-2, 3)));
assertThat(NumberUtils.parseIntRange("(-2,-1)"), is(Range.open(-2, -1)));
assertThat(NumberUtils.parseIntRange("(2,3)"), is(Range.open(2, 3)));
assertThat(NumberUtils.parseIntRange("[2,3)"), is(Range.closedOpen(2, 3)));
assertThat(NumberUtils.parseIntRange("(2,3]"), is(Range.openClosed(2, 3)));
assertThat(NumberUtils.parseIntRange("[2,3]"), is(Range.closed(2, 3)));
assertThat(NumberUtils.parseIntRange("(2..3)"), is(Range.open(2, 3)));
assertThat(NumberUtils.parseIntRange("[2..3)"), is(Range.closedOpen(2, 3)));
assertThat(NumberUtils.parseIntRange("(2..3]"), is(Range.openClosed(2, 3)));
assertThat(NumberUtils.parseIntRange("[2..3]"), is(Range.closed(2, 3)));
}
@Test
public void testParseIntRange_WithInvalidStrings_failsAccordingly() {
String[] invalidParams = {
null, "", "(4 5", "[2,3] ", " [2,3]", "[2,3][2,3]", "[a,b]", " [2..3]", "[2.3]",
"[3...4]", "(3 4)", "[2]", "(5,1)", "ab[2,4]", "[2,4]cd", "(2,-2)", "(2,2)"
};
for (String invalidParam : invalidParams) {
try {
NumberUtils.parseIntRange(invalidParam);
fail("Parsing '" + invalidParam + "' did not fail");
} catch (IllegalArgumentException ex) {
}
}
}