使用扫描仪创建单词流
Create words' stream using scanner
需要 return 文件中包含 3 个或更多字母的所有单词的流。有没有更好的方法然后跟随,也许使用 Stream.iterate:
private Stream<String> getWordsStream(String path){
Stream.Builder<String> wordsStream = Stream.builder();
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Scanner s = new Scanner(inputStream);
s.useDelimiter("([^a-zA-Z])");
Pattern pattern = Pattern.compile("([a-zA-Z]{3,})");
while ((s.hasNext())){
if(s.hasNext(pattern)){
wordsStream.add(s.next().toUpperCase());
}
else {
s.next();
}
}
s.close();
return wordsStream.build();
}
有更简单的方法:从文件中读取行到 Stream
并使用所需条件(例如长度 >= 3)对其进行过滤。 Files.lines()
有延迟加载,所以它不会在开始时准备好文件中的所有单词,它会在每次需要下一个单词时执行
public static void main(String... args) throws IOException {
getWordsStream(Paths.get("d:/words.txt")).forEach(System.out::println);
}
public static Stream<String> getWordsStream(Path path) throws IOException {
final Scanner scan = new Scanner(path);
return StreamSupport.stream(new Spliterators.AbstractSpliterator<String>(Long.MAX_VALUE,
Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED) {
@Override
public boolean tryAdvance(Consumer<? super String> action) {
while (scan.hasNext()) {
String word = scan.next();
// you can use RegExp if you have more complicated condition
if (word.length() < 3)
continue;
action.accept(word);
return true;
}
return false;
}
}, false).onClose(scan::close);
}
您可以使用 Files.lines()
和 Pattern
:
private static final Pattern SPACES = Pattern.compile("[^a-zA-Z]+");
public static Stream<String> getWordStream(String path) throws IOException{
return Files.lines(Paths.get(path))
.flatMap(SPACES::splitAsStream)
.filter(word -> word.length() >= 3);
}
代码中最糟糕的部分是以下部分
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Scanner s = new Scanner(inputStream);
因此,当文件不存在时,您将打印 FileNotFoundException
堆栈跟踪并继续 null
输入流,导致 NullPointerException
。您不应要求调用者处理虚假的 NullPointerException
,而应在方法签名中声明 FileNotFoundException
。否则,return 错误情况下的空流。
但是您根本不需要构造 FileInputStream
,因为 Scanner
提供了接受 File
或 Path
的构造函数。将此与 return 匹配流的功能相结合(自 Java 9),您将获得:
private Stream<String> getWordsStream(String path) {
try {
Scanner s = new Scanner(Paths.get(path));
return s.findAll("([a-zA-Z]{3,})").map(mr -> mr.group().toUpperCase());
} catch(IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
return Stream.empty();
}
}
或最好
private Stream<String> getWordsStream(String path) throws IOException {
Scanner s = new Scanner(Paths.get(path));
return s.findAll("([a-zA-Z]{3,})").map(mr -> mr.group().toUpperCase());
}
这里甚至不需要 .useDelimiter("([^a-zA-Z])")
,因为跳过所有不匹配的内容是默认行为。
关闭 returned Stream
也会关闭 Scanner
。
所以调用者应该这样使用
try(Stream<String> s = getWordsStream("path/to/file")) {
s.forEach(System.out::println);
}
需要 return 文件中包含 3 个或更多字母的所有单词的流。有没有更好的方法然后跟随,也许使用 Stream.iterate:
private Stream<String> getWordsStream(String path){
Stream.Builder<String> wordsStream = Stream.builder();
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Scanner s = new Scanner(inputStream);
s.useDelimiter("([^a-zA-Z])");
Pattern pattern = Pattern.compile("([a-zA-Z]{3,})");
while ((s.hasNext())){
if(s.hasNext(pattern)){
wordsStream.add(s.next().toUpperCase());
}
else {
s.next();
}
}
s.close();
return wordsStream.build();
}
有更简单的方法:从文件中读取行到 Stream
并使用所需条件(例如长度 >= 3)对其进行过滤。 Files.lines()
有延迟加载,所以它不会在开始时准备好文件中的所有单词,它会在每次需要下一个单词时执行
public static void main(String... args) throws IOException {
getWordsStream(Paths.get("d:/words.txt")).forEach(System.out::println);
}
public static Stream<String> getWordsStream(Path path) throws IOException {
final Scanner scan = new Scanner(path);
return StreamSupport.stream(new Spliterators.AbstractSpliterator<String>(Long.MAX_VALUE,
Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED) {
@Override
public boolean tryAdvance(Consumer<? super String> action) {
while (scan.hasNext()) {
String word = scan.next();
// you can use RegExp if you have more complicated condition
if (word.length() < 3)
continue;
action.accept(word);
return true;
}
return false;
}
}, false).onClose(scan::close);
}
您可以使用 Files.lines()
和 Pattern
:
private static final Pattern SPACES = Pattern.compile("[^a-zA-Z]+");
public static Stream<String> getWordStream(String path) throws IOException{
return Files.lines(Paths.get(path))
.flatMap(SPACES::splitAsStream)
.filter(word -> word.length() >= 3);
}
代码中最糟糕的部分是以下部分
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Scanner s = new Scanner(inputStream);
因此,当文件不存在时,您将打印 FileNotFoundException
堆栈跟踪并继续 null
输入流,导致 NullPointerException
。您不应要求调用者处理虚假的 NullPointerException
,而应在方法签名中声明 FileNotFoundException
。否则,return 错误情况下的空流。
但是您根本不需要构造 FileInputStream
,因为 Scanner
提供了接受 File
或 Path
的构造函数。将此与 return 匹配流的功能相结合(自 Java 9),您将获得:
private Stream<String> getWordsStream(String path) {
try {
Scanner s = new Scanner(Paths.get(path));
return s.findAll("([a-zA-Z]{3,})").map(mr -> mr.group().toUpperCase());
} catch(IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
return Stream.empty();
}
}
或最好
private Stream<String> getWordsStream(String path) throws IOException {
Scanner s = new Scanner(Paths.get(path));
return s.findAll("([a-zA-Z]{3,})").map(mr -> mr.group().toUpperCase());
}
这里甚至不需要 .useDelimiter("([^a-zA-Z])")
,因为跳过所有不匹配的内容是默认行为。
关闭 returned Stream
也会关闭 Scanner
。
所以调用者应该这样使用
try(Stream<String> s = getWordsStream("path/to/file")) {
s.forEach(System.out::println);
}