Loading partial sudoku text file into 2D array - java

I am making a Sudoku game and I have an issue with opening a saved Sudoku file. Let's say I am doing the puzzle, and want to come back to it later, I save the partially completed puzzle to a file, which works. When I go to open it, it does not work.
Here is the code for save (both variables ROWS and COLUMNS are equal to 9):
private void savePuzzle() throws IOException {
JFileChooser fileChooser = new JFileChooser();
int returnVal = fileChooser.showDialog(this, "Save");
BufferedWriter saveFile = null;
File file;
// If the user has canceled, no need to continue
if (returnVal != JFileChooser.APPROVE_OPTION) {
return;
}
file = fileChooser.getSelectedFile();
saveFile = new BufferedWriter(new FileWriter(file));
for (int r = 0; r < ROWS; r++) {
for (int c = 0; c < COLUMNS; c++) {
saveFile.write(inputBoxes[r][c].getText());
if (c < ROWS - 1) {
saveFile.write(",");
}
}
saveFile.write("\n");
}
saveFile.close();
}
Here is the action button for save:
saveAction.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
try {
// call method to save puzzle by writing its contents to an external save file
savePuzzle();
} catch (IOException ex) {
System.out.println(ex.toString());
}
}
});
Here is the code for open:
private void openPuzzle() throws FileNotFoundException, IllegalArgumentException {
JFileChooser fileChooser = new JFileChooser();
int returnVal = fileChooser.showDialog(this, "Open");
File file = fileChooser.getSelectedFile();
Scanner readFile = new Scanner(file);
// If the user has canceled, no need to continue with open process
if (returnVal != JFileChooser.APPROVE_OPTION) {
return;
}
// Row
int r = 0;
// Update squares with data from file
while (readFile.hasNextLine()) {
String[] splitLine = readFile.nextLine().split(",");
// Verify the length of the row
if (splitLine.length != 9) {
throw new IllegalArgumentException(String.format("Row length(%d) not correct in %s at row %d",
splitLine.length, file, r));
}
for (int c = 0; c < 9; c++) {
// Verify each item in row
if (splitLine[c].length() != 1 || !(Character.isDigit(splitLine[c].charAt(0)))) {
throw new IllegalArgumentException(String.format("Invalid token %s in %s at row %d col %d",
splitLine[c], file, r, c));
}
// Update square
inputBoxes[r][c].setText(splitLine[c]);
}
// Move to next row
r++;
}
}
And the open action button:
openAction.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
try {
// call method so that user can open their saved puzzle and populate the grid with its contents
openPuzzle();
} catch (FileNotFoundException ex) {
System.out.println(ex.toString());
} catch (IllegalArgumentException ex) {
System.out.println(ex.toString());
}
}
});
I know that the open code is finding empty values in the saved text file if it is a partially completed puzzle, and it is returning those catch statements, but I have no clue how to get it to just keep reading each element on the line (whether it's a number or empty space), and just return what's in the text file to the GUI grid (populate the grid with the corresponding numbers).
The errors I'm getting are either the "Row length %d..." error or the "Invalid token %s..." error. It loads nothing back into the grid when I select the file from the Open function. That's where I'm lost. If the puzzle is complete, and it is saved, then it can be open, because the external text file that contains the puzzle is full, and each element (space) in the file has a number in it, so when it iterates over each number, it does not find any errors or missing numbers. But when a partial file is saved, it cannot be opened back into the grid to start playing it again...

Your representation of a (sample) row is this:
1,2,,7,6,,,,5
But when you split that line on ,, you will get:
[1,2,7,6,5]
This is clearly not an array of length 9, so if (splitLine.length != 9) will return false. You must save a non-digit character to the text file that indicates an 'empty' space, or the split function will very rarely return a row of the correct length. It is possible to represent an entire Sodoku board with a single list of 81 characters, the digits 0 through 9 and an 'empty' character, no commas or line breaks needed. The key here is that the empty character is still required to maintain the topology of your data structure.
As a side note this:
if (c < ROWS - 1) {
saveFile.write(",");
}
Should probable reference COLS instead.

From reading your code it would seem to me that the offending line is:
if (splitLine[c].length() != 1 || !(Character.isDigit(splitLine[c].charAt(0)))) {
throw new IllegalArgumentException(...
In the function openPuzzle. This is because when saving a partially completed sudoku your file will look like "1,2,,3,,1,6,7,....." Now the empty cells when read in will have a string length of 0. So splitLine[c].length() != 1 will fail. To solve this i would suggest changing the code to be:
if (splitLine[c].length() > 0 || !(Character.isDigit(...
So that zero length characters (unfilled spaces) are accepted when loading it in.

Related

How can I get Java to read all text in file?

I am trying to get Java to read text from a file so that I can convert the text into a series of ascii values, but currently it only seems to be reading and retrieving the first line of the txt file. I know this because the output is much shorter than the text in the file.
The text in the file is below:
AD Mullin Sep 2014 https://hellopoetry.com/poem/872466/prime/
Prime
Have you ever thought deeply about Prime numbers?
We normally think of prime as something unbreachable
In base ten this is most likely true
But there are other languages that might be used to break down numbers
I'm no theorist but I have my theories
What was behind the Big Bang?
Prime
If impermeable ... then the Big Bang never happened
And any good programmer worth a lick of salt, always leaves a back door
So, I bet there are some Prime numbers out there that are permeable, otherwise ...
We wouldn't be the Children of the Big Bang
I think because each line of text has an empty line between them the program is only reading the first line then stopping when it sees there is no line after it, but in facts 2 lines down instead.
Here is the code I have written:
package poetry;
import java.io.FileNotFoundException;
import java.util.Formatter;
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import java.io.FileWriter;
public class poetry {
public static void main(String[] args) {
// TODO Auto-generated method stub
//Below try catch block reads file text and encodes it.
try {
File x = new File("/Users/jordanbendon/Desktop/poem.txt");
Scanner sc = new Scanner(x);
//Right below is where I think the issue lies!
while(sc.hasNextLine()) {
String lines = sc.nextLine();
char[] stringArray = lines.toCharArray();
String result = "";
for (int i = 0; i < lines.length(); i++) {
int ascii = lines.codePointAt(i);
if ((ascii >= 65 && ascii <= 90) || (ascii >= 97 && ascii <= 122)) {
ascii += 15;
result += Integer.toString(ascii);
} else {
result += stringArray[i];
}
}
System.out.println(result);
//Try catch block here creates a new file.
try {
File myObj = new File("/Users/jordanbendon/Desktop/EncryptedMessage.txt");
File s = myObj;
if (myObj.createNewFile()) {
System.out.println("File created: " + myObj.getName());
} else {
System.out.println("File already exists.");
break;
}
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
//Try catch block here writes the new encrypted code to the newly created file.
try {
FileWriter myWriter = new FileWriter("/Users/jordanbendon/Desktop/EncryptedMessage.txt");
myWriter.write(result);
myWriter.close();
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}}
catch(FileNotFoundException e) {
System.out.println("error");
}
}
}
I have commented in the code where I think the issue is. The first while condition checks whether there is a next line by using the hasNextLine(), I have tried using the method ReadAllLines() but it says this method is undefined for the type scanner.
How can I get the program to read and retrieve the entire text file instead of the first line?
Thanks!
To read the entire input stream:
Scanner sc = new Scanner(x).useDelimiter("\\A");
then just:
String entireInput = sc.next();
This works by setting the token delimiter to start of all input, which of course is never encountered after any byte read, so the "next" token is the entire input.
For each execution you check whether the hard coded file name was created or already exists. In case it already existed you happen to break the loop which halts the execution from progressing.
https://www.javatpoint.com/java-break

How to update txt file in java

I have JTable where I show data from text file:
Now, for deleting I have method like this:
private void delete(ActionEvent evt) {
DefaultTableModel model = (DefaultTableModel) tblRooms.getModel();
// get selected row index
try {
int SelectedRowIndex = tblRooms.getSelectedRow();
model.removeRow(SelectedRowIndex);
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex);
}
}
And action listener:
btnDelete.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
delete(e);
}
});
It will delete row in JTable and thats okey, but my text file have 7 splits, where last spit is for logical deleting. So, if false - room isn't deleted.
13|family room|name apartman|4|true|true|true|false
14|superior room|super room|2|true|false|false|false
15|room|room for one|1|false|false|true|false
0|MisteryRoom|Mistery|0|true|true|free|false
How to delete certain room from JTable on correct way, and change from false to true?
For example if I click on super room, how to delete exactly that room.
This sort of thing is best handled using a database rather than a text file for a great number of reasons, never the less since you are utilizing a text file as your data storage I will demonstrate one way to replace a value (substring) in a specific data text file line.
Now, the following method can be used to modify any piece of field data on any file data line...even the room number so keep that in mind. You will need to ensure that you only modify when it's best to do so:
/**
* Updates the supplied Room Number data within a data text file. Even the
* Room Number can be modified.
*
* #param filePath (String) The full path and file name of the Data File.
*
* #param roomNumber (Integer - int) The room number to modify data for.
*
* #param fieldToModify (Integer - int) The field number in the data line to
* apply a new value to. The value supplied here is to be considered 0 based
* meaning that 0 actually means column 1 (room number) within the file data
* line. A value of 7 would be considered column 8 (the deleted flag).
*
* #param newFieldValue (String) Since the file is string based any new field
* value should be supplied as String. So to apply a boolean true you will need
* to supply "true" (in quotation marks) and to supply a new room number that
* room number must be supplied a String (ie: "666").
*
* #return (Boolean) True if successful and false if not.
*/
public boolean updateRoomDataInFile(String filePath, int roomNumber,
int fieldToModify, String newFieldValue) {
// Try with resources so as to auto close the BufferedReader.
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
// Add the data file contents to a List interface...
List<String> dataList = new ArrayList<>();
while ((line = reader.readLine()) != null) {
dataList.add(line);
}
for (int i = 0; i < dataList.size(); i++) {
line = dataList.get(i).trim(); // Trim off any leading or trailing whitespaces (if any).
// Skip Blank lines (if any) and skip Comment lines (if any).
// In this example file comment lines start with a semicolon.
if (line.equals("") || line.startsWith(";")) {
continue;
}
//Split each read line so as to collect the desired room number
// since everything will always be based from this unique ID number.
// Split is done baesed on the Pipe (|) character since this is
// what is implied with your data example.
String[] roomData = line.split("\\|");
// Get the current file data line room number.
// Make sure the first piece of data is indeed a valid integer room
// number. We use the String.matches() method for this along with a
// regular expression.
if (!roomData[0].trim().matches("\\d+")) {
// If not then inform User and move on.
JOptionPane.showMessageDialog(null, "Invalid room number detected on file line: "
+ (i + 1), "Invalid Room Number", JOptionPane.WARNING_MESSAGE);
continue;
}
// Convert the current data line room number to Integer
int roomNum = Integer.parseInt(roomData[0]);
// Does the current data line room number equal the supplied
// room number?
if (roomNum != roomNumber) {
// If not then move on...
continue;
}
// If we reach this point then we know that we are currently on
// the the data line we need and want to make changes to.
String strg = ""; // Use for building a modified data line.
// Iterate through the current data line fields
for (int j = 0; j < roomData.length; j++) {
// If we reach the supplied field number to modify
// then we apply that modification to the field.
if (j == fieldToModify) {
roomData[j] = newFieldValue;
}
// Build the new data line. We use a Ternary Operator, it is
// basicaly the same as using a IF/ELSE.
strg += strg.equals("") ? roomData[j] : "|" + roomData[j];
}
// Replace the current List element with the modified data.
dataList.set(i, strg);
}
// Rewrite the Data File.
// Try with resources so as to auto close the FileWriter.
try (FileWriter writer = new FileWriter(filePath)) {
// Iterate through the List and write it to the data file.
// This ultimately overwrites the data file.
for (int i = 0; i < dataList.size(); i++) {
writer.write(dataList.get(i) + System.lineSeparator());
}
}
// Since no exceptions have been caught at this point return true
// for success.
return true;
}
catch (FileNotFoundException ex) {
Logger.getLogger("updateFileRoomStatus()").log(Level.SEVERE, null, ex);
}
catch (IOException ex) {
Logger.getLogger("updateFileRoomStatus()").log(Level.SEVERE, null, ex);
}
// We must of hit an exception if we got
// here so return false for failure.
return false;
}
To use this method you might want to do it this way:
private void delete() {
DefaultTableModel model = (DefaultTableModel) tblRooms.getModel();
try {
// get selected row index
int SelectedRowIndex = tblRooms.getSelectedRow();
// Get out if nothing was selected but the button was.
if (SelectedRowIndex == -1) { return; }
int roomNumber = Integer.parseInt(model.getValueAt(SelectedRowIndex, 0).toString());
updateRoomDataInFile("HotelRoomsData.txt", roomNumber, 7, "true");
model.removeRow(SelectedRowIndex);
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex);
}
In the code above a data file name of "HotelRoomsData.txt" was supplied. This of course assumes the the data file contains that name and that is is located within the root folder (directory) of your particular project. If the file is named differently and it is located in some completely different location then you will need to change this to the full path and file name of the data file, for example:
"C:/Users/Documents/MyDataFile.txt"
The code really isn't that long, it's just that there are a lot of comments accompanying it so as to explain things. Of course these comments can be deleted from the code.

Limit Android Filesize

Background
I'm keeping a relatively large text file in android storage, and appending to it periodically- while limiting the file's size to some arbitrary size (say 2MB)
Hopefully I'm missing a function somewhere, or hopefully there is a better way to do this process.
Currently, when the file a goes over that arbitrary size, I create a temporary file b, copy the relevant portion of the file a (more or less the substring of the file a starting at byte xxx where xxx is the number of bytes too large the file a would be if I wrote the next bit of data to the log) plus the current data, then overwrite the file a with the second file b.
This is obviously terribly inefficient...
Another solution that I'm not terribly fond of is to keep two files, and toggle between the two of them, clearing the next when the current is full, and switching to that file for output.
However, it would be suuuuuper handy if I could just do something like this
File A = new File("output");
A.chip(500);
or maybe
A.subfile(500,A.length()-500);
TLDR;
Is there a function or perhaps library available for Android that can remove a portion of a file?
Did you already take a look at RandomAccessFile? Though you cannot remove portions of a file you can seek any position within the file and even set the length. So if you detect your file grows too large, just grab the relevant portion and jump to the beginning. Set length to 0 and write the new data.
EDIT:
I wrote a small demo. It shows if the file size is limeted to 10 bytes. If you pass in the values 10 to 15 as strings and separate them with commas, after 10,11,12, the file is written from the beginning, so after 15 it reads 13,14,15
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final long MAX = 10;
private static final String FILE_TXT = "file.txt";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
for (int i = 10; i <= 15; i++) {
if (i > 10) {
writeToFile(",");
}
writeToFile(Integer.toString(i));
}
}
private void writeToFile(String text) {
try {
File f = new File(getFilesDir(), FILE_TXT);
RandomAccessFile file = new RandomAccessFile(f, "rw");
long currentLength = file.length();
if (currentLength + text.length() > MAX) {
file.setLength(0);
}
file.seek(file.length());
file.write(text.getBytes());
file.close();
} catch (IOException e) {
Log.e(TAG, "writeToFile()", e);
}
printFileContents();
}
private void printFileContents() {
StringBuilder sb = new StringBuilder();
try {
FileInputStream fin = openFileInput(FILE_TXT);
int ch;
while ((ch = fin.read()) != -1) {
sb.append((char) ch);
}
fin.close();
} catch (IOException e) {
Log.e(TAG, "printFileContents()", e);
}
Log.d(TAG, "current content: " + sb.toString());
}
}

Writting Duplicate Object data to a binary file

I'm going to try to keep it as simple as possible. I have a Program that adds values to the table. When a button is clicked it reads values from the table, creates an object and writes to a binary file(.ser).
When I loop trough the list (e.g. 3 times if i added three object values to the table) of the table, then I create an object and write it to a file. But when I read the same file it just reads the same object ( 3 times if I added 3 objects to the Table). Why is it doing that? I'm new to Java any help would be great thanks
Object Structure
public class Empleados implements Serializable {
public int NoEmpleado;
public String ENombre;
public String EApellido;
public String EGrupo;
.
.
Setters and getters
}
Following code runs when I click a button to write to binary file (takes data from a table previously filled with data). I am also closing the file after all of this is done.
try // open file
{
outputS = new ObjectOutputStream(new FileOutputStream( "empleados.ser" ) );
} // end try
catch ( IOException ioException )
{
System.err.println( "Error opening file." );
} // end catch
for (int num = 0; num < tableModel2.getRowCount();num++)
{
try
{
empleado1.setNumero(Integer.parseInt(tableModel2.getValueAt(num, 0).toString()));
empleado1.setNombre(tableModel2.getValueAt(num, 1).toString());
empleado1.setApellido(tableModel2.getValueAt(num, 2).toString());
empleado1.setGrupo(tableModel2.getValueAt(num, 3).toString());
int n = empleado1.getNumero();
if (empleado1.getNumero() > 0)
{
outputS.writeObject(empleado1);
}
else
{
System.out.println("Numero debe ser mayor a 0.");
}
}
.
.
fallow by rest of catch stataments

Java: Sequential sorting names and saving into a Random Access File

I am trying to make a program where I can add a name and it's supposed to be saved in a RandomAccessFile, (alphabetically). Whenever I add a name it gets saved in a file, it's supposed to have at the end the next corresponding position for the next name. I'm having problems with the saving whenever I add a name with starting with A, then I add a name with C, and if I where to add a name starting with B it's not pointing me in the right alphabetical order.
Here is what an example of what the program should do:
I add a name starting with A.
Numbers on "left" side are just where the next name start,
Numbers on "right" side are the pointers to the next name
[0]-----A----[-1] ----------- the "-1" pointer means its the end of the list
I add a name starting with C.
[0]-----A----[100] ------- the "100" pointer means that the next name "C" start on byte 100
[100]---C----[-1] --------- end of the list pointer, notice how A no longer has the "-1" pointer
I add a name starting with B.
[0]-----A----[200] ------ "A" no longer points to 100 because the next letter should be "B"
[100]---C----[-1] -------- -1 still means that "C" is the end of the list pointer
[200]---B----[100] --------- "B" is pointing at "C" because the next letter after
Here is my code so far, but I'm missing the part where a name that belongs in the middle of the list is added.
public boolean add(String name, String lastName, String telf) {
try {
fileSize = file.length();
} catch (IOException ex) {
ex.printStackTrace();
}
if (fileSize == 0) { //must be a new entry
try {
byte entry[] = new byte[sizeEntry]; // size of each entry
file.write(entry);
file.seek(file.getFilePointer() - sizeEntry);
file.writeUTF(name); //name gets saved
file.writeUTF(lastName);// last name gets saved
file.writeUTF(telf); // telf gets saved
file.writeUTF("N"); // deleted "Y" or "N" gets saved
file.writeUTF("-1"); // pointer gets saved
} catch (IOException e) {
System.out.println("Error at saving....");
e.printStackTrace();
}
} else {
pPresent= 0; //variable for the pointer reading
pPrevious= 0; // variable for the pointer read
try {
file.seek(0); //start reading at the top
do {
pPresent= file.getFilePointer();//saves present pointer
file.seek(pPresent);//seeks to present pointer
nameRead = file.readUTF(); //reads name
file.readUTF(); //reads last name
file.readUTF(); //reads telf
file.readUTF(); //reads deleted?
pNext= Long.parseLong(file.readUTF()); // reads the next pointer
int comparison= name.compareTo(nameRead);
if (comparison< 0) {
//enters here if the new entry goes before the present entry
if (pNext!= -1) {
file.seek(pNext);//enters here if pointer is not at end of list
} else {
try {// proceeds to writing a new entry
file.seek(file.length()); //goes to the end of the file
byte entry[] = new byte[sizeEntry];
file.write(entry);
file.seek(file.getFilePointer() - sizeEntry);
file.writeUTF(name);
file.writeUTF(lastname);
file.writeUTF(telf);
file.writeUTF("N");
file.writeUTF(Long.toString(pPrevious));//writes the previous pointer
file.seek(pPrevious);//seeks to the previous entry
file.readUTF();//reads name
file.readUTF();//reads lastname
file.readUTF();//reads telf
file.readUTF();//reads deleted?
file.writeUTF(Long.toString(pPrevious));//overwrites the new previous
} catch (IOException e) {
System.out.println("Error at saving...");
e.printStackTrace();
}
break;//exits
}
} else {//enteres here if the entry is bigger than the present
if (pNext!= -1) {
file.seek(pNext);
} else {
try {
pPresent= file.length()-sizeEntry;//saves present entry
file.seek(pPrevious); //seeks to previous entry
file.readUTF();//reads name
file.readUTF();//reads last name
file.readUTF();//reads telf
file.readUTF();//reads deleted
file.writeUTF(Long.toString(pPresent+100));//overwrites the next pointer
file.seek(file.length());//seeks at the end
byte entry[] = new byte[sizeEntry];//creates a new entry
file.write(entry);
file.seek(file.getFilePointer() - sizeEntry);
file.writeUTF(name);//writes name
file.writeUTF(lastname);//writes lastname
file.writeUTF(telf);//writes telf
file.writeUTF("N");//writes deleted
file.writeUTF(Long.toString(pNext));//writes next pointer
} catch (IOException e) {
System.out.println("Error at saving...");
e.printStackTrace();
}
break;//exits
}
}
pPresent= file.getFilePointer();//present pointer read
pPrevious= pPresent;//present pointer becomes previous
} while (true);
} catch (IOException e) {
System.out.println("Error at saving....");
e.printStackTrace();
}
}
return false;
}
I hope you guys understand the idea of the program a little better now with the source code. The part I don't know how to do is where I add a entry that belongs in the middle of the list. Remember that the order of the names dont change only the pointers pointing to the next do.
Finding the insertion point will require traversing the list, which in turn requires a disk access for every single name. Assuming you have 1 million names, and a typical disk access time of 10 ms, inserting a single name would take about 3 hours. Put differently, linked lists are an extremly unsuitable data structure for storing data on disk.
A reasonable data structure such as a B-Tree would permit lookup and insertion among 1 million names in as little as 3 disk accesses.
Yo have to develop a method that calculates the pointer, I develop a code a few days ago:
public int getNext(String lastName){
int auxNext=0;
int auxActual=0;
byte[] relleno= new byte[100];
try{
int Next=-1;
while(auxNext!=-1){
auxActual=auxNext;
myRaf.seek(auxNext);
String auxPreviousLastName=myRaf.readUTF();
auxNext=Integer.valueOf(myRaf.readUTF());
if(auxNext!=-1){
myRaf.seek(auxNext);
String auxApellido=myRaf.readUTF();
String aux=myRaf.readUTF();
if(lastName.compareTo(auxLastName)<0){
Next=auxNext;
myRaf.seek(auxActual);
myRaf.write(relleno);
myRaf.seek(auxActual);
myRaf.writeUTF(auxPreviousLastName);
myRaf.writeUTF(String.valueOf(myRaf.length()));
return Next;
}
}else{
updateEnds();
return -1;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return -1;
}
public void updateEnds(){ //This method search the last register, and updates that reference
byte[] relleno= new byte[100];
try {
for (int i = 0; i < myRaf.length(); i+=100) {
myRaf.seek(i);
String auxLastName=myRaf.readUTF();
String next=myRaf.readUTF();
if (next.equals("-1")) {
myRaf.seek(i);
myRaf.write(relleno);
myRaf.seek(i);
myRaf.writeUTF(auxLastName);
myRaf.writeUTF(String.valueOf(myRaf.length()));
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
PD, Sorry, I don't write in english as well as you.

Categories

Resources