WordCount

The Simple Solution: Counting Words on Demand

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.

Show the number of words 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

Version updated to Emacs 24 (as of Jul. 16, 2015): https://github.com/tomaszskutnik/word-count-mode

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.

For emacs24 compatibility, comment out the line that reads “(make-local-hook hook)”.

wc-mode: show "wc"-like info in the mode-line

wcMode (‘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 wc command). If there’s an active region, it displays that information for the region instead. It requires Emacs 24 or newer.

All this small “one-liner” package does is set the ‘mode-line-position’ variable appropriately (see the code), conveniently wrapped in a minor-mode to make it toggleable.

Somewhat related: modeline-posn

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.

Various other ways to show the number of words

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+"))))

I wrote a quick elisp file based on this logic: wc.el. I can’t understand why something like it isn’t standard. --TheronTlax

If you are like me, you can’t remember whether 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

or count the number of words in the region with the command

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.

Other solutions

    ;; 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

Use wc the unix way

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)))

To pipe the contents of region to the wc command-line tool:

 (defun wc () (interactive) (shell-command (concat "echo " (buffer-substring (mark) (point)) " | wc")))

Maybe add a keybinding (C-c w):

 (global-set-key "\C-cw" 'wc)

How many times was each word used?

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))

Enhanced analysis

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))))))

References


CategoryDotEmacs