1. Select a region.
2. Run M-=, or M-| wc for older versions of emacs.
3. See the count of lines, words, and characters, in the mode-line.
The minor mode
‘word-count-mode’ displays the number of words in the current buffer. It is quite handy:
http://taiyaki.org/elisp/word-count/src/word-count.el (down on Nov. 1, 2012)
Wayback Machine link working Nov. 1, 2012: http://web.archive.org/web/20100924082154/http://taiyaki.org/elisp/word-count/src/word-count.el
As you type, it shows the number of characters in the buffer, the number of words, and the number of lines: “WC:148/40/4”. You can set a marker to the point where you want to begin counting (when you enable
‘word-count-mode’ in a buffer, the marker is at the position of point), and you can set a region to count.
‘wc-mode’) is another minor mode that does essentially the same thing: it displays the character count, word count and line count for the buffer in the mode line (similar to the output of the Unix
[Somewhat related: Library modeline-posn.el shows, in the mode line, the number of characters (not words) in the active region. That is, the buffer size is replaced in the mode line by the region size whenever the region is active. See ModeLinePosition.]
Here is a function that counts words in the region.
(defun count-words (start end) "Print number of words in the region." (interactive "r") (save-excursion (save-restriction (narrow-to-region start end) (goto-char (point-min)) (count-matches "\\sw+"))))
If you are like me, you can’t remember wether it is word-count or count-words, so what I did is I defined an alias:
(defalias 'word-count 'count-words)
For those without their DotEmacs, you can find the number of words in a buffer with the command
M-< M-x count-matches RET \w+ RET
or count the number of words in the region with the command
C-x n n M-x count-matches RET \w+ RET C-x n w
This is how the function works: count-matches counts whatever matches from point to the end of the buffer. So the first things was to use a RegularExpression. In this case \\sw matches a word character. Now all we need is to narrow the buffer (see Narrowing) to the region, put point at the beginning of the accessible buffer, and call count-matches. save-excursion will restore point when it is done, and save-restriction will restore the previous state of narrowing (or no narrowing at all) when it is done.
;; source: xemacs 20.3 (defun count-words-region (start end) (interactive "r") (save-excursion (let ((n 0)) (goto-char start) (while (< (point) end) (if (forward-word 1) (setq n (1+ n)))) (message "Region has %d words" n) n)))
- from the SF Wiki, http://mcd.freeshell.org/wiki.pl
Using this definition, we can do:
(defun count-lines-words-region (start end) "Print number of lines words and characters in the region." (interactive "r") (message "Region has %d lines, %d words, %d characters" (count-lines start end) (count-words-region start end) (- end start)))
A defun which work just like
wc shell command:
(defun wc (&optional start end) "Prints number of lines, words and characters in region or whole buffer." (interactive) (let ((n 0) (start (if mark-active (region-beginning) (point-min))) (end (if mark-active (region-end) (point-max)))) (save-excursion (goto-char start) (while (< (point) end) (if (forward-word 1) (setq n (1+ n))))) (message "%3d %3d %3d" (count-lines start end) n (- end start))))
Now I can’t tell whether I’ve stolen it from someone or wrote it myself ;) – mina86
To pipe the contents of the file in the current buffer to the wc commandline tool, just add this to your .emacs:
(defun wc () (interactive) (shell-command (concat "wc " buffer-file-name)))
Maybe add a keybinding (C-c w):
(global-set-key "\C-cw" 'wc)
As this is using the wc installed on your *nix system, it only works for files saved on a disk, not temporary buffers.
To get all the words used in the region and the number of times they have been used, use the following:
(defun word-count-analysis (start end) "Count how many times each word is used in the region. Punctuation is ignored." (interactive "r") (let (words) (save-excursion (goto-char start) (while (re-search-forward "\\w+" end t) (let* ((word (intern (match-string 0))) (cell (assq word words))) (if cell (setcdr cell (1+ (cdr cell))) (setq words (cons (cons word 1) words)))))) (when (interactive-p) (message "%S" words)) words))
Using this on the following region:
This is a test, just a test.
The result would be:
((just . 1) (test . 2) (a . 2) (is . 1) (This . 1))
Here’s an enhanced version of word-count-analysis that does a little more.
(cond ((not (boundp 'word-count-analysis-syntax-table)) (setq word-count-analysis-syntax-table (make-syntax-table text-mode-syntax-table)) ;; allow these to be part of words (modify-syntax-entry ?, "w" word-count-analysis-syntax-table) (modify-syntax-entry ?. "w" word-count-analysis-syntax-table) (modify-syntax-entry ?' "w" word-count-analysis-syntax-table))) (defun word-count-analysis (start end &optional ignore-list len) "Count how many times each word is used in the region. Punctuation is ignored. Third optional parameter is a list of words to ignore; fourth means ignore words shortern than len." (interactive "rxp") (let ((cur-table (syntax-table)) words word-str word cell) (set-syntax-table word-count-analysis-syntax-table) (save-excursion (goto-char start) (while (re-search-forward "\\w+" end t) (setq word-str (downcase (match-string 0))) ;; KLUDGE: we want punctuation embedded in words, e.g. 5.10 and ;; 4,300, but not at end (if (string-match "\\(.+\\)[,.]$" word-str) (setq word-str (match-string 1 word-str))) (setq word (intern word-str)) (setq cell (assq word words)) (if (and (not (eq word ',)) ; in "(foo)," both foo and , are words; ; we want to ignore the comma (not (eq word '.)) ; ditto (or (not len) (> (length word-str) len)) (or (not ignore-list) (not (memq word ignore-list)))) (if cell (setcdr cell (1+ (cdr cell))) (setq words (cons (cons word 1) words)))))) ;; done, so restore table (set-syntax-table cur-table) (when (interactive-p) (message "%S" words)) words))
The following sexps are examples of how to use this enhanced version of word-count-analysis.
(let ((tot 0) ;; ignore specific words ;;(l (word-count-analysis (point-min) (point-max) '(i a an the))) ;; ;; ignore one-character words ;;(l (word-count-analysis (point-min) (point-max) nil 1)) ;; ;; default (l (word-count-analysis (point-min) (point-max)))) (mapcar '(lambda (cell) (setq tot (+ tot (cdr cell)))) l) (format "total = %d" tot))
(insert (pp (sort (word-count-analysis (point-min) (point-max)) '(lambda (a b) (> (cdr a) (cdr b)))))))
(mapcar '(lambda (cell) (let ((w (car cell)) (ct (cdr cell))) (while (> ct 0) (insert (format "%s\n" w)) (setq ct (1- ct))))) (sort (word-count-analysis (point-min) (point-max)) '(lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b))))))