0

I am creating a program using processing that read txt file and send the result to the arduino. I can get it to send string and keep update, but as soon as I try to just send the last char it won't work... Can anyone help me with this? Basically I need to read the last char from a txt file and send it as char through serial to arduino, python or processing both will work!

*Here is my code [ Processing ]

import processing.serial.*;
import java.io.*;

int mySwitch=0;
int counter=0;
String [] subtext;
Serial myPort;
   
void setup(){
   //Create a switch that will control the frequency of text file reads.
   //When mySwitch=1, the program is setup to read the text file.
   //This is turned off when mySwitch = 0
   mySwitch=1;
 
   //Open the serial port for communication with the Arduino
   //Make sure the COM port is correct
   myPort = new Serial(this, "COM4", 9600);
   myPort.bufferUntil('\n');
}

void draw() {
   if (mySwitch>0){
       /*The readData function can be found later in the code. This is the call to read a CSV file on the computer hard-drive. */
       readData("G:/Personal/control.txt");
 
       /*The following switch prevents continuous reading of the text file, until   we are ready to read the file again. */
       mySwitch=0;
   }

   /*Only send new data. This IF statement will allow new data to be sent to the arduino. */
   if(counter<subtext.length){
       /* Write the next number to the Serial port and send it to the Arduino There will be a delay of half a second before the command is sent to turn the LED off : myPort.write('0'); */
       myPort.write(subtext[counter]);
       delay(500);
       myPort.write('0');
       delay(100);

       //Increment the counter so that the next number is sent to the arduino.
       counter++;
   } else{
       //If the text file has run out of numbers, then read the text file again in 5 seconds.
       delay(5000);
       mySwitch=1;
   }
} 


/* The following function will read from a CSV or TXT file */
void readData(String myFileName){
 
 File file=new File(myFileName);
 BufferedReader br=null;
 
 try{
   br=new BufferedReader(new FileReader(file));
   String text=null;
 
   /* keep reading each line until you get to the end of the file */
 while((text=br.readLine())!=null){
   * Spilt each line up into bits and pieces using a comma as a separator */

   subtext = splitTokens(text,",");
 }
 }catch(FileNotFoundException e){
     e.printStackTrace();
 }catch(IOException e){
     e.printStackTrace();
 }finally{
     try {
        if (br != null){
           br.close();
        }
     } catch (IOException e) {
     e.printStackTrace();
   }
 }

And this is the data that i am dealing with

[19:54:57] [Server thread/INFO]: [@] b

[19:54:57] [Server thread/INFO]: [@] a

[19:54:57] [Server thread/INFO]: [@] b

[19:54:57] [Server thread/INFO]: [@] a

[19:54:58] [Server thread/INFO]: [@] b

[19:54:58] [Server thread/INFO]: [@] a

[19:54:59] [Server thread/INFO]: [@] b

[20:30:23] [Server thread/INFO]: [@] a

[20:30:24] [Server thread/INFO]: [@] b

[21:07:34] [Server thread/INFO]: [@] a

[21:07:35] [Server thread/INFO]: [@] b

  • The only value that I really care is a / b
  • And this file will consistently update in this format
Community
  • 1
  • 1
Ricky Chau
  • 41
  • 1
  • 1
  • 2

5 Answers5

12

Reading the last character from a file:

with open(filename, 'rb+') as f:
    f.seek(f.tell()-1,2)    # f.seek(0,2) is legal for last char in both python 2 and 3 though
    print f.read()

For Python 3
To make it more generic say we want to read second last (can be any though) char of the file without turning on the binary mode:

with open('file.txt', 'r') as f:
    f.seek(0, 2)
    # seek to end of file; f.seek(0, os.SEEK_END) is legal

    f.seek(f.tell() - 2, 0)
    # seek to the second last char of file;
    # while f.seek(f.tell()-2, os.SEEK_SET) is legal,
    # f.seek(-2, 0) will throw an error.

    print(f.read())

Seek is a file object handling comment

file.seek(offset,position)

  1. offset: How many characters you need (i.e. 1 means one character)
  2. position: Tell where your file read/write operation should start .
    • 0 means starting of file
    • 1 means current position of file read/write cursor
    • 2 means end of file

f.seek(f.tell()-1,2) means go to the end of file and traverse back one element

print f.read() prints the value obtained from the seek command

Anonymous
  • 738
  • 4
  • 14
  • 36
Stefan Gruenwald
  • 2,582
  • 24
  • 30
  • import os with open('G:\Personal\control.txt' , 'rb+') as f: f.seek(-1,2) print f.read(1) Like this? – Ricky Chau Nov 26 '14 at 06:30
  • The explanation of `offset` parameter can be reworded a bit. Currently it leaves the impression that `offset` determines how many bytes are read with the next call to `read`, which is only true in this very specific context (because we are reading everything to the end of the file). Also, `2` can be replaced with os.SEEK_END to make it more readable, and `+` mode is not actually needed, as the file is not modified, only read. – quasoft Feb 04 '17 at 12:49
  • Note that this reads in binary mode which not work well for all use cases. If you are in an append+read (a+) mode and need to use strings, then [see this answer](https://stackoverflow.com/a/51131242/1526703) – Anupam Jan 06 '20 at 06:05
  • To make the code more explicit you can use built-in constant [`os.SEEK_END`](https://docs.python.org/3/library/os.html#os.SEEK_END) instead of `2` when calling `seek()` – Cédric Van Rompay Apr 23 '20 at 12:03
  • 1
    thanks this was the only solution that worked properly for me. I had to fish out 100 lines of good code from a 20,000,000 line ipynb file that had printed a ludicrously large error messages – Matt Oct 14 '21 at 09:46
  • Do note: this is Undefined Behavior. SEEK_SET or 0: seek from the start of the stream (the default); offset must either be a number returned by TextIOBase.tell(), or zero. Any other offset value produces undefined behaviour. https://docs.python.org/3/library/io.html#io.TextIOBase.seek – user1011113 Mar 10 '22 at 13:24
6

Answer of Stefan is simple and kind of works, but does not account for text files with:

  • UTF-8 encoding containing non-English characters (which is the default encoding for text files in Python 3)
  • one newline character at the end of the file (which is the default in Linux editors like vim or gedit)

If the text file contains non-English characters, neither of the answers provided so far would work.

What follows in an example that solves both problems.

import os


def get_last_utf8_char(filename, ignore_newlines=True):
    """
    Reads the last character of a UTF-8 text file.
    :param filename: The path to the text file to read
    :param ignore_newlines: Set to true, if the newline character at the end of the file should be ignored 
    :return: Returns the last UTF-8 character in the file or None, if the file is empty 
    """
    with open(filename, 'rb') as f:
        last_char = None

        # Reads last 4 bytes, as the maximum size of an UTF-8 character is 4 bytes
        num_bytes_to_read = 4

        # If ignore_newlines is True, read two more bytes, as a newline character
        # can be up to 2 bytes (eg. \r\n)
        # and we might have a newline character at the end of file
        # or size bytes, if file's size is less than 4 bytes
        if ignore_newlines:
            num_bytes_to_read += 2

        size = os.fstat(f.fileno()).st_size
        f.seek(-min(num_bytes_to_read, size), os.SEEK_END)
        last_bytes = f.read()

        # Find the first byte of a UTF-8 character, starting
        # from the last byte
        offset = -1
        while abs(offset) <= len(last_bytes):
            b = last_bytes[offset]
            if ignore_newlines and b in b'\r\n':
                offset -= 1
                continue
            if b & 0b10000000 == 0 or b & 0b11000000 == 0b11000000:
                # If this is first byte of a UTF8 character,
                # interpret this and all bytes after it as UTF-8
                last_char = last_bytes[offset:].decode('utf-8')
                break
            offset -= 1

        if last_char and ignore_newlines:
            last_char = last_char.replace('\r', '').replace('\n', '')

        return last_char

How it works:

  • Reads only the last few bytes of a UTF-8 encoded text file in binary mode
  • Iterates the bytes backwards, looking for the start of a UTF-8 character
  • Once a character (different from a newline) is found, return that as the last character in the text file

Sample text file - bg.txt:

Здравей свят

How to use:

>>> get_last_utf8_char('bg.txt')
т

This works with both UTF-8 and ASCII encoded files.

quasoft
  • 5,291
  • 1
  • 33
  • 37
0

What i have done :

  1. Opened the file.
  2. Read the last line as list.
  3. Converted it into string and read the last character and printed into.

    a=open('does&donts.txt','rb')
    lines = a.readlines()
    a.close()
    if lines:
        last_line = lines[-1]
    print  last_line
    b="".join(last_line)
    print b[-1:]
    

Hope it helps

Ashwani
  • 1,938
  • 11
  • 15
The6thSense
  • 8,103
  • 8
  • 31
  • 65
0

simply use this :

with open("file.txt", "r") as f:
    file_str = str(f.read())
    f.close()
last_chr = file_str[-1]
print(last_chr)

its important to read the file by "r" not "rb"

-1

Simplest thing I could come up with:

s="".join(list(open(rfile_directory))) 
pos=s[-1:]

steps:

  1. opened the file
  2. converted it into list
  3. Joined the list to str form
  4. The got the last character!

Now, if the character is wanted in int form, simply:

pos=int(s[-1:])