0

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()));
            }
        }
    ...
}

错误信息:

错误信息

4

2 回答 2

2

您正在 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...
    ...
于 2021-01-24T20:04:24.337 回答
0

Aside from the issue with StringTokenizer which could be resolved by additionally checking if token.hasNextToken() before calling each setter in Opera, there are some other issues in the code which need to be refactored:

  1. Use try-with-resources to ensure the Scanner instance is closed automatically
  2. Fix declaration and usage of local variables
  3. Use String::split method instead of StringTokenizer
  4. Implement a helper method to build an instance of Opera from the array of Strings
  5. Populate operas with the read Opera only after the file has been successfully processed - in current implementation existing list is cleared at once and populated afterwards, that is, in case of I/O exception existing data are destroyed.

All the mentioned issues are addressed in the following sample:

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;
}

Using Java Stream API along with java.nio classes would result in the following implementation (reusing method 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
    }
}
于 2021-01-24T20:57:36.647 回答