使用Java8流验证字符串如何克服“流已被操作或关闭”?

56lgkhnf  于 2021-06-27  发布在  Java
关注(0)|答案(3)|浏览(321)

任务是使用流检查这种类型的字符串“az6byw59uo6cr8bnt7nm284130”是否满足以下条件:
空格前20个大写字母字符或数字,严格数字为6
空格后6位
以下是我迄今为止所做的:

public static boolean validateCode(String input)
{
    String[] words = input.split("\\s+");
    ArrayList<String> wordList = new ArrayList<String>(Arrays.asList(words));

    Stream<Character> left = wordList.get(0).chars().mapToObj(ch -> (char)ch);
    Stream<Character> right = wordList.get(1).chars().mapToObj(ch -> (char)ch);

   boolean leftIs20 = left
            .collect(Collectors.counting()).equals(20L);

    boolean leftisAlfaDigit = left
            .allMatch(x -> (Character.isDigit(x) || Character.isUpperCase(x)));

     boolean leftis6Digits = left.filter(x -> Character.isDigit(x)).equals(6L);

    boolean rightAreDigits = right.allMatch(x ->Character.isDigit(x));

    boolean rightAre6Digits = right.collect(Collectors.counting()).equals(6L);

    return leftIs20 && leftisAlfaDigit && leftis6Digits && rightAreDigits && rightAre6Digits;
}

但是由于流不能被重用,这是错误的,但是我不知道如何克服这个问题。

zxlwwiss

zxlwwiss1#

确保检查结果数组的大小(即通过拆分给定字符串获得的数组)以避免 ArrayIndexOutOfBoundsException .
由于您必须在子字符串的左侧执行三个独立的操作,因此必须创建一个流提供者来构造一个新的流,并且已经设置了所有中间操作。查看本文了解更多信息。
演示:

import java.util.function.Supplier;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        // Test
        String[] testStrs = { "AZ6BYW59UO6CR8BNT7NM 284130", "AZ6BYW59UOXCR8BNT7NM 284130",
                "AZ6BYW59UO6CR8BNT7NM 7284130", "AbZ6BYW59UO6CR8BNT7NM 284130", "AZ6BYW59UO6CR8BNT7NM284130" };
        for (String s : testStrs) {
            System.out.println(s + " => " + validateCode(s));
        }
    }

    public static boolean validateCode(String input) {
        int leftAllCount = 20, leftDigCount = 6, rightDigCount = 6;
        String[] words = input.split("\\s+");
        if (words.length >= 2) {
            Supplier<Stream<Character>> streamSupplierLeft = () -> words[0].chars().mapToObj(ch -> (char) ch);
            Stream<Character> right = words[1].chars().mapToObj(ch -> (char) ch);
            return streamSupplierLeft.get().count() == leftAllCount
                    && streamSupplierLeft.get().filter(Character::isDigit).count() == leftDigCount
                    && streamSupplierLeft.get().filter(Character::isUpperCase).count() == (leftAllCount - leftDigCount)
                    && right.filter(Character::isDigit).count() == rightDigCount;
        }
        return false;
    }
}

输出:

AZ6BYW59UO6CR8BNT7NM 284130 => true
AZ6BYW59UOXCR8BNT7NM 284130 => false
AZ6BYW59UO6CR8BNT7NM 7284130 => false
AbZ6BYW59UO6CR8BNT7NM 284130 => false
AZ6BYW59UO6CR8BNT7NM284130 => false
jtw3ybtb

jtw3ybtb2#

正如其他人指出的,没有必要为此使用流,但可以做到。如果我理解正确的要求,那么这应该可以满足您对streams的需求。

private static boolean validateCode(String in) {
    //The task is to check using Stream if such type of string "AZ6BYW59UO6CR8BNT7NM 284130" satisfies these conditions:
    //20 uppercase alphabetical chars or digits before the space, the number of digits is 6 strictly
    //6 digits after the space
    final var input = in.split("\\s");
    final var a = input[0];
    final var b = input[1];
    return a.chars().filter(c -> Character.isDigit(c) || (Character.isAlphabetic(c) && Character.isUpperCase(c))).count() == 20 &&
      b.chars().filter(Character::isDigit).count() == 6;
  }

要测试它:

System.err.println(validateCode("AZ6BYW59UO6CR8BNT7NM 284130")); //valid
    System.err.println(validateCode("Z6BYW59UO6CR8BNT7NM 284130")); //less than 20
    System.err.println(validateCode("A$6BYW59UO6CR8BNT7NM 284130")); //non alpha numeric
    System.err.println(validateCode("AZ6BYW59UO6CR8BNT7NM 284k30")); //non-digit in end
    System.err.println(validateCode("AZ6BYW59UO6CR8BNT7NM 28413")); //less than 6
    System.err.println(validateCode("AZ6BYW59UO6CR8BNT7NM 2841302")); //more than 6

编辑-我错过了第一部分必须有6位数字的事实

private static boolean validateCode(String in) {
    final var input = in.split("\\s");
    final var a = input[0];
    final var b = input[1];
    final var prefixCounts = a.chars()
      .mapToObj(c -> Map.entry(Character.isDigit(c) ? 1 : 0, Character.isAlphabetic(c) && Character.isUpperCase(c) ? 1 : 0))
      .reduce(Map.entry(0, 0), (lhs, rhs) -> Map.entry(lhs.getKey() + rhs.getKey(), lhs.getValue() + rhs.getValue()));
    return prefixCounts.getKey() == 6 && prefixCounts.getValue() == 14 && //6 digits and 14 upper case letters = 20 total
      b.chars().filter(Character::isDigit).count() == 6;
  }

我要提醒的是,使用它可以工作,但是streamapi可能太过杀伤力了。此解决方案还可以在map reduce期间创建临时对象,而其他解决方案可以避免这些临时对象。对于许多情况来说,这并不是什么大问题,但在性能危急的情况下,这可能会出现在分析中。您必须在自己的测试中查看它与regex或其他解决方案的比较。这只是使用所要求的方法回答问题。

iqjalb3h

iqjalb3h3#

您所需要的只是匹配模式,这样流就没有必要或不太合适了。但是,您可以使用 filter ```
String[] data = { "AZ6BYW59UO6CR8BNT7NM 284130", // valid
"AZ6BYW59UO6&C8BNT7NM 2841S0", // non letter or digit (&)
"MMMMMMMMMMMMMMMMMMMM 823820", // not enough digits
"111111MMMMMMMMMMMMMM 228130", // valid
"11111111111111111111 228130", // too may digits
"AZ6BYW59UO6CR8BT7NM 284130", // first word too short
"AZ6BYW59UO6CR8BNT7NM 2824130", // seven digits
"AZ6BYW59UO6CRs8BNT7NM 284130", // first word too long
"AZ6BYW59UO6CR8BNT7NM 282230" // too many spaces
"AZ6BYW59UO6CR8BNT7NM 28230" }; // five digits

for (String text : data) {
System.out.printf("%30s -> %s%n", text, validateCode(text) ? "Valid" : "Invalid");
}

印刷品

AZ6BYW59UO6CR8BNT7NM 284130 -> Valid
AZ6BYW59UO6&C8BNT7NM 2841S0 -> Invalid
MMMMMMMMMMMMMMMMMMMM 823820 -> Invalid
111111MMMMMMMMMMMMMM 228130 -> Valid
11111111111111111111 228130 -> Invalid
AZ6BYW59UO6CR8BT7NM 284130 -> Invalid
AZ6BYW59UO6CR8BNT7NM 2824130 -> Invalid
AZ6BYW59UO6CRs8BNT7NM 284130 -> Invalid
AZ6BYW59UO6CR8BNT7NM 282230 -> Invalid
AZ6BYW59UO6CR8BNT7NM 28230 -> Invalid

这将在单个空间上拆分,并尝试匹配所需的内容。如果通过,计数将为1,并返回true。否则,0和false。 `\\w{20}` 将匹配20个字母和数字的单词。 `\\d{6}` 将匹配6位数字。

public static boolean validateCode(String text) {
return Stream.<String[]>of(text.split("\s"))
.filter(s -> s.length == 2 && s[0].matches("\w{20}") && s[0].chars()
.filter(Character::isDigit)
.count() == 6 && s[1].matches("\d{6}"))
.count() == 1;
}

相关问题