问题描述
从“|”读取时我得到一个 NoSuchElementException
(管道)分隔的文本文件。
我认为这是导致错误的部分:
public void readFromFile(String file)
{
operas.clear(); //clear the ArrayList
try
{
//Read while there is data
Scanner input = new Scanner(new File(file)); //alternative to FileReader & BufferedReader
String line = "";
Opera music = null;
StringTokenizer token = null;
while(input.hasNextLine())
{
line = input.nextLine();
music = new Opera(); // create an opera
token = new StringTokenizer(line,"|");
while (token.hasMoreElements())
{
music.setName(token.nextToken());
music.setComposer(token.nextToken());
music.setYear(Integer.parseInt(token.nextToken()));
music.setCity(token.nextToken());
music.setSynopsis(token.nextToken());
music.setLink((token.nextToken()));
}
}
...
}
错误信息:
解决方法
您正在 while 循环中检查是否有更多元素。 如果是这样,则保证您至少有 1 个元素。 但是,您正在尝试在此循环中读取 6 个元素...
因此,对于没有 6 个标记的行,您会收到此错误。
您应该检查您是否有足够的令牌来进行 6 次“nextToken”调用。
我建议使用不同的网络功能。有没有考虑过字符串拆分?
类似于:
...
line = input.nextLine();
String[] tokens = line.split("\\|");
// Check that "tokens" is an array with 6 tokens as you expect...
...
,
除了 StringTokenizer
的问题可以通过在调用 token.hasNextToken()
中的每个 setter 之前额外检查 Opera
来解决,代码中还有一些其他问题需要重构:
- 使用
try-with-resources
确保Scanner
实例自动关闭 - 修复局部变量的声明和使用
- 使用
String::split
方法代替StringTokenizer
- 实现一个辅助方法以从
Opera
数组构建String
的实例 - 在成功处理文件后使用读取的
operas
填充Opera
- 在当前实现中,现有列表会立即清除并随后填充 em>,即在 I/O 异常的情况下,现有数据被销毁。
所有提到的问题都在以下示例中得到解决:
public void readFromFile(String file) {
try (Scanner input = new Scanner(new File(file))) { // auto-close Scanner when done
List<Opera> readOperas = new ArrayList<>();
while(input.hasNextLine()) {
String line = input.nextLine();
String[] row = line.split("\\|");
Opera music = buildOpera(row); // create an opera
readOperas.add(music);
}
operas.clear();
operas.addAll(readOperas);
} catch (IOException ioex) {
// log the exception
}
}
static Opera buildOpera(String ... row) {
Opera opera = new Opera();
if (row.length > 0) { opera.setName(row[0]); }
if (row.length > 1) { opera.setComposer(row[1]); }
if (row.length > 2) { opera.setYear(Integer.parseInt(row[2])); }
if (row.length > 3) { opera.setCity(row[3]); }
if (row.length > 4) { opera.setSynopsis(row[4]); }
if (row.length > 5) { opera.setLink(row[5]); }
return opera;
}
将 Java Stream API 与 java.nio
类一起使用将导致以下实现(重用方法 buildOpera
):
public void readFromFile(String file) {
try {
List<Opera> readOperas = Files.lines(Path.of(inputFileName)) // get stream of lines from the input file
.map(s -> s.split("\\|")) // split a line into columns,get Stream<String[]>
.filter(row -> row.length == 6) // make sure a row contains full data
.map(MyClass::buildOpera)
.collect(Collectors.toList());
operas.clear();
operas.addAll(readOperas);
} catch (IOException ex) {
// log IO exception
}
}