Responsive Ad Area

Share This Post

Blog Role / Featured Tutorials / Java

Java Fixed Width Text files using row, col type storage.

Java Fixed Width Text files using row, col type storage.

Prev post in this trail is Reading and Writing Property Files using Java
Next post in this trail is Reading and Writing Serialized Objects to a File

In the previous examples I demonstrated using delimited text files and property style text files. Next I will demonstrate both fixed width text files and row, col based storage. What this amounts to is that each text file will be like a single sheet in a spread sheet. Now accessing data by row col is probably not going to be a typical use. However for certain type of report generation it might come in handy. So in this example we will read in the fixed width file and then get the values by accessing with given row or column. We will also have some utility methods for conversion to and from various data types. When the file is written each column width will be the width of the largest data types string representation in that column. Also a file like this should be easily imported into any spreadsheet. Also note that we either need A:for the column widths to always need to be the same or B:to have some data source tell us the column widths before we load or c:use a delimiter which makes each column one character wider such as | symbol that we used before. I think I will use the | delimiter as before. The difference here is that when the field is added the String is appended with white space to the proper length or a char array which can be converted back to a string.

In this first example we display in columns and rows without space instead of |.

import java.io.*;
import java.util.regex.*;
 
public class SheetReader {
 private static String aFileName = "survey.txt";
 public static void main(String[] args) {
  try (BufferedReader br = new BufferedReader(new FileReader(aFileName))) {
   String aLine;
	while ((aLine = br.readLine()) != null) {
	 String[] dataRecord=aLine.split(Pattern.quote("|"));
	 for(int i=0;i<dataRecord.length;i++)
	  System.out.print(dataRecord[i]+" ");
	 System.out.println();
	}
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
}

So as you have seen in prior articles its very handy to create a specialized object to hold data in a given format such as a record. In this case we need one to hold rows and then access those rows by columns. Obviously each row in our spreadsheet is a line in the data file. Now we could put each cell in an array of arrays. In other languages this is called a two dimensional array. In Java this is an Array of Arrays. The outer array will be columns and inner one rows. But instead of making the programmer use this array of arrays we will wrap it up neatly in an object that accesses it with get(col,row) and set(col,row) methods. One of the first things we must do is determine the size of the sheet in the file. Of course if we knew it we could just hard code it. It will be better if we can determine it. This is easy enough, the number of columns the dataRecord.length in the above code. But we don't know how many rows until we have read the entire file of rows.

import java.io.*;
import java.util.regex.*;
import java.util.*;
 
public class SheetReader {
 private static String aFileName = "survey.txt";
 Sheet sheet=null;
 public class Sheet{
  String[][] sheet=null;
  int rows;
  int cols;
  public Sheet(int rows, int cols){
   this.rows=rows;
   this.cols=cols;
   sheet=new String[rows][cols];
  }
  public void setData(int row, int col, String string){
   sheet[row][col]=string;
  }
  public String getData(int row, int col){
   return sheet[row][col];
  }
 }
 public SheetReader(){
  ArrayList<String> rows = new ArrayList<String>();
  try (BufferedReader br = new BufferedReader(new FileReader(aFileName))) {
   String aLine;
	while ((aLine = br.readLine()) != null) {
	 rows.add(aLine);
	}
	Iterator<String> rowsIterator = rows.iterator();
	int r=0;
	while(rowsIterator.hasNext()){
	 String[] cols=rowsIterator.next().split(Pattern.quote("|"));
	 if(sheet==null)sheet= new Sheet(rows.size(),cols.length);
	 for(int c=0;c<cols.length;c++){
	  sheet.setData(r,c,cols);
	 }
	 r++;
	}
	for(r=0;r<sheet.rows;r++){
	 for(int c=0;c<sheet.cols;c++)
	  System.out.print(sheet.getData(r,c)+" ");
	 System.out.println();
	}
   } catch (IOException e){
    e.printStackTrace();
   }
  }
 
 public static void main(String[] args) {
  new SheetReader();
 }
}

Next thing to do is to code the utility methods for data type conversions. We also need a way to make sure the data type when converted back to string is appended with white space to fill the cells. If a column needs to be expanded then we need a private utility method to do just that, which means expanding all the cells in that column. Also the logic for this white space fill and column expansion became complex and it had to be repeated in each setter method therefore I abstracted it out into a method called manageColumnSpace that took String for the data being stored and col number and returned a string to be stored in the cell or newly expanded column and cell.

The code below compiles yet the new code has not been tested or debugged. But this code shows what we are trying to do. If you try to store something outside the size of our sheet you will get index out of bounds exception. So this means we may want to write some code to increase the size of the array of arrays by either adding columns or rows of white space. The when we write the file out it will be a larger text file.
And this is the data file we will use. Its half of survey information taken in the cave. The other half is the sketching which I will not show. Though it would be a better example if I had time to make something up.

cave survey|Bear Cave        |Front Sight|John J Smith      |    |    |    |     |
latitute   |51° 30' 12.11'' N|Back Sight |Albert Einstein   |    |    |    |     |
longitute  |0° 7' 39.45'' W  |Tape Smart |Adolf Hitler      |    |    |    |     |
date       |02/03/2018       |Tape Dumb  |George Washington |    |    |    |     |
survey name|north passage    |Sketcher   |Larry Gray        |    |    |    |     |
           |                 |back az    |back va           |    |    |    |     |
           |dist             |azmith     |vert angle        |up  |down|left|right|
a1         |                 |147.25     |-9.75             |    |    |    |     |
           |21.5             |302.5      |+10.5             |10.8|2   |15.3|18.9 |
a2         |                 |           |                  |    |    |    |     |
           |13.2             |265.25     |-1.25             |4   |1.2 |3.5 |1.3  |
a3         |                 |           |                  |    |    |    |     |
           |85.7             |150.75     |-4.75             |25  |5.7 |30  |55   |
a4         |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
           |                 |           |                  |    |    |    |     |
import java.io.*;
import java.util.regex.*;
import java.util.*;
import java.text.*;

public class SheetReader {
 private static String aFileName = "survey.txt";
 private static String outputFileName = "survey2.txt";
 Sheet sheet=null;
 public class Sheet{
  String[][] sheet=null;
  int rows;
  int cols;
  public String toString(){
   String rowString=null;
   String sheetString=null;
   for(int r=0;r<rows;r++){
    for(int c=0;c<cols;c++)
     rowString+=sheet[r]+"|";
    sheetString+=rowString+"\n";
    rowString="";
   }
   return sheetString;
  }
  public Sheet(int rows, int cols){
   this.rows=rows;
   this.cols=cols;
   sheet=new String[rows][cols];
  }
  private int getColWidth(int col){
   if(sheet[0][col]==null)
    return 0;
   else
    return sheet[0][col].length();
  }
  private String appendWhiteSpace(String theString, int length){
      return (theString+"                                                    "+
       "                                                                     "+
       " ").substring(0,length);
  }
  private void expandColWidth(int col, int w){
   for(int r=0;r<rows;r++){
    sheet[r][col]=appendWhiteSpace(sheet[r][col],w);
   }
  }
  SimpleDateFormat dateFormat=new SimpleDateFormat("dd/MM/yy");
 
   public String manageColumnSpace(String string,int col){
   int w=getColWidth(col);
   if(w==0)return string;
   int l=string.length();
   if(l<=w) return appendWhiteSpace(string,w);
   else {
    expandColWidth(col,l);
    return string;
   }
  }
  public int getInt(int row, int col){
   return Integer.parseInt((sheet[row][col]).trim());
  }
  public void setInt(int row, int col, int anInt){
    sheet[row][col]=manageColumnSpace(""+anInt,col);
  }
  public void setFloat(int row,int col,float aFloat){
   sheet[row][col]=manageColumnSpace(""+aFloat,col);
  }
  public float getFloat(int row,int col){
   return Float.parseFloat((sheet[row][col]).trim());
  }
  public boolean getBoolean(int row, int col){
   return Boolean.parseBoolean((sheet[row][col]).trim());
  }
  public void setBoolean(int row, int col, boolean aBoolean){
   sheet[row][col]=manageColumnSpace(""+aBoolean,col);
  }
  public Date getDate(int row, int col){
   return new Date(sheet[row][col]);
  }
  public void setDate(int row, int col, Date date){
   sheet[row][col]=manageColumnSpace(dateFormat.format(date),col);
  }
  public String getData(int row, int col){
   return sheet[row][col];
  }
  public void setData(int row, int col, String string){
   
   sheet[row][col]=manageColumnSpace(string,col);
  }
 }
 public SheetReader(){
  ArrayList<String> rows = new ArrayList<String>();
  try (BufferedReader br = new BufferedReader(new FileReader(aFileName))) {
   String aLine;
	while ((aLine = br.readLine()) != null) {
	 rows.add(aLine);
	}
	Iterator<String> rowsIterator = rows.iterator();
	int r=0;
	while(rowsIterator.hasNext()){
	 String[] cols=rowsIterator.next().split(Pattern.quote("|"));
	 if(sheet==null)sheet= new Sheet(rows.size(),cols.length);
	 for(int c=0;c<cols.length;c++){
	  sheet.setData(r,c,cols);
	 }
	 r++;
	}
	for(r=0;r<sheet.rows;r++){
	 for(int c=0;c<sheet.cols;c++)
	  System.out.print(sheet.getData(r,c)+" ");
	 System.out.println();
	}
   } catch (IOException e){
    e.printStackTrace();
   }
   write();
  }
  public void write(){
   try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName))) {
    String aString = sheet.toString();
    bw.write(aString);
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 public static void main(String[] args) {
  new SheetReader();
 }
}

Without even testing the code above I'm going to move on and code the write sheet back to file code. I am thinking the easiest way to do
this is to simply have the Sheet object have a toString() method which builds the lines appending each to a String with "\n" delimiting lines. Then all that is needed is for the write method to open the file and write the sheet.toString(); and done. We are safe most likely in that our actual data would have be over 2 gig in bytes and each char in a string is 2 byte unicode char. So this means we can do near 1 gig in characters in our sheet before we have problems. For our simple applications it will probably never be an issue.

import java.io.*;
import java.util.regex.*;
import java.util.*;
import java.text.*;

public class SheetReaderWriter {
 private static String aFileName = "survey.txt";
 private static String outputFileName = "survey2.txt";
 Sheet sheet=null;
 public class Sheet{
  String[][] sheet=null;
  int rows;
  int cols;
  public String toString(){
   String rowString="";
   String sheetString="";
   for(int r=0;r<rows;r++){
    for(int c=0;c<cols;c++)
     rowString+=sheet[r]+"|";   
    sheetString+=rowString+"\n";
    rowString="";
   }
   return sheetString;
  }
  public Sheet(int rows, int cols){
   this.rows=rows;
   this.cols=cols;
   sheet=new String[rows][cols];
  }
  private int getColWidth(int col){
   if(sheet[0][col]==null)
    return 0;
   else
    return sheet[0][col].length();
  }
  private String appendWhiteSpace(String theString, int length){
      return (theString+"                                                    "+
       "                                                                     "+
       " ").substring(0,length);
  }
  private void expandColWidth(int col, int w){
   for(int r=0;r<rows;r++){
    sheet[r][col]=appendWhiteSpace(sheet[r][col],w);
   }
  }
  SimpleDateFormat dateFormat=new SimpleDateFormat("dd/MM/yy");
  
   public String manageColumnSpace(String string,int col){
   int w=getColWidth(col);
   if(w==0)return string;
   int l=string.length();
   if(l<=w) return appendWhiteSpace(string,w);
   else {
    expandColWidth(col,l);
    return string;
   }
  }
  public int getInt(int row, int col){
   return Integer.parseInt((sheet[row][col]).trim());
  }
  public void setInt(int row, int col, int anInt){
    sheet[row][col]=manageColumnSpace(""+anInt,col);
  }
  public void setFloat(int row,int col,float aFloat){
   sheet[row][col]=manageColumnSpace(""+aFloat,col);
  }
  public float getFloat(int row,int col){
   return Float.parseFloat((sheet[row][col]).trim());
  }
  public boolean getBoolean(int row, int col){
   return Boolean.parseBoolean((sheet[row][col]).trim());
  }
  public void setBoolean(int row, int col, boolean aBoolean){
   sheet[row][col]=manageColumnSpace(""+aBoolean,col);
  }
  public Date getDate(int row, int col){
   return new Date(sheet[row][col]);
  }
  public void setDate(int row, int col, Date date){
   sheet[row][col]=manageColumnSpace(dateFormat.format(date),col);
  }
  public String getData(int row, int col){
   return sheet[row][col];
  }
  public void setData(int row, int col, String string){
   
   sheet[row][col]=manageColumnSpace(string,col);
  }
 }
 public SheetReaderWriter(){
  ArrayList<String> rows = new ArrayList<String>();
  try (BufferedReader br = new BufferedReader(new FileReader(aFileName))) {
   String aLine;
	while ((aLine = br.readLine()) != null) {
	 rows.add(aLine);
	}
	Iterator<String> rowsIterator = rows.iterator();
	int r=0;
	while(rowsIterator.hasNext()){
	 String[] cols=rowsIterator.next().split(Pattern.quote("|"));
	 if(sheet==null)sheet= new Sheet(rows.size(),cols.length);
	 for(int c=0;c<cols.length;c++){
	  sheet.setData(r,c,cols);
	 }
	 r++;
	}
	for(r=0;r<sheet.rows;r++){
	 for(int c=0;c<sheet.cols;c++)
	  System.out.print(sheet.getData(r,c)+" ");
	 System.out.println();
	}
   } catch (IOException e){
    e.printStackTrace();
   }
   write();
  }
  public void write(){
   try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName))) {
    String aString = sheet.toString();
    bw.write(aString);
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 public static void main(String[] args) {
  new SheetReaderWriter();
 }
}

So the above duplicates the file survey.txt to survey2.txt
Next we need to test some set/get methods. I will simply use each method on some cells.

import java.io.*;
import java.util.regex.*;
import java.util.*;
import java.text.*;

public class SheetReaderWriter {
 private static String aFileName = "survey.txt";
 private static String outputFileName = "survey2.txt";
 Sheet sheet=null;
 public class Sheet{
  String[][] sheet=null;
  int rows;
  int cols;
  public String toString(){
   String rowString="";
   String sheetString="";
   for(int r=0;r<rows;r++){
    for(int c=0;c<cols;c++)
     rowString+=sheet[r]+"|";
    sheetString+=rowString+"\n";
    rowString="";
   }
   return sheetString;
  }
  public Sheet(int rows, int cols){
   this.rows=rows;
   this.cols=cols;
   sheet=new String[rows][cols];
  }
  private int getColWidth(int col){
   if(sheet[0][col]==null)
    return 0;
   else
    return sheet[0][col].length();
  }
  private String appendWhiteSpace(String theString, int length){
      return (theString+"                                                    "+
       "                                                                     "+
       " ").substring(0,length);
  }
  private void expandColWidth(int col, int w){
   for(int r=0;r<rows;r++){
    sheet[r][col]=appendWhiteSpace(sheet[r][col],w);
   }
  }
  SimpleDateFormat dateFormat=new SimpleDateFormat("dd/MM/yy");
  public String manageColumnSpace(String string,int col){
   int w=getColWidth(col);
   if(w==0)return string;
   int l=string.length();
   if(l<=w) return appendWhiteSpace(string,w);
   else {
    expandColWidth(col,l);
    return string;
   }
  }
  public int getInt(int row, int col){
   return Integer.parseInt((sheet[row][col]).trim());
  }
  public void setInt(int row, int col, int anInt){
    sheet[row][col]=manageColumnSpace(""+anInt,col);
  }
  public void setFloat(int row,int col,float aFloat){
   sheet[row][col]=manageColumnSpace(""+aFloat,col);
  }
  public float getFloat(int row,int col){
   return Float.parseFloat((sheet[row][col]).trim());
  }
  public boolean getBoolean(int row, int col){
   return Boolean.parseBoolean((sheet[row][col]).trim());
  }
  public void setBoolean(int row, int col, boolean aBoolean){
   sheet[row][col]=manageColumnSpace(""+aBoolean,col);
  }
  public Date getDate(int row, int col){
   return new Date(sheet[row][col]);
  }
  public void setDate(int row, int col, Date date){
   sheet[row][col]=manageColumnSpace(dateFormat.format(date),col);
  }
  public String getData(int row, int col){
   return sheet[row][col];
  }
  public void setData(int row, int col, String string){
   
   sheet[row][col]=manageColumnSpace(string,col);
  }
 }
 public SheetReaderWriter(){
  ArrayList<String> rows = new ArrayList<String>();
  try (BufferedReader br = new BufferedReader(new FileReader(aFileName))) {
   String aLine;
	while ((aLine = br.readLine()) != null) {
	 rows.add(aLine);
	}
	Iterator<String> rowsIterator = rows.iterator();
	int r=0;
	while(rowsIterator.hasNext()){
	 String[] cols=rowsIterator.next().split(Pattern.quote("|"));
	 if(sheet==null)sheet= new Sheet(rows.size(),cols.length);
	 for(int c=0;c<cols.length;c++){
	  sheet.setData(r,c,cols);
	 }
	 r++;
	}
	
   } catch (IOException e){
    e.printStackTrace();
   }
   sheet.setDate(3,1,new Date("09/02/2018"));
   sheet.setData(2,3,"Joseph Stalin Jr III");
   sheet.setData(0,3,sheet.getData(1,3));
   sheet.setFloat(9,2,sheet.getFloat(10,2)-180.0f);
   sheet.setInt(10,4,sheet.getInt(10,4)+1);
   sheet.setBoolean(0,4,false);
   sheet.setBoolean(1,4,sheet.getBoolean(0,4));
   sheet.setBoolean(2,4,true);
   sheet.setBoolean(3,4,sheet.getBoolean(2,4));
   write();
  }
  public void write(){
   try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName))) {
    String aString = sheet.toString();
    bw.write(aString);
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 public static void main(String[] args) {
  new SheetReaderWriter();
 }
}

So here we see how the test worked out.

cave survey|Bear Cave        |Front Sight|John J Smith      |    |    |    |     |
latitute   |51° 30' 12.11'' N|Back Sight |Albert Einstein   |    |    |    |     |
longitute  |0° 7' 39.45'' W  |Tape Smart |Adolf Hitler      |    |    |    |     |
date       |02/03/2018       |Tape Dumb  |George Washington |    |    |    |     |
survey name|north passage    |Sketcher   |Larry Gray        |    |    |    |     |
           |                 |back az    |back va           |    |    |    |     |
           |dist             |azmith     |vert angle        |up  |down|left|right|
a1         |                 |147.25     |-9.75             |    |    |    |     |
           |21.5             |302.5      |+10.5             |10.8|2   |15.3|18.9 |
a2         |                 |           |                  |    |    |    |     |
           |13.2             |265.25     |-1.25             |4   |1.2 |3.5 |1.3  |
a3         |                 |           |                  |    |    |    |     |
           |85.7             |150.75     |-4.75             |25  |5.7 |30  |55   |
a4         |                 |           |                  |    |    |    |     |

cave survey|Bear Cave        |Front Sight|Albert Einstein     |false|    |    |     |
latitute   |51° 30' 12.11'' N|Back Sight |Albert Einstein     |false|    |    |     |
longitute  |0° 7' 39.45'' W  |Tape Smart |Joseph Stalin Jr III|true |    |    |     |
date       |02/09/18         |Tape Dumb  |George Washington   |true |    |    |     |
survey name|north passage    |Sketcher   |Larry Gray          |     |    |    |     |
           |                 |back az    |back va             |     |    |    |     |
           |dist             |azmith     |vert angle          |up   |down|left|right|
a1         |                 |147.25     |-9.75               |     |    |    |     |
           |21.5             |302.5      |+10.5               |10.8 |2   |15.3|18.9 |
a2         |                 |85.25      |                    |     |    |    |     |
           |13.2             |265.25     |-1.25               |5    |1.2 |3.5 |1.3  |
a3         |                 |           |                    |     |    |    |     |
           |85.7             |150.75     |-4.75               |25   |5.7 |30  |55   |
a4         |                 |           |                    |     |    |    |     |


And that is it for now. I could demonstrate doing some calculations that would be needed for cave mapping.
For example to the right after the up down left right data we could calculate using trigonometry change in vertical and horizontal distances. Also change in e-w distance and change in n-s distance. This data would then be used to plot plan view line plot of survey or profile views of survey. Also we could calculate error in azimuth and error in vertical angle which should be less than one degree usually. This is the difference in front and back sights. Once we do that, this is a fully functional app for aiding in cave surveying or any surveying for that matter, even above ground. The cartographer would either use the data in some CAD program or on actual physical graph paper. I don't know what you might want this type of data storage for but you now have it. Have fun.

Prev post in this trail is Reading and Writing Property Files using Java
Next post in this trail is Reading and Writing Serialized Objects to a File

Share This Post

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>