I looked into translating each of the examples in the PLEAC and it would be a lot of work. Instead I’ll do my own thing. This post covers function for working with strings.
Please give feedback if you can think of any improvements.
Characters and Strings
Strings are a sequence of characters. Unlike many languages emacs lisp doesn’t differentiate between characters and integers. Evaluating ?A returns 65. Therefore you don’t need an ord or char function.
To convert a character (or integer) into a string use (string ...).
(string 65) ;; "A" is the same as (string ?A) ;; this (string 10) ;; these two are (string ?\n) ;; also the same
To extract a character from a string use (elt ...).
(elt "ABC" 1) ;; 66 -> the ASCII value of B
Slicing and Dicing Strings
The basic function for slicing and dicing strings is (substring ...) You pass it the string and from and to indexes. 0 indicates the start of the string and negative indexes measure length from the end.
(defvar data "1234567890123456789012345678901234567890") (substring data 0 4) ;; 1234 (substring data 35) ;; 67890 (substring data 35 -1) ;; 6789 (substring data -3 -1) ;; 89
To split a string use (split-string ...) defined in subr.el.
(split-string "a,b:c" "[,:]") ;; ("a" "b" "c")
You can join it back together with (mapconcat ...) which also takes a function with which you can transform each element.
(mapconcat (lambda (e) e) '("abc" "def" "ghi" "klm") ",") ;; gives --> "abc,def,ghi,klm"
Join strings together without a separator using (concat ...)
(concat "Hello " "[" "World" "] " "!!!") ;; "Hello [World] !!!"
Convert strings to uppercase using (upcase ...). There are also capitalize, downcase and upcase-initials.
(upcase "abc") ;; "ABC" (capitalize "abc def") ;; "Abc Def"
Strings and Regular Expressions
The PLEAC has a nice example of substituting variables within a string using regular expressions.
$AGE = 17; $text = 'I am $AGE years old'; # note single quotes $text =~ s/(\$\w+)/$1/eeg; # finds my() variables
Here is an imperfect translation into emacs-lisp.
(defvar AGE 17) (defvar text "I am $AGE years old") (when (string-match "$\\([A-Z]+\\)" text) (concat (substring text 0 (match-beginning 0)) (format "%s" (symbol-value (intern (match-string 1 text)))) (substring text (match-end 0))))
Unpack
Bindat is a library that provides similar to functionality to pack and unpack in perl. This is the example given.
# get a 5-byte string, skip 3, then grab 2 8-byte strings, then the rest ($leading, $s1, $s2, $trailing) = unpack("A5 x3 A8 A8 A*", $data);
The obvious translation doesn’t quite work - there might be a minor bug-ette in the library. This fails with an args-out-of-range error.
(require 'bindat) (bindat-unpack '((leading str 5) (fill 3) (s1 str 8) (s2 str 8) (rest str (eval (- (length data) bindat-idx)))) data)
Subtracting an extra one leaves one character off rest as expected.
;; ((rest . "567890123456789") ;; (s2 . "78901234") ;; (s1 . "90123456") ;; (leading . "12345"))
Fortunately, I have a workaround that extends the source string with a dummy character and then takes 1 fewer characters. (yes, it is pretty horrible).
(bindat-unpack '((leading str 5)
(fill 3)
(s1 str 8)
(s2 str 8)
(rest str (eval (- (length data) bindat-idx 1))))
(concat data "x"))
And the full equivalent would be something like this. As the association list is built in reverse order we need to reverse the parameter list.
(multiple-value-bind (rest s2 s1 leading) (mapcar (lambda (e) (cdr e)) (bindat-unpack '((leading str 5) (fill 3) (s1 str 8) (s2 str 8) (rest str (eval (- (length data) bindat-idx 1)))) (concat data "x"))) (insert "\n") (insert (format "[%s] [%s] [%s] [%s]" leading s1 s2 rest)))