Java中校验歌曲是否已存在于TXT文件时重复添加的问题
如何正确校验歌曲是否已存在于TXT文件中
当前要实现的功能是将Song对象信息写入TXT文件,写入前需要校验是否存在同名且同歌手的歌曲,但现有代码每次用相同测试对象运行都会重复添加,控制台多次输出"variable changed"。
原问题代码
static void addSong(Song song){ Boolean SongAlreadyExists = true; // 逐字符比较至ARTIST字段,校验歌曲是否已存在 String lineToAdd = "NAME:" + song.name + "|ARTIST:" + song.artist; try { FileReader fReader = new FileReader("songs.txt"); BufferedReader bReader = new BufferedReader(fReader); String line; while((line = bReader.readLine()) != null){ for (int i=0; i < lineToAdd.length(); i++){ if (line.charAt(i) != lineToAdd.charAt(i)){ SongAlreadyExists = false; System.out.println("variable changed"); } } } bReader.close(); fReader.close(); } catch (IOException exception){ System.out.println("file not found"); } // 若歌曲不存在,则将其添加至歌曲列表文本文件 if (! (SongAlreadyExists)){ System.out.println("Adding song"); try { FileWriter fileWriter = new FileWriter("songs.txt", true); fileWriter.write("\nNAME:" + song.name + "|ARTIST:" + song.artist + "|ALBUM:" + song.album + "|YEAR:" + song.year + "|"); fileWriter.close(); } catch (Exception exception){ System.out.println("error"); } } }
测试用TXT数据
NAME:TestName|ARTIST:TestArtist|ALBUM:TestAlbum|YEAR:2005|
问题根源
- 初始值逻辑颠倒:
SongAlreadyExists初始设为true,但正确逻辑应该是默认歌曲不存在,找到匹配项后再标记为存在。 - 逐字符对比逻辑缺陷:
- 只要某一行和目标字符串有一个字符不同,就将
SongAlreadyExists设为false,但后续如果遇到匹配行,这个值不会重新设为true。 - 文件中每行的长度比
lineToAdd长(包含专辑、年份字段),逐字符对比到lineToAdd长度时,后续字符不匹配会直接触发判断,导致误判。
- 只要某一行和目标字符串有一个字符不同,就将
- 未及时终止遍历:找到匹配歌曲后仍继续读取文件,既浪费资源又可能覆盖正确的标记状态。
正确实现思路
- 初始化
songAlreadyExists为false,默认歌曲不存在。 - 对文件每行内容,通过分割字符串提取
NAME和ARTIST字段,直接和待添加歌曲的对应字段对比,避免逐字符对比的局限性。 - 一旦找到匹配项,立即标记
songAlreadyExists为true并终止循环,停止读取文件。 - 使用
try-with-resources自动管理文件流,避免手动关闭流时出现异常导致资源泄漏。
修正后的代码
static void addSong(Song song) { boolean songAlreadyExists = false; // 初始默认歌曲不存在 String targetName = song.name; String targetArtist = song.artist; // try-with-resources自动关闭流,无需手动调用close() try (BufferedReader bReader = new BufferedReader(new FileReader("songs.txt"))) { String line; while ((line = bReader.readLine()) != null) { // 按|分割每行的字段 String[] fields = line.split("\\|"); String currentName = ""; String currentArtist = ""; // 提取当前行的NAME和ARTIST字段值 for (String field : fields) { if (field.startsWith("NAME:")) { currentName = field.substring(5); // 截取NAME:之后的内容 } else if (field.startsWith("ARTIST:")) { currentArtist = field.substring(7); // 截取ARTIST:之后的内容 } } // 对比名称和歌手,完全匹配则标记为已存在并终止循环 if (targetName.equals(currentName) && targetArtist.equals(currentArtist)) { songAlreadyExists = true; break; } } } catch (IOException exception) { System.out.println("读取文件失败: " + exception.getMessage()); } // 根据校验结果执行添加操作 if (!songAlreadyExists) { System.out.println("添加歌曲"); try (FileWriter fileWriter = new FileWriter("songs.txt", true)) { String newSongLine = "\nNAME:" + targetName + "|ARTIST:" + targetArtist + "|ALBUM:" + song.album + "|YEAR:" + song.year + "|"; fileWriter.write(newSongLine); } catch (Exception exception) { System.out.println("写入文件失败: " + exception.getMessage()); } } else { System.out.println("该歌曲已存在,无需重复添加"); } }
改进点说明
- 字段精准对比:通过分割提取字段的方式,只对比关键的名称和歌手信息,不受行内其他字段影响,判断更准确。
- 资源安全管理:
try-with-resources会在代码块结束后自动关闭文件流,避免因异常导致的资源泄漏。 - 逻辑高效清晰:找到匹配项后立即终止循环,减少不必要的IO操作;初始值和判断逻辑符合直观认知,更易维护。
- 错误信息优化:捕获异常时输出具体错误内容,便于定位问题。
内容的提问来源于stack exchange,提问作者Abriel
相关产品推荐
相关产品推荐

