Java: Multi-line File Read Iterator - java

I am trying to override the next() and nextLine() methods in the LineIterator class (org.apache.commons.io). Basically I want to specify the maximum number of lines to read from the text file for each invocation (default for the base class is of course 1).
Here is the derived class that I have come up with. Unfortunately it throws a StackOverflowError exception.
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import org.apache.commons.io.LineIterator;
public class MultiLineIterator extends LineIterator{
int maxLines = 1;
public static void main(String[] args) throws Exception {
File file = new File ("/path/to/inputfile.txt");
LineIterator iterator = new MultiLineIterator(new FileReader(file), 3);
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public MultiLineIterator(Reader reader, int maxLines) {
super(reader);
this.maxLines = maxLines;
}
#Override
public String next() {
String retVal = null;
if(hasNext()) {
retVal = "";
}
String nextFragment = "";
for(int i = 1; i <= maxLines; i++) {
if(hasNext()) {
nextFragment = super.next();
retVal += (nextFragment + " ");
}
else
break;
}
return retVal;
}
#Override
public String nextLine() {
return next();
}
}

To fix StackOverflowError you should remove:
#Override
public String nextLine() {
return next();
}
, because you have an infinite recursion:
this:next() -> super:next() -> this:nextLine() -> this:next() -> ... and so on
Also I would suggest do not override next() method in this way it leads to inconsistent results.
I would propose to use simple counter. Increment it on next line and check the counter on hasNext :
public class MultiLineIterator extends LineIterator {
private int maxLines = 1;
private int cursor = 0;
public static void main(String[] args) throws Exception {
File file = new File("/path/inputfile.txt");
LineIterator iterator = new MultiLineIterator(new FileReader(file), 3);
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public MultiLineIterator(Reader reader, int maxLines) {
super(reader);
this.maxLines = maxLines;
}
#Override
public boolean hasNext() {
return (cursor < maxLines) && super.hasNext();
}
#Override
public String next() {
String next = super.next();
cursor++;
return next;
}
}
In this implementation we do not concatenate lines but just limit the number.

Related

Why saving the char '�' to a file saves it as '?'?

I learned about Huffman Coding and tried to apply. So I made a very basic text reader that can only open and save files. And wrote a decorator that can be used to compress the text before saving (which uses Huffman Coding).
There was a bug that I couldn't find and after alot of debugging I figured out that when I compress the text, as a result the character � may be in the compressed text. For example, the text ',-.:BCINSabcdefghiklmnoprstuvwy gets compressed to 앐낧淧翵�ဌ䤺큕㈀.
I figured out that the bug lies in the saving function. When I save the compressed text, it changes every occurence of � to ?. For example, when saving 앐낧淧翵�ဌ䤺큕㈀, I get 앐낧淧翵?ဌ䤺큕㈀.
When I try to read the saved file to decompress it, I get a different string so the decompression fails.
What makes it more difficult is that the saving function alone works fine, but it doesn't work when using it in my code. the function looks like this:
public void save() throws IOException {
FileWriter fileWriter = new FileWriter(this.filename);
fileWriter.write(this.text);
fileWriter.close();
}
It's confusing that this.text at the moment of saving is 앐낧淧翵�ဌ䤺큕㈀ yet it saves it as 앐낧淧翵?ဌ䤺큕㈀.
As I said before, the function works fine when alone, but doesn't work in my code. I couldn't do any thing more that removing as much as possible from my code and and putting it here. Anyways, a breakpoint can be put at the function FileEditor::save and you'll find that this.text at the moment of saving is 앐낧淧翵�ဌ䤺큕㈀ and the content of the file is 앐낧淧翵?ဌ䤺큕㈀.
Code:
FileEditor is right below Main.
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.PriorityQueue;
import java.util.TreeMap;
import static pack.BitsManipulator.CHAR_SIZE_IN_BITS;
public class Main {
public static void main(String[] args) throws IOException {
String text = " ',-.:BCINSabcdefghiklmnoprstuvwy";
FileEditor fileEditor2 = new FileEditor("file.txt");
HuffmanDecorator compressor = new HuffmanDecorator(fileEditor2);
compressor.setText(text);
System.out.println(compressor.getText());
compressor.save();
}
}
class FileEditor implements BasicFileEditor {
private String filename;
private String text;
public FileEditor(String filename) throws IOException {
this.filename = filename;
File file = new File(filename);
StringBuilder builder = new StringBuilder();
if (!file.createNewFile()) {
FileReader reader = new FileReader(file);
int ch;
while ((ch = reader.read()) != -1)
builder.append((char) ch);
}
this.text = builder.toString();
}
#Override
public String getText() {
return text;
}
#Override
public void setText(String text) {
this.text = text;
}
#Override
public void save() throws IOException {
FileWriter fileWriter = new FileWriter(this.filename);
fileWriter.write(this.text);
fileWriter.close();
}
}
interface BasicFileEditor {
String getText();
void setText(String text);
void save() throws IOException;
}
abstract class FileEditorDecorator implements BasicFileEditor {
FileEditor fileEditor;
public FileEditorDecorator(FileEditor fileEditor) {
this.fileEditor = fileEditor;
}
#Override
public String getText() {
return fileEditor.getText();
}
#Override
public void setText(String text) {
fileEditor.setText(text);
}
#Override
public void save() throws IOException {
String oldText = getText();
setText(getModifiedText());
fileEditor.save();
setText(oldText);
}
protected abstract String getModifiedText();
}
class HuffmanDecorator extends FileEditorDecorator {
public HuffmanDecorator(FileEditor fileEditor) {
super(fileEditor);
}
#Override
protected String getModifiedText() {
HuffmanCodingCompressor compressor = new HuffmanCodingCompressor(getText());
return compressor.getCompressedText();
}
}
class HuffmanCodingCompressor {
String text;
public HuffmanCodingCompressor(String text) {
this.text = text;
}
public String getCompressedText() {
EncodingBuilder builder = new EncodingBuilder(text);
return builder.getCompressedText();
}
}
class Node implements Comparable<Node> {
public Node left;
public Node right;
public int value;
public Character character;
public Node(Node left, Node right, int value) {
this(left, right, value, null);
}
public Node(Node left, Node right, int value, Character character) {
this.left = left;
this.right = right;
this.character = character;
this.value = value;
}
#Override
public int compareTo(Node o) {
return this.value - o.value;
}
public boolean isLeafNode() {
return left == null && right == null;
}
Node getLeft() {
if (left == null)
left = new Node(null, null, 0);
return left;
}
Node getRight() {
if (right == null)
right = new Node(null, null, 0);
return right;
}
}
class EncodingBuilder {
private String text;
private Node encodingTree;
private TreeMap<Character, String> encodingTable;
public EncodingBuilder(String text) {
this.text = text;
buildEncodingTree();
buildEncodingTableFromTree(encodingTree);
}
private void buildEncodingTableFromTree(Node encodingTree) {
encodingTable = new TreeMap<>();
buildEncodingTableFromTreeHelper(encodingTree, new StringBuilder());
}
public void buildEncodingTableFromTreeHelper(Node root, StringBuilder key) {
if (root == null)
return;
if (root.isLeafNode()) {
encodingTable.put(root.character, key.toString());
} else {
key.append('0');
buildEncodingTableFromTreeHelper(root.left, key);
key.deleteCharAt(key.length() - 1);
key.append('1');
buildEncodingTableFromTreeHelper(root.right, key);
key.deleteCharAt(key.length() - 1);
}
}
public void buildEncodingTree() {
TreeMap<Character, Integer> freqArray = new TreeMap<>();
for (int i = 0; i < text.length(); i++) {
// improve here.
char c = text.charAt(i);
if (freqArray.containsKey(c)) {
Integer freq = freqArray.get(c) + 1;
freqArray.put(c, freq);
} else {
freqArray.put(c, 1);
}
}
PriorityQueue<Node> queue = new PriorityQueue<>();
for (Character c : freqArray.keySet())
queue.add(new Node(null, null, freqArray.get(c), c));
if (queue.size() == 1)
queue.add(new Node(null, null, 0, '\0'));
while (queue.size() > 1) {
Node n1 = queue.poll();
Node n2 = queue.poll();
queue.add(new Node(n1, n2, n1.value + n2.value));
}
encodingTree = queue.poll();
}
public String getCompressedTextInBits() {
StringBuilder bits = new StringBuilder();
for (int i = 0; i < text.length(); i++)
bits.append(encodingTable.get(text.charAt(i)));
return bits.toString();
}
public String getCompressedText() {
String compressedInBits = getCompressedTextInBits();
int remainder = compressedInBits.length() % CHAR_SIZE_IN_BITS;
int paddingNeededToBeDivisibleByCharSize = CHAR_SIZE_IN_BITS - remainder;
String compressed = BitsManipulator.convertBitsToText(compressedInBits + "0".repeat(paddingNeededToBeDivisibleByCharSize));
return compressed;
}
}
class BitsManipulator {
public static final int CHAR_SIZE_IN_BITS = 16;
public static int bitsInStringToInt(String bits) {
int result = 0;
for (int i = 0; i < bits.length(); i++) {
result *= 2;
result += bits.charAt(i) - '0';
}
return result;
}
public static String convertBitsToText(String bits) {
if (bits.length() % CHAR_SIZE_IN_BITS != 0)
throw new NumberOfBitsNotDivisibleBySizeOfCharException();
StringBuilder result = new StringBuilder();
for (int i = 0; i < bits.length(); i += CHAR_SIZE_IN_BITS)
result.append(asciiInBitsToChar(bits.substring(i, i + CHAR_SIZE_IN_BITS)));
return result.toString();
}
public static char asciiInBitsToChar(String bits) {
return (char) bitsInStringToInt(bits);
}
public static class NumberOfBitsNotDivisibleBySizeOfCharException extends RuntimeException {
}
}
� is the Unicode replacement character U+FFFD. If you encode that in a non-unicode encoding, it will get converted to a regular question mark, as non-unicode encodings can't encode all unicode characters, and this provides a "safety" (i.e. convert everything to question marks that we can't encode).
You seem to be confused about the difference between binary data and text data, leading you to look at compressed data as if it were Korean text instead of binary data. You need to store (and observe) the data as bytes, not chars or Strings.

"The method from ... is never used locally" on a public method inside an anonymous class

I'm implementing the iterable interface and returning the iterator using anonymous class.
The problem is I can't use my custom add from the anonymous class in the main method.
I can't call the add method in the main method.
Eclipse complains that it's not defined.
What is the problem?
I'll spare from you the outer class and just show you the method iterator:
#Override
public Iterator<Symbol> iterator() {
return new Iterator<Symbol>() {
int i = 0; //Remove i and it works. WHY?!!
public boolean hasNext() {
return i < symArr.length && symArr[i] != null;
}
public Symbol next() {
if (hasNext()) {
return symArr[i++];
}
return null;
}
public void add(Symbol addMe) {
if (i < symArr.length) {
symArr[i] = addMe;
}
}
};
}
My main method inside the outer class:
public static void main(String[] args) {
SymbolTable st = new SymbolTable(22);
Iterator<Symbol> it = st.iterator();
it.next();
it.add(new Symbol("2", 2)); //Have problem here.
//Problem disappears when I completely remove i variable in the iterator method.
}
The whole code:
package tirgul_iteratorsExceptions;
import java.util.Iterator;
public class SymbolTable implements Iterable<Symbol> {
Symbol[] symArr;
public SymbolTable(int size) {
symArr = new Symbol[size];
}
public SymbolTable(SymbolTable st, boolean isDeep) {
symArr = new Symbol[st.symArr.length];
if (isDeep) {
for (int i = 0; i < st.symArr.length; i++) {
symArr[i] = new Symbol(st.symArr[i].name, st.symArr[i].value);
}
}
// Shallow copy
else {
for (int i = 0; i < st.symArr.length; i++) {
symArr[i] = st.symArr[i];
}
}
}
#Override
public Iterator<Symbol> iterator() {
return new Iterator<Symbol>() {
int i = 0;
public boolean hasNext() {
return i < symArr.length && symArr[i] != null;
}
public Symbol next() {
if (hasNext()) {
return symArr[i++];
}
return null;
}
public void add(Symbol addMe) {
if (i < symArr.length) {
symArr[i] = addMe;
}
}
};
}
public static void main(String[] args) {
SymbolTable st = new SymbolTable(22);
Iterator<Symbol> it = st.iterator();
it.next();
it.add(new Symbol("2", 2));
}
}

Reverse Linked List error- Java

I am trying to reverse a linked list in Java using stack I keep on receiving this error:
LinkStackApp.java:84: error: constructor LinkStack in class LinkStack cannot be applied to given types;
LinkStack stackrev = new LinkStack(stackSize);
^
required: no arguments
found: int
reason: actual and formal argument lists differ in length
1 error
I don't know where it finds int, I tried changing the Strings to char and vice versa but just keep on getting more errors any ideas? here is my full code:
import java.io.*;
import java.util.Scanner;
class Link
{
public char dData;
public Link next;
public Link(char dd)
{
dData = dd;
}
public void displayLink()
{
System.out.print(dData + " ");
}
}
class LinkList
{
private Link first; //ref to first item on the list
public LinkList() //no items on list yet
{
first = null;
}
public boolean isEmpty()
{
return (first == null);
}
public void insertFirst(char dd)
{
Link newLink = new Link(dd);
newLink.next = first;
first = newLink;
}
public char deleteFirst()
{
Link temp = first;
first = first.next;
return temp.dData;
}
public void displayList()
{
Link current = first;
while(current != null)
{
current.displayLink();
current = current.next;
}
System.out.println(" ");
}
}
class LinkStack
{
private LinkList theList;
public LinkStack()
{
theList = new LinkList();
}
public void push(char j)
{
theList.insertFirst(j);
}
public char pop()
{
return theList.deleteFirst();
}
public boolean isEmpty()
{
return (theList.isEmpty());
}
}
class Reverser{
private String input;
private String output;
public Reverser(String in){
input = in;
}
public String doRev(){
int stackSize = input.length();
LinkStack stackrev = new LinkStack(stackSize);
for (int j = 0; j <input.length(); j++){
char ch = input.charAt(j);
stackrev.push(ch);
}
output = "";
while(!stackrev.isEmpty()){
char ch = stackrev.pop();
output = output + ch;
}
return output;
}
}
class LinkStackApp
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
String inputString, outputString;
while(true){
System.out.print("Enter A String: ");
inputString = input.nextLine();
if(inputString.equals(""))
break;
Reverser therev = new Reverser(inputString);
outputString = therev.doRev();
System.out.println("Reversed: "+outputString);
}
}
}
stackSize is an int. It's telling you to call the constructor with no params.
The correct constructor invocation is:
LinkStack stackrev = new LinkStack();

sort two columns in a text file by using java collections framework

I have an input file containing lines like
21,mahesh
12,suresh
23,rajesh
25,lokesh
By using ArrayList I wrote code the code below to handle ascending and descending order
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class FileRead {
public static void main(String[] args)throws IOException {
Scanner s = new Scanner(new FileReader("D:\\Numbers.txt"));
ArrayList<String> al = new ArrayList<String>();
while (s.hasNextLine()) {
String line = s.nextLine();
al.add(line);
}
Collections.sort(al,Collections.reverseOrder());
for (String i: al)
System.out.println(i);
}
}
This yielded the following output
- 25,lokesh
- 23,rajesh
- 21,mahesh
- 12,suresh
$ In the above code, when I take the entire row as a line by using Collections.sort() operation it works.
$ If I take the input like below String column first and integer column next the above code is not working properly it will assign by using String values Alphabetical order,i want to sort the data by using only integer not by String values please help me friends
- mahesh,21
- suresh,12
- rajesh,23
- lokesh,25
First Read the file store it in a Map
Map map = new TreeMap();
while(true)
{
String line = bufferedReader.readLine();
if(line == null)
break;
else {
String arr[] = line.split(",");
for(int i=0;i<arr.length-1;i++)
{
map.put(arr[i],arr[i+1]);
}}
}
Sort it using Comparator
List list = new LinkedList(map.entrySet());
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o2)).getValue())
.compareTo(((Map.Entry) (o1)).getValue());
}
});
Finally displat the result
Map result = new LinkedHashMap();
for (Iterator it = list.iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry)it.next();
result.put(entry.getKey(), entry.getValue());
}
System.out.println(result.toString());
For acending switch o1 and o2 in when returning from comparator.
For the second type of input
List list = new LinkedList(map.keySet());
Collections.sort(list);
Set set = map.entrySet();
Map result = new LinkedHashMap();
for (Iterator it = set.iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry)it.next();
result.put(entry.getKey(), entry.getValue());
}
System.out.println(result.toString());
The following will allow you to parse the information into the appropriate types using a class LineEntry to wrap the data. It will provide the proper sorting on Integer values instead of treating them as Strings and applying alphabetical ordering.
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class FileRead {
public static void main(String[] args) throws Exception {
Scanner s = new Scanner(new InputStreamReader(FileRead.class.getResourceAsStream("/numbers.txt")));
s.useDelimiter("[,\\s]");
ArrayList<LineEntry> lineEntryList = new ArrayList<LineEntry>();
while (s.hasNextLine()) {
int amount = s.nextInt();
String value = s.next();
LineEntry lineEntry = new LineEntry(value, amount);
lineEntryList.add(lineEntry);
}
Collections.sort(lineEntryList, Collections.reverseOrder());
for (LineEntry i : lineEntryList) {
System.out.println(i);
}
}
public static class LineEntry implements Comparable<LineEntry>{
private String value;
private Integer amount;
public LineEntry(String value, Integer amount) {
this.value = value;
this.amount = amount;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Integer getAmount() {
return amount;
}
public void setAmount(Integer amount) {
this.amount = amount;
}
#Override
public String toString() {
return "LineEntry{" + "value=" + value + ", amount=" + amount + '}';
}
#Override
public int compareTo(LineEntry o) {
int compareTo = o.getAmount().compareTo(amount);
if (compareTo == 0) {
compareTo = o.getValue().compareTo(value);
}
return compareTo;
}
}
Better to create a different class which contains data in each line seperated by comma as variables of that class so that in future if you have multiple columns data in the same line then the code can be scalable and also you can create custom comparators based on your sort condition:-
public class FileRead {
public static void main(String[] args) throws Exception {
Scanner s = new Scanner(new FileReader("E:\\Numbers.txt"));
List<FileObject> al = new ArrayList<FileObject>();
while (s.hasNextLine()) {
String line = s.nextLine();
al.add(new FileObject().createFileObject(line));
}
Collections.sort(al,new FileObjectComparator());
for (FileObject i: al)
System.out.println(i);
}
}
class FileObject {
private int id;
private String name;
public FileObject createFileObject(String line) {
if(line != null && !line.isEmpty()) {
for(String str : line.split(",")) {
str = str.trim();
if(str.matches("([\\d]*)")) {
id = Integer.valueOf(str);
} else {
name = str;
}
}
}
return this;
}
... // getters and setters
#Override
public String toString() {
return "[ID] "+id+ " [Name] "+name ;
}
}
class FileObjectComparator implements Comparator<FileObject> {
#Override
public int compare(FileObject o1, FileObject o2) {
return o2.getId() - o1.getId();
}
}

Elements from a text file not being added to the array

I'm trying to add an Entry object to an array of Entries which consists of a Surname, Initial and Extention number. The first word on the line is the surname etc. however when I print the array using System.out.print(Arrays.toString(entries)); it doesn't print the array or it is empty.
Entry class
public class Entry {
private String surname, initial, extension;
public Entry() {
}
// shadowing
public Entry(String line) {
String[] lines = line.split("\t");
this.surname = lines[0];
this.initial = lines[1];
this.extension = lines[2];
}
public void setSurname(String sur) {
this.surname = sur;
}
public void setInitial(String ini) {
this.initial = ini;
}
public void setExtention(String ext) {
this.extension = ext;
}
public String getSurname() {
return surname;
}
public String getInitial() {
return initial;
}
public String getExtension() {
return extension;
}
}
Array directory class
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;
public class ArrayDirectory implements Directory {
Entry[] entries = new Entry[0];
int lines = 0;
public ArrayDirectory() {
try {
Scanner inFile = new Scanner(new FileReader("directory.txt"));
Scanner lCounter = new Scanner(new FileReader("directory.txt"));
while (lCounter.hasNext()) {
lCounter.nextLine();
lines++;
}
entries = new Entry[lines];
for(int i = 0; i < lines; i++){
addEntry(inFile.nextLine());
}
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
public void addEntry(String l) {
Entry newEntry = new Entry(l);
int i = 0;
while(entries[i] != null) i++;
}
public void printTable(){
System.out.print(Arrays.toString(entries));
}
}
You are not adding anything to your array:
public void addEntry(String l) {
Entry newEntry = new Entry(l);
int i = 0;
while(entries[i] != null) i++;
}
You are missing an eventual entries[i] = newEntry;. Of course, once your array is full you'll get an exception with this code. You must ensure i doesn't grow larger than entries.size -1.
Consider using a list instead of an array.
Your Entry class need to override the toString() method. It is this method that Arrays.toString() will use and unless you implement your own version it will fall back to the default one from java.lang.Object which is not very human friendly.
#Override
public String toString() {
return String.format("%s. %s", this.initial, this.surname);
}
You might be better off using a more flexible storage type too so you do not have to read through the file twice. An ArrayList would be appropriate and you can convert it to an array trivially after you have finished adding new entries to it.
How big is your file? Maybe a BufferedReader would be a better choice?
File file = new File("directory.txt");
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
List<Entry> entries = new ArrayList<Entry>()
while ((line = br.readLine()) != null) {
entries.add(new Entry(line));
}
br.close();
Edit: You are not actually adding your Entry objects to the array. You should change this section:
entries = new Entry[lines];
for(int i = 0; i < lines; i++){
addEntry(inFile.nextLine());
}
and this method:
public void addEntry(String l) {
Entry newEntry = new Entry(l);
int i = 0;
while(entries[i] != null) i++;
}
You could add entries[i] = newEntry; just after that final while loop but it is a very poor way to do it. You are iterating through the whole array every time you add a new item. I'd suggest the ArrayList method I describe above.

Categories

Resources