我正在创建数独游戏,我正在尝试提供保存,保存和打开游戏的选项。我正在使用JFileChooser来做到这一点。我可以保存(或“另存为”)但是当我尝试打开保存的文件时,我收到错误。我是编程的新手,我希望有人能够发现这个问题,并教我如何在保存时阅读数独板的内容(以及如何在我打开时重新创建数独板)文件)。我听说有一种更简单的方法来处理这个使用InputStream / OutputStream而不是Reader / Writer ......
这是我实现此内部类的代码(我不知道是否有办法发布我的整个类而不超过此文本框的字符限制。):
编辑:
// this inner class provides a JMenuBar object at the top of
// the board
class MenuAtTop extends JMenuBar implements ActionListener{
// SudokuMain object we are dealing with
private SudokuMain main;
// the "File" menu
private JMenu fileMenu;
// the "New Game" option
private JMenuItem newGame;
// the "Open" option
private JMenuItem open;
// the "Save" option
private JMenuItem save;
// the "Save As" option
private JMenuItem saveAs;
// the "Reset" option
private JMenuItem reset;
// the "Quit" option
private JMenuItem quit;
// the ability to choose files
private JFileChooser choose;
// the saved file
// // compiler would not allow "static" keyword
private File fileSaved = null;
private Object opener;
// JDialog object to create a dialog box to prompt
// user for new game information
private JDialog createNewWin;
/**
* Constructs MenuAtTop object.
*
* @param m The SudokuMain object to be referred to.
*/
public MenuAtTop(final SudokuMain m) {
main = m;
opener = null;
choose = new JFileChooser();
// instantiate and bind to reference
fileMenu = new JMenu("File");
add(fileMenu);
// instantiate and bind to reference
newGame = new JMenuItem("New Game");
newGame.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
ActionEvent.CTRL_MASK));
fileMenu.add(newGame);
newGame.addActionListener(this);
open = new JMenuItem("Open");
open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
ActionEvent.CTRL_MASK));
fileMenu.add(open);
// add action listener to "Open" option
open.addActionListener(this);
save = new JMenuItem("Save");
save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
ActionEvent.CTRL_MASK));
fileMenu.add(save);
// add action listener to "Save" option
save.addActionListener(this);
saveAs = new JMenuItem("Save As");
saveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
ActionEvent.CTRL_MASK));
fileMenu.add(saveAs);
// add action listener to "Save As" option
saveAs.addActionListener(this);
reset = new JMenuItem("Reset");
reset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
ActionEvent.CTRL_MASK));
fileMenu.add(reset);
// add action listener to "Reset" option
reset.addActionListener(this);
quit = new JMenuItem("Quit");
quit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
ActionEvent.CTRL_MASK));
fileMenu.add(quit);
// add action listener to "Quit" option
quit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(newGame)) {
setEnabled(false);
// create dialog box prompting for the new board information
createNewWin = new Dialog1(main, "Create New Board", true);
// make it visible
createNewWin.setVisible(true);
fileSaved = null;
} else if(e.getSource().equals(open)) {
int returnVal = choose.showOpenDialog(main.win);
if(returnVal == JFileChooser.APPROVE_OPTION) {
boolean error = false;
File openFile = choose.getSelectedFile();
try {
FileInputStream fis = new FileInputStream(openFile);
ObjectInputStream ois = new ObjectInputStream(fis);
opener = ois.readObject();
SudokuBase sudoku = (SudokuBoard) opener;
ois.close();
} catch (Exception exc) {
exc.printStackTrace();
JOptionPane.showMessageDialog(main.win, "Error opening file.");
error = true;
}
// "opener" reads something and it is of type SudokuBase
if(opener != null && opener instanceof SudokuBase){
main.north.remove(main.rowColRegStates);
main.west.remove(main.symbols);
main.east.remove(main.view);
main.view = new SudokuView((SudokuBase) opener);
main.rowColRegStates = new ShowStates(main.view);
main.symbols = new SetSymbols(main.view);
main.north.add(main.rowColRegStates);
main.west.add(main.symbols);
main.east.add(main.view);
main.win.requestFocus();
fileSaved = openFile;
} else {
if(error) {
JOptionPane.showMessageDialog(main.win, "Incorrect file type.");
}
}
}
} else if(e.getSource().equals(save)) {
if(fileSaved == null) {
saveAsPrompt();
} else {
try {
FileOutputStream fos = new FileOutputStream(fileSaved);
ObjectOutputStream oos = new ObjectOutputStream(fos);
board.writeToStream(fos);
oos.writeObject(board);
oos.close();
} catch (Exception exc) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
}
}
} else if(e.getSource().equals(saveAs)) {
saveAsPrompt();
} else if(e.getSource().equals(reset)) {
int n = JOptionPane.showConfirmDialog(main.win,
"Any player values will" +
" be lost. Proceed?",
"Warning!", 2);
if(n == JOptionPane.OK_OPTION) {
main.board.reset();
main.view.repaint();
}
} else if(e.getSource().equals(quit)) {
closePrompt();
}
}
// This method prompts the user to choose a file to save to,
// and then saves the file.
private int saveAsPrompt() {
boolean saveError;
int rtn = choose.showSaveDialog(main.win);
if(rtn == JFileChooser.APPROVE_OPTION) {
saveError = false;
File fileSaveAs = choose.getSelectedFile();
try {
board.writeToStream(new FileOutputStream(fileSaveAs));
} catch (Exception e) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
saveError = true;
}
if(!saveError) {
fileSaved = fileSaveAs;
}
}
return rtn;
}
// This method prompts the user whether they want to save before
// closing, only if changes occurred.
private void closePrompt() {
if(true) {
int n = JOptionPane.showConfirmDialog(main.win, "Save game?");
if(n == JOptionPane.YES_OPTION) {
int saved = saveAsPrompt();
if(saved != JFileChooser.CANCEL_OPTION){
main.win.dispose();
}
} else if(n == JOptionPane.NO_OPTION) {
main.win.dispose();
}
}
else { // no changes were made
main.win.dispose();
}
}
}
这是保存数据的类(由SudokuBoard扩展):
// Allow short name access to following classes
import java.util.Observable;
import java.io.InputStream;
import java.io.OutputStream;
public abstract class SudokuBase extends Observable {
// rows per region
private int rows;
// columns per region
private int columns;
// size of a region (rows * columns)
private int size;
// array of each element of entire sudoku board
private int[] grid;
// the masked 8-bit "given" value constant
private static final int GIVEN_MASK = 0x00000100;
// the bitwise complement of the masked "given" constant,
// which produces an unmasked constant
private static final int GIVEN_UNMASK = ~ GIVEN_MASK;
/**
* Enumerated type to store constants that indicate the "State" of
* a specified row, column, or region.
*/
public enum State {COMPLETE, INCOMPLETE, ERROR};
/**
* Constructs SudokuBase object.
*
* @param layoutRows The number of rows per region.
* @param layoutColumns The number of columns per region.
*/
public SudokuBase(int layoutRows, int layoutColumns) {
rows = layoutRows;
columns = layoutColumns;
size = columns * rows;
grid = new int[size*size];
}
/**
* Gets the number of rows per region.
*
* @return The rows per region.
*/
public int getRowsPerRegion() {
return rows;
}
/**
* Gets the number of columns per region.
*
* @return The columns per region.
*/
public int getColumnsPerRegion() {
return columns;
}
/**
* Gets the size of the region (rows * columns).
*
* @return The size of the region.
*/
public int getBoardSize() {
return size;
}
// gets the index of the specified row and column for the grid
private int getIndex(int row, int col) {
// handle invalid arguments
if(row < 0 || row >= size || col < 0 || col >= size) {
String msg = "Error in location";
throw new IllegalArgumentException(msg);
}
return row * size + col;
}
/**
* Gets the value of the element at the specified row
* and column on the grid.
*
* @param row The specified row.
* @param col The specified column.
* @return The value of the element at the specified row and column.
*/
public int getValue(int row, int col) {
return grid[getIndex(row, col)] & GIVEN_UNMASK;
}
/**
* Sets the desired value at the specified row and column.
*
* @param row The specified row.
* @param col The specified column.
* @param value The specified value to be set.
*/
public void setValue(int row, int col, int value) {
// handle invalid argument
if(value < 0 || value > size) {
String msg = "Value out of range: " + value;
throw new IllegalArgumentException(msg);
}
// handle attempt to set a value for a "given" location
if(isGiven(row, col)) {
String msg = "Cannot set given location: " + row + ", " + col;
throw new IllegalStateException(msg);
}
grid[getIndex(row, col)] = value;
setChanged();
notifyObservers();
}
/**
* This method checks the status of the "givens" bit.
*
* @param row The specified row.
* @param col The specified column.
* @return Whether or not the specified location is a "given" value.
*/
public boolean isGiven(int row, int col) {
return (grid[getIndex(row, col)] & GIVEN_MASK) == GIVEN_MASK;
}
/**
* This method sets non-zero values on the Sudoku board with the
* "givens" bit.
*/
public void fixGivens() {
for(int i = 0; i < grid.length; i++)
if(grid[i] != 0)
grid[i] |= GIVEN_MASK;
setChanged();
notifyObservers();
}
/**
* This abstract method gets the "State" (COMPLETE, INCOMPLETE,
* or ERROR) of the specified row.
*
* @param n The specified row.
* @return The "State" of the row.
*/
public abstract State getRowState(int n);
/**
* This abstract method gets the "State" (COMPLETE, INCOMPLETE,
* or ERROR) of the specified column.
*
* @param n The specified column.
* @return The "State" of the column.
*/
public abstract State getColumnState(int n);
/**
* This abstract method gets the "State" (COMPLETE, INCOMPLETE,
* or ERROR) of the specified region.
*
* @param n The specified region.
* @return The "State" of the region.
*/
public abstract State getRegionState(int n);
/**
* Represents the Sudoku board as a grid of appropriate characters.
*
* @return The string representation of the Sudoku board.
*/
public String toString() {
String board = "";
for(int i = 0; i < size; i ++) {
for(int j = 0; j < size; j ++)
board += charFor(i, j) + " ";
board += "\n";
}
return board;
}
// this method provides a character for all possible values encountered on the
// Sudoku board, to be utilized in "toString()"
private String charFor(int i, int j) {
int v = getValue(i, j);
// negative value (invalid)
if(v < 0) {
return "?";
} else if(v == 0) { // blank or zero value
return ".";
} else if(v < 36) { // value from 1 to (size * size)
return Character.toString(Character.forDigit(v, 36)).toUpperCase();
} else { // non-numeric input or v >= size * size (both invalid)
return "?";
}
}
/**
* This method reads from an input stream.
*
* @param is The input stream to read from.
*/
protected void readFromStream(InputStream is) {
}
/**
* This method writes to an output stream.
*
* @param os The output stream to write to.
*/
protected void writeToStream(OutputStream os) {
try {
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.close();
} catch(IOException e) {
}
}
/**
* Gets the "raw" value directly, not having checked whether there is an
* unfixed error message.
*
* @param row The row where the raw value is located.
* @param col The column where the raw value is located.
* @return The raw value.
*/
protected int getRawValue(int row, int col) {
return grid[getIndex(row, col)];
}
/**
* Sets the raw value directly, not having checked whether there is an
* unfixed error message.
*
* @param row The row where the raw value is to be located.
* @param col The column where the raw value is to be located.
*/
protected void setRawValue(int row, int col, int value) {
grid[getIndex(row, col)] = value;
}
protected void reset() {
for(int row = 0; row < rows; row++) {
for(int col = 0; col < columns; col++) {
if(!isGiven(row, col)) {
grid[getIndex(row, col)] = 0;
}
}
}
}
}
答案 0 :(得分:3)
我无法给出完整的答案,我不想浏览完整的源代码。但是您可以找到一些解决方案:
在开发应用程序时永远不要捕获这样的异常:
} catch (Exception e) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
saveError = true;
}
有了这个,你就完全没有机会发现错误了。至少在您的异常处理中添加以下行:
e.printStackTrace();
通常你会记录异常,依此类推,但是你可以在控制台上看到你的错误来源。总比没有好。
针对您更具体的问题: 您似乎将一个Object写入包含所有配置的文件。在你的阅读方法中出现问题。可能你没有读到与你写的相同的对象或类似的东西。很难说没有任何代码。尝试获取异常堆栈跟踪并找出问题所在。如果您无法弄明白,请使用更具体的信息编辑您的问题,我会尝试提供更好的指示。
编辑: 这是一个小例子,显示数独游戏的对象序列化:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class SerializationExample {
public static void main(String args[]) throws IOException, ClassNotFoundException {
final File target = new File(System.getProperty("java.io.tmp"), "mySerializedObject.txt");
Map<Integer, Integer> initialState = new HashMap<Integer, Integer>();
initialState.put(1, 1);
initialState.put(21, 3);
// ...
GameState state = new GameState(10, initialState);
state.setField(2, 2);
state.setField(3, 8);
System.out.println("Game state before writing to file: " + state);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(target));
out.writeObject(state);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(target));
Object gameStateReadFromFile = in.readObject();
GameState readGameState = (GameState)gameStateReadFromFile;
System.out.println("Read from file: " + readGameState);
}
private static class GameState implements Serializable {
private int[] fields;
private int boardSize;
private int[] fixedFields;
public GameState(int boardSize, Map<Integer, Integer> initialState) {
this.boardSize = boardSize;
this.fields = new int[boardSize * boardSize];
this.fixedFields = new int[this.fields.length];
for (Entry<Integer, Integer> entry : initialState.entrySet()) {
this.fixedFields[entry.getKey()] = entry.getValue();
}
}
public void setField(int index, int value) {
this.fields[index] = value;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("\nFixed fields: ");
appendArray(builder, this.fixedFields);
builder.append("\nSet fields: ");
appendArray(builder, this.fields);
return builder.toString();
}
private void appendArray(StringBuilder builder, int[] fieldArray) {
for (int i = 0; i < fieldArray.length; ++i) {
if (fieldArray[i] != 0) {
builder.append("row ").append(i / this.boardSize).append(" column ").append(i % this.boardSize)
.append(" has value ")
.append(fieldArray[i]).append(",");
}
}
}
}
}