My task is to read a text file in chunks of 64 characters, and use 2 different processes called Substitution and Column Transposition to encrypt it. Then, I have to decrypt it and write it out to another file.
I have written and tested out both processes of encrypting and decrypting and it worked wonderfully. But then I tried to loop the processes in case more than 64 characters were in the input file.
As a test case, I tried a 128 character input file. Unfortunately, the result only gives me the first 64 characters twice. I've tracked the scanner position and it goes beyond 64, but the characters read start back from 0. I'm not sure what the problem is.
Here is the relevant part of my code:
public static void main(String[] args) {
//Declare variables
Scanner console = new Scanner(System.in);
String inputFileName = null;
File inputFile = null;
Scanner in = null;
do
{
//Check if there are enough arguments
try
{
inputFileName = args[1];
}
catch (IndexOutOfBoundsException exception)
{
System.out.println("Not enough arguments.");
System.exit(1);
}
catch (Exception exception)
{
System.out.println("There was an error. Please try again.");
System.exit(1);
}
//Check if Input File is valid
try
{
inputFile = new File(inputFileName);
in = new Scanner(inputFile);
outputFile = new File(outputFileName);
out = new Scanner(outputFile);
}
catch (FileNotFoundException exception)
{
System.out.println("Could not find input file.");
System.exit(1);
}
catch (Exception exception)
{
System.out.println("There was an error. Please try again.");
System.exit(1);
}
} while (outputFileName != null && !inputFile.exists());
//Encryption
//Prepare patterns
String subPattern = CreateSubstitutionPattern(hash);
int[] transPattern = CreateTranspositionPattern(hash);
//Apply patterns
String textContent = "";
String applySub = "";
String applyTrans = "";
do
{
textContent = Read64Chars(in);
applySub = applySub + ApplySubstitutionPattern(textContent, subPattern);
applyTrans = applyTrans + ApplyTranspositionPattern(applySub, transPattern);
} while (in.hasNext());
//Decryption
String encryptContent = "";
Scanner encrypt = new Scanner(applyTrans);
String removeTrans = "";
String removeSub = "";
do
{
encryptContent = Read64Chars(encrypt);
System.out.println(applyTrans);
removeTrans = removeTrans + RemoveTranspositionPattern(encryptContent, transPattern);
removeSub = removeSub + RemoveSubstitutionPattern(removeTrans, subPattern);
} while (encrypt.hasNext());
console.close();
in.close();
encrypt.close();
System.out.println(removeSub); //For temporary testing
}
public static String Read64Chars (Scanner in)
{
String textContent = "";
in.useDelimiter("");
for (int x=0; x<64; x++)
{
if (in.hasNext())
{
textContent = textContent + in.next().charAt(0);
}
}
return textContent;
}
Do note that I have more variables to fill in args[0] and args[2] but I removed them for simplicity.
I would like to know if it is true that once a scanner reads a portion of it's input, it "consumes" it, and that portion gets removed. Does the scanner reset itself when declared again through a method? For example, does the declaration only point to the input source of the original scanner, or the actual scanner with its current properties?
encrypt is a diffrent Scanner from in, which you advance by 64 characters when you first call Read64Chars. So, encrypt starts at the first character when you call Read64Chars(encrypt). It seems like you want to use the same Scanner both times.
Also, in the future please name your functions starting with a lowercase letter. I felt dirty typing that... :)
A proper solution to get the whole encrypted text would be a code like this
public static String encryptedTextFile (Scanner in)
{
//ArrayList<String> stringBlocksOf64Chars = new ArrayList<String>();
StringBuilder encryptedTxt = new StringBuilder();
String currentTxt = "";
while (in.hasNextLine()) {
String line = currentTxt + in.nextLine();
currentTxt = "";
int i = 0;
for( ; i < line.length()/64 ; i++){
currentTxt = line.substring(i * 64, (i+1)*64);
//TODO - encrypt the text before adding it to the list
encryptedTxt.append(currentTxt);//encryptedTxt.append(encrypt(currentTxt));
}
currentTxt = line.substring(i * 64, line.length());
}
encryptedTxt.append(currentTxt);
/*for(String str : stringBlocksOf64Chars)
System.out.println(str);*/
return encryptedTxt.toString();
}
Your loop for (int x=0; x<64; x++) makes sure that you read only first 64 characters always and not the complete file. To get around that you should actually read whole file line by line.
The above code block follows this idea.
Steps to break down the logic.
Read the file line by line using scanner.
Break each line into chunks of 64 characters and encrypt the block 64 characters at a time
Generate encrypted text adding the encrypted 64 characters.
Whatever you do first break down the logic/steps you want to use in your code to make it simpler to understand or code.
Break the lines into 64 characters
Related
I have class Account which have username, fullName, password, id and points.
All accounts are saved in a file.I have many accounts in my file, not just one. This is example of one account in text file.
Miljan9602 Rakita Miljan miljan123 1463433398614 0.0
username, full name, password, id and points
Now, for example if i want to change points for my username. First thing i would do is go through all lines in file and compare all usernames, if i find equal username. I would change point's. This is my idea how to do it. Just dont know how to edit it in file.
public void edit(String username, double points)
{
File f = new File("Accounts.txt");
// file doesnt exist, return from method
if(!f.exists())
return;
Scanner sc = null;
try
{
sc = new Scanner(f);
while(sc.hasNextLine())
{
String line = sc.nextLine(); // Take whole line
String split[] = line.split(" "); // Split it so i can check username
if(split[0].equals(username))
{
String change = Double.toString(points); // Make string from double
split[5] = change; // on fifth index are points
/* My question is now how to edit file and to replace my new points
* with old points ?
* Miljan9602 Rakita Miljan miljan123 1463433398614 0.0 <- Need to change this 0.0 with split[4];
*/
}
}
}catch(Exception e)
{
e.printStackTrace();
}finally{
// finally will always close file
sc.close();
}
You could use the Apache's Commons IO library. Everything you'll need, and more, can be found there. Also, here is the GitHub mirror of Commons IO. Worth a look through.
{
File f = new File("Accounts.txt");
FileWriter fw = new FileWriter(f);
// file doesnt exist, return from method
if(!f.exists())
return;
Scanner sc = null;
try
{
sc = new Scanner(f);
while(sc.hasNextLine())
{
String line = sc.nextLine(); // Take whole line
String split[] = line.split(" "); // Split it so i can check username
if(split[0].equals(username))
{
String change = Double.toString(points); // Make string from double
split[5] = change; // on fifth index are points
/* My question is now how to edit file and to replace my new points
* with old points ?
* Miljan9602 Rakita Miljan miljan123 1463433398614 0.0 <- Need to change this 0.0 with split[4];
*/
for(int i = 0; i < spit.length(); i++{
fw.write(split[i] + " ");
}
System.getProperty("line.separator");
}
}
}catch(Exception e)
{
e.printStackTrace();
}finally{
// finally will always close file
sc.close();
fw.close();
}
This should work
As one has to write the entire read text back to the file system, use Files.readAllLines().
Path path = Paths.get(".../Accounts.txt");
Charset charset = StandardCharsets.UTF_8;
List<String> lines = new ArrayList<>();
if (Files.exists()) {
Files.readAllLines(path, charset);
for (int i = 0; i < lines.size(); ++i) {
String split[] = lines.get(i).split(" ");
if (split[0].equals(username)) {
String change = String.valueOf(points);
split[5] = change; // on fifth index are points
StringBuilder sb = new StringBuilder();
for (String value : split) {
if (sb.length() != 0) {
sb.append(' ');
}
sb.append(value);
}
lines.set(i, sb.toString()); // Changes the line.
Files.write(path, lines, charset);
break; // leave loop
}
}
}
More explained
To alter a single line of a text file, one in principle has to load the entire text and after altering the line, safe it entirely.
The reason is that the file can shrink or grow, depending on the line changes.
Even with some twists this is not optimal.
Files.readAllLines is a nice method for that. One might also change the format:
Fixed length records (lines) allow a RandomAccessFile. However a text risks being manually edited so the file gets corrupted, and one also has limited field lengths.
The .properties format allows access with the Properties class. Requirement is a key, and a format key = value. Also the text has some escaping (\).
One could keep Accounts.txt in core, say in a class Accounts, representing all as a Map from user name to Account.
class Account {
public final String userName; // Unmodifiable key
public String password;
...
}
class Accounts {
private Map<String, Account> accountsByUserName = new HashMap<>();
public void loadAccounts() throws IOException { ... }
public void saveAccounts() throws IOException { ... }
public Optional<Account> getAccountByUserName(String userName) { ... }
public void deleteAccountByUserName(String userName) { ... }
public void createAccount(Account account) throws AlreadyExistsException { ... }
}
The scanner reads the wrong data, the text file format is:
111,Smith,Sam, 40,10.50
330,Jones,Jennifer,30,10.00
The program is:
public class P3 {
public static void main(String[] args) {
String file=args[0];
File fileName = new File(file);
try {
Scanner sc = new Scanner(fileName).useDelimiter(", ");
while (sc.hasNextLine()) {
if (sc.hasNextInt( ) ){ int id = sc.nextInt();}
String lastName = sc.next();
String firstName = sc.next();
if (sc.hasNextInt( ) ){ int hours = sc.nextInt(); }
if (sc.hasNextFloat()){ float payRate=sc.nextFloat(); }
System.out.println(firstName);
}
sc.close();
} catch(FileNotFoundException e) {
System.out.println("Can't open file "
+ fileName + " ");
}
}
}
The output is:
40,10.50
330,Jones,Jennifer,30,10.00
It is supposed to be:
Sam
Jennifer
How do I fix it?
The problem is that your data isn't just delimited by commas. It is also delimited by line-endings, and also by Unicode character U+FF0C (FULLWIDTH COMMA).
I took your code, replaced the line
Scanner sc = new Scanner(fileName).useDelimiter(", ");
with
Scanner sc = new Scanner(fileName, "UTF-8").useDelimiter(", |\r\n|\n|\uff0c");
and then ran it. It produced the output it was supposed to.
The text , |\r\n|\n|\uff0c is a regular expression that matches either:
a comma followed by a space,
a carriage-return (\r) followed by a newline (\n),
a newline on its own,
a Unicode full-width comma (\uff0c).
These are the characters we want to delimit the text by. I've specified both types of line-ending as I'm not sure which line-endings your file uses.
I've also set the scanner to use the UTF-8 encoding when reading from the file. I don't know whether that will make a difference for you, but on my system UTF-8 isn't the default encoding so I needed to specify it.
First, please swap fileName and file. Next, I suggest you use a try-with-resources. Your variables need to be at a common scope if you intend to use them. Finally, when using hasNextLine() I would then call nextLine and you can split on optional white space and comma. That could look something like
String fileName = // ...
File file = new File(fileName);
try (Scanner sc = new Scanner(file)) {
while (sc.hasNextLine()) {
String line = sc.nextLine();
String[] arr = line.split("\\s*,\\s*");
int id = Integer.parseInt(arr[0]);
String lastName = arr[1];
String firstName = arr[2];
int hours = Integer.parseInt(arr[3]);
float payRate = Float.parseFloat(arr[4]);
System.out.println(firstName);
}
} catch (FileNotFoundException e) {
System.out.println("Can't open file " + fileName + " ");
e.printStackTrace();
}
I tried to do counting lines, words, character from user "inputted" file.
After this show counting and keep asking again.
If file doesn't exist print all data which have been counted during running.
Code:
public class KeepAskingApp {
private static int lines;
private static int words;
private static int chars;
public static void main(String[] args) {
boolean done = false;
//counters
int charsCount = 0, wordsCount = 0, linesCount = 0;
Scanner in = null;
Scanner scanner = null;
while (!done) {
try {
in = new Scanner(System.in);
System.out.print("Enter a (next) file name: ");
String input = in.nextLine();
scanner = new Scanner(new File(input));
while(scanner.hasNextLine()) {
lines += linesCount++;
Scanner lineScanner = new Scanner(scanner.nextLine());
lineScanner.useDelimiter(" ");
while(lineScanner.hasNext()) {
words += wordsCount++;
chars += charsCount += lineScanner.next().length();
}
System.out.printf("# of chars: %d\n# of words: %d\n# of lines: ",
charsCount, wordsCount, charsCount);
lineScanner.close();
}
scanner.close();
in.close();
} catch (FileNotFoundException e) {
System.out.printf("All lines: %d\nAll words: %d\nAll chars: %d\n",
lines, words, chars);
System.out.println("The end");
done = true;
}
}
}
}
But I can't understand why it always show output with no parameters:
All lines: 0
All words: 0
All chars: 0
The end
Why it omits all internal part.
It may be coz I'm using few scanners, but all look ok.
Any suggestions?
UPDATE:
Thanks all who give some hint. I rethinking all constructed and rewrite code with newly info.
To awoid tricky scanner input line, I used JFileChooser:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import javax.swing.JFileChooser;
public class KeepAskingApp {
private static int lines;
private static int words;
private static int chars;
public static void main(String[] args) {
boolean done = false;
// counters
int charsCount = 0, wordsCount = 0, linesCount = 0;
Scanner in = null;
Scanner lineScanner = null;
File selectedFile = null;
while (!done) {
try {
try {
JFileChooser chooser = new JFileChooser();
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
selectedFile = chooser.getSelectedFile();
in = new Scanner(selectedFile);
}
while (in.hasNextLine()) {
linesCount++;
lineScanner = new Scanner(in.nextLine());
lineScanner.useDelimiter(" ");
while (lineScanner.hasNext()) {
wordsCount++;
charsCount += lineScanner.next().length();
}
}
System.out.printf(
"# of chars: %d\n# of words: %d\n# of lines: %d\n",
charsCount, wordsCount, linesCount);
lineScanner.close();
lines += linesCount;
words += wordsCount;
chars += charsCount;
in.close();
} finally {
System.out.printf(
"\nAll lines: %d\nAll words: %d\nAll chars: %d\n",
lines, words, chars);
System.out.println("The end");
done = true;
}
} catch (FileNotFoundException e) {
System.out.println("Error! File not found.");
}
}
}
}
Couple of issues (actually there are many issues with your code, but I will address the ones directly related to the output you have posted):
First of all, the stuff in the catch block only happens if you get a FileNotFoundException; that's there to handle and recover from errors. I suspect you meant to put a finally block there, or you meant to do that after the catch. I suggest reading this tutorial on catching and handling exceptions, which straightforwardly describes try, catch, and finally.
Once you read that tutorial, come back to your code; you may find that you have a little bit of reorganizing to do.
Second, with the above in mind, it's obvious by the output you are seeing that you are executing the code in that catch block, which means you are getting a FileNotFoundException. This would be caused by one of two (possibly obvious) things:
The file you entered, well, wasn't found. It may not exist or it may not be where you expect. Check to make sure you are entering the correct filename and that the file actually exists.
The input string is not what you expect. Perhaps you read a blank line from previous input, etc.
Addressing reason 2: If there is already a newline on the input buffer for whatever reason, you will read a blank line with Scanner. You might want to print the value of input just before opening the file to make sure it's what you expect.
If you're seeing blank lines, just skip them. So, instead of this:
String input = in.nextLine();
scanner = new Scanner(new File(input));
Something like this instead would be immune to blank lines:
String input;
do {
input = in.nextLine().trim(); // remove stray leading/trailing whitespace
} while (input.isEmpty()); // keep asking for input if a blank line is read
scanner = new Scanner(new File(input));
And, finally, I think you can work out the reason that you're seeing 0's in your output. When you attempt to open the file with new Scanner(new File(input)); and it fails because it can't find the file, it throws an exception and the program immediately jumps to the code in your catch block. That means lines, words, and chars still have their initial value of zero (all code that modifies them was skipped).
Hope that helps.
Your println()s are in a catch block
} catch (FileNotFoundException e) {
System.out.printf("All lines: %d\nAll words: %d\nAll chars: %d\n",
lines, words, chars);
System.out.println("The end");
done = true;
}
That means you caught a FileNotFoundException. I think you can figure out from here.
I'm looking to read in a text file in my app but I need to read it in word by word rather than line by line. I'm used to Java and the Scanner class with the hasNext() method so I am currently using that. Here is my code:
try
{
FileInputStream fis = openFileInput(FILENAME);
Scanner infile = new Scanner(new InputStreamReader(fis));
while(infile.hasNext())
{
accName = infile.next();
uName = infile.next();
pWord = infile.next();
((ArrayHandler)getApplicationContext()).info.add(new Information(accName, uName, pWord));
for( int i = 0; i < ((ArrayHandler)getApplicationContext()).info.size(); i++)
{
}
}
}
catch(Exception err)
{
Toast.makeText(getBaseContext(), "Could Not Read File", Toast.LENGTH_LONG).show();
}
If there is another way that either better please let me know. Also, this file is not created until the app is actually used, so it'll throw the error first and then after onDestroy() occurs and the user reloads the information it will read it.
EDIT
Sorry for the non responsive reply until now. But the method shown above is actually 100% working. So for those looking to read a file in word by word and assign them (like above) this code works without any issue.
You can traverse line by line and pick up the lines matching a pattern (three words with spaces):
Pattern p = Pattern.compile("(\\w+)\\s+(\\w+)\\s+(\\w+)");
Scanner sc = null;
try {
sc = new Scanner(getAssets().open("test.txt"));
while (sc.hasNextLine()){
Matcher m = p.matcher(sc.nextLine());
if(m.find()){
Log.i("TESTING","Account:"+m.group(1)+", User:"+m.group(2)+", Password:"+m.group(3));
}
}
} catch (IOException e) {
e.printStackTrace();
}
I need to have this file print to an array, not to screen.And yes, I MUST use an array - School Project - I'm very new to java so any help is appreciated. Any ideas? thanks
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
public class HangmanProject
{
public static void main(String[] args) throws FileNotFoundException
{
String scoreKeeper; // to keep track of score
int guessesLeft; // to keep track of guesses remaining
String wordList[]; // array to store words
Scanner keyboard = new Scanner(System.in); // to read user's input
System.out.println("Welcome to Hangman Project!");
// Create a scanner to read the secret words file
Scanner wordScan = null;
try {
wordScan = new Scanner(new BufferedReader(new FileReader("words.txt")));
while (wordScan.hasNext()) {
System.out.println(wordScan.next());
}
} finally {
if (wordScan != null) {
wordScan.close();
}
}
}
}
Nick, you just gave us the final piece of the puzzle. If you know the number of lines you will be reading, you can simply define an array of that length before you read the file
Something like...
String[] wordArray = new String[10];
int index = 0;
String word = null; // word to be read from file...
// Use buffered reader to read each line...
wordArray[index] = word;
index++;
Now that example's not going to mean much to be honest, so I did these two examples
The first one uses the concept suggested by Alex, which allows you to read an unknown number of lines from the file.
The only trip up is if the lines are separated by more the one line feed (ie there is a extra line between words)
public static void readUnknownWords() {
// Reference to the words file
File words = new File("Words.txt");
// Use a StringBuilder to buffer the content as it's read from the file
StringBuilder sb = new StringBuilder(128);
BufferedReader reader = null;
try {
// Create the reader. A File reader would be just as fine in this
// example, but hay ;)
reader = new BufferedReader(new FileReader(words));
// The read buffer to use to read data into
char[] buffer = new char[1024];
int bytesRead = -1;
// Read the file to we get to the end
while ((bytesRead = reader.read(buffer)) != -1) {
// Append the results to the string builder
sb.append(buffer, 0, bytesRead);
}
// Split the string builder into individal words by the line break
String[] wordArray = sb.toString().split("\n");
System.out.println("Read " + wordArray.length + " words");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
The second demonstrates how to read the words into an array of known length. This is probably closer to the what you actually want
public static void readKnownWords()
// This is just the same as the previous example, except we
// know in advance the number of lines we will be reading
File words = new File("Words.txt");
BufferedReader reader = null;
try {
// Create the word array of a known quantity
// The quantity value could be defined as a constant
// ie public static final int WORD_COUNT = 10;
String[] wordArray = new String[10];
reader = new BufferedReader(new FileReader(words));
// Instead of reading to a char buffer, we are
// going to take the easy route and read each line
// straight into a String
String text = null;
// The current array index
int index = 0;
// Read the file till we reach the end
// ps- my file had lots more words, so I put a limit
// in the loop to prevent index out of bounds exceptions
while ((text = reader.readLine()) != null && index < 10) {
wordArray[index] = text;
index++;
}
System.out.println("Read " + wordArray.length + " words");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
If you find either of these useful, I would appropriate it you would give me a small up-vote and check Alex's answer as correct, as it's his idea that I've adapted.
Now, if you're really paranoid about which line break to use, you can find the values used by the system via the System.getProperties().getProperty("line.separator") value.
Do you need more help with the reading the file, or getting the String to a parsed array? If you can read the file into a String, simply do:
String[] words = readString.split("\n");
That will split the string at each line break, so assuming this is your text file:
Word1
Word2
Word3
words will be: {word1, word2, word3}
If the words you are reading are stored in each line of the file, you can use the hasNextLine() and nextLine() to read the text one line at a time. Using the next() will also work, since you just need to throw one word in the array, but nextLine() is usually always preferred.
As for only using an array, you have two options:
You either declare a large array, the size of whom you are sure will never be less than the total amount of words;
You go through the file twice, the first time you read the amount of elements, then you initialize the array depending on that value and then, go through it a second time while adding the string as you go by.
It is usually recommended to use a dynamic collection such as an ArrayList(). You can then use the toArray() method to turnt he list into an array.