Nate spends some time figuring out how to track his time.
- New problem: Want to track time using a text file.
- Text files are great:
- Keyboard oriented: in terminal, in editor
- Something you can put under revision control
- Need: date and time range
- Format of the time stamp should be convenient for us to read and edit.
- Let the computer do the work to convert the convenient notation to something usable!
- Format:
Fri Feb 08 2019 11:30-13:45
- One timestamp per line. Oh wait, we need a description. Six minutes in and we're already changing the requirements!
- What are "literate" text files? "You can mix human words in amongst data the computer will use to make it more understandable for the humans."
- How to find the times? Attempt to match the time format within each line.
- "It's kind of like an inverse comment. Instead of every line being valid and you have to comment out lines you don't want, if it's in a known format, those are the lines we want and everything else is a comment."
- Can use
line-seq
with clojure.java.io/reader
. That uses lazy I/O.
- Potential Issue: lazy I/O can defer I/O errors to when you're in the middle of processing the data.
- Lazy I/O is less of an issue with files and more of an issue with network sockets.
- Another approach:
slurp
in all the data at once and call split-lines
- "Trade a little memory for some safety."
- Clojure uses the
seq
abstraction whichever way you choose. It's Clojure's unifying abstraction.
- "Even maps get squeezed into a seq if you push them hard enough."
- Next step: figure out which lines are time entry lines and which lines are commentary
- Use a regex to match against each string using Clojure's built-in
#"..."
form.
- In other situations, you can use instaparse for grammar-based parsing.
- Put timestamps on their own line
- Easier to work with
- Allows for more error detection (in the future)
- Use capture groups and
re-matches
detect the match and extract the parts.
- Use
when-let
to destructure and do something only if it matches
- Better yet, make a function
time-info
that does the match and returns either nil
or the string that matched.
- For now, just start by using
doseq
to just go through all the lines and test our matching.
- Making sense of the timestamp string is a whole new problem for next time.
Clojure in this episode:
nil
seq
line-seq
clojure.java.io/reader
slurp
clojure.string/split-lines
#""
, re-matches
let
, when-let
doseq
Related projects:
Code sample from this episode:
(def timestamp-re #"(\w+)\s(\w+)\s(\d+)\s(\d+)\s+(\d{2}):(\d{2})-(\d{2}):(\d{2})")
(with-open [rdr (clojure.java.io/reader "time-log.txt")]
(doseq [line (line-seq rdr)]
(when-let [[whole d m dt yr hs ms he me] (re-matches timestamp-re line)]
(println whole))))