0

Introduction

I challenged myself with this proyect.

I need to modify data at a specific line on a .txt without doing the next things:

  • Loading everything on memory (Maps, Lists, Arrays...)
  • Having to copy the file (such as -> this) and thus eating space on the computer (imagine a huge file, massive!)
  • No relational DBs and stuff alike

A simple .txt file will have everything!

Text file

1;asd1324;2019-05-22 18:28:56;0;0;
2;asd1324;2019-05-22 18:28:56;0;0;
3;asd1324;2019-05-22 18:28:56;0;0;
4;asd1324;2019-05-22 18:28:56;0;0;
5;asd1324;2019-05-22 18:28:56;0;0;
6;asd1324;2019-05-22 18:28:56;0;0;

follows this format which are Strings with ";" as separator.

My code try which works for numbers from 0 to 9

(0 to 9 means this first number -> 1;asd1324;2019-05-22 18:28:56;0;0;)

public static void test(int lineNumber, String data)
    {
        String line;


          try{

              System.out.println("----DEBUG TEST----\n");

              RandomAccessFile file = new RandomAccessFile("parking.txt", "rw");

              System.out.println("File pointer is: " + file.getFilePointer());

              System.out.println("Line size is: " + file.readLine().length());

              System.out.println("Read line is: " + (line = file.readLine()) + " with size: " + line.length());     

              file.seek(line.length());

              System.out.println("File pointer is: " + file.getFilePointer());

              file.writeChars("\n");
              file.writeBytes("7;asd1324;2019-05-22 18:28:56;0;0;");
              file.writeChars("\n");


              System.out.println("File pointer is: " + file.getFilePointer());

              System.out.println("Line size is: " + file.readLine().length());

              System.out.println("Read line is: " + (line = file.readLine()) + " with size: " + line.length());    


              file.seek(lineNumber);

              file.close();

          }catch(IOException e){System.out.println(e);}

          System.out.println("\n----DEBUG TEST----\n");        



    }

Understanding the problem

The string -> 1;asd1324;2019-05-22 18:28:56;0;0;

  • Those last two '0' will be another date format as "yy/MM/dd HH:mm:ss" and a random (int) which add more length and it going to mess with the .txt

Example: 1;asd1324;2019-05-22 18:28:56;2019-06-01 10:11:16;100;

  • The first String is a number from 0 to the last entry number (it's a code)

I want to add them by replacing the String on it's position.

Notes

We will know at all times the size of the string we are going to modify (methods) and act accordingly.

What do I expect from this

Being able to modify the line from a file in Java. Which could be also a general method for this action.

Jonalcaide
  • 560
  • 8
  • 21
  • Are all the lines the same length? – guest May 22 '19 at 19:36
  • @guest Those I provide yes. But they will change overtime check out "Understanding the problem" if not. I will try to clarify it further. – Jonalcaide May 22 '19 at 19:51
  • It's always best to give explicit examples rather than a prose description of what might happen. For example, "I have `1;asd1324;2019-05-22 18:28:56;0;0;` and want to replace it with `1;asd1324;2019-05-22 18:28:56;0;123;`". – guest May 22 '19 at 20:07
  • Next question: do you understand _why_ it's much better to replace while making a copy of the file? Or, are you OK if the file is destroyed by your replacement? – guest May 22 '19 at 20:08
  • @guest There is no creation of a new file nor destruction by the replacement. – Jonalcaide May 22 '19 at 20:20
  • You cannot "change a line", you can only change specific bytes at specific locations in the file. Consequently, you can only change a line to a line of the same length. Otherwise, you will have to fully overwrite file's end. You can do that without loading it in memory in full, though. – yeputons May 22 '19 at 20:25
  • @yeputons could you provide an example? – Jonalcaide May 22 '19 at 20:26
  • @WhiteGlove your code does exactly that: positions itself inside the file by reading some data (you cannot do anything else unless you know exact byte position, then you can use `setFilePointer`) and then calls `write*` to write new bytes – yeputons May 22 '19 at 21:05
  • I know it works. But the question is... how can I still be able to do that if the size changes? (more bytes) – Jonalcaide May 22 '19 at 21:13
  • 1
    "There is no creation of a new file nor destruction by the replacement" - if you're changing the size of a single row then you need to move all of the bytes from that point onward -- either shrinking the file or expanding it. If that process fails for any reason -- such as someone tripping over a power cord -- you will have irrevocably corrupted your file. – guest May 23 '19 at 13:08
  • 1
    That risk may be acceptable to you, but it's something that you have to consider _before_ writing any code. There are alternatives, such as append-only file formats, but whether or not they're appropriate will depend on your use case (which you don't describe). In general, however, creating a copy is the lowest-risk solution, and often the lowest cost (because disk is cheap). – guest May 23 '19 at 13:12
  • @guest I went for a binary file to store data that I can control. Now I am working with 80 bytes of data and inside those 80 there are other divisions. I will post something once I finish it. But so far I am happy with it. – Jonalcaide May 23 '19 at 13:26

1 Answers1

2

There is no such thing as a text file to a storage media (i.e. HDD, RAM, Flash, etc). Computers always store everything in binary (i.e. bytes). Text file is a human concept.

Taking your example above:

1;asd1324;2019-05-22 18:28:56;0;0;
2;asd1324;2019-05-22 18:28:56;0;0;
3;asd1324;2019-05-22 18:28:56;0;0;
4;asd1324;2019-05-22 18:28:56;0;0;
5;asd1324;2019-05-22 18:28:56;0;0;
6;asd1324;2019-05-22 18:28:56;0;0;

Here is how the first 2 lines looks to a computer (in HEX):

313B6173 64313332 343B3230 31392D30 352D3232 2031383A 32383A35 363B303B 303B0A
323B6173 64313332 343B3230 31392D30 352D3232 2031383A 32383A35 363B303B 303B0A

Note how the line is terminated (0A), which the end of line character for most *nix system. On Windows it will be 0D0A (or is it 0A0D?) and on a Mac it will be 0D.

Now to change a line, it must absolutely not be longer than the orignal one otherwise you will end up overwriting onto the start of the next line. Suppose you change the first line from

1;asd1324;2019-05-22 18:28:56;0;0;

to

1;asd1324;2019-05-22 18:28:56;0;01;

you will end up with (first 2 lines):

313B6173 64313332 343B3230 31392D30 352D3232 2031383A 32383A35 363B303B 30313B
323B6173 64313332 343B3230 31392D30 352D3232 2031383A 32383A35 363B303B 303B0A

so your file will now be read as:

1;asd1324;2019-05-22 18:28:56;0;01;2;asd1324;2019-05-22 18:28:56;0;0;
3;asd1324;2019-05-22 18:28:56;0;0;
4;asd1324;2019-05-22 18:28:56;0;0;
5;asd1324;2019-05-22 18:28:56;0;0;
6;asd1324;2019-05-22 18:28:56;0;0;

You now have 5 lines instead of 6 since you have overwritten the line termination character of the original first line.

With all that said, modifying a text file without rewriting it can be a very tedious and hard task. It way faster and easier to rewrite the whole with your modifications. Otherwise, a military boot camp will feel like walk in the park in comparison.

  • Good answer. It helped me and I am doing progress but one more question. How I can do a professional (well made) binary (I decided I am going to work with binaries) – Jonalcaide May 22 '19 at 22:04
  • 1
    @WhiteGlove This is way too large topic for a simple discussion, IMHO. Google is your friend. Try searching for things like Java NIO for starter or maybe Java I/O. – Yanick Poirier May 22 '19 at 23:01