0

I'd like to convert numerals from half-width to full-width characters with a simple regex search and replace as follows:

replace-regexp-in-string regexp rep string &optional fixedcase literal subexp start

(replace-regexp-in-string '(0 1 2 3 4 5 6 7 8 9) '(0 1 2 3 4 5 6 7 8 9))

Evaluating the above code in a buffer gave me this error:

(wrong-number-of-arguments (3 . 7) 2)

I suppose the missing argument is the string, which I assumed would take care of itself when I evaluate the code using M-: on a buffer.

How do I do this correctly?


Follow-up Question:

I would like to abstract the commands given in @Tobias' answer into a single function, so that I can call them with a single key-binding.

Here's my attempt:

(defun num-half2full
    (query-replace-regexp [0-9]
              /,(string (+ (string-to-char \&) (- ?0 ?0)))))

Error:

(error "Malformed arglist: (query-replace-regexp [0-9] / (, (string (+ (string-to-char &) (- 65296 48)))))")

My last attempt at writing the function:

(defun num-full2half () "Convert numbers from full-width to half-width" (interactive) (goto-char (point-min)) (replace-regexp "[0-9]" (quote (replace-eval-replacement replace-quote (string (+ (string-to-char (match-string 0)) 48 (- 65296)))))))

The expected replacement did not take place in the buffer at which M-x<\kbd>(num-full2half) was called.

3

No, regplace-regexp-in-string does not magically substitute the buffer string into the STRING argument. It is a function but not a command (does not have an interactive specification).

The command you are actually looking for is query-replace-regexp which can be used interactively and is bound to C-M-%.

Use: C-M-% [0-9] RET \,(string (+ (string-to-char \&) (- ?0 ?0))) RET to replace normal digit chars with wide digit chars.

The escape sequence \,(...) allows you to call Elisp from the replacement string. In our case the regexp is the character group [0-9] of normal digits. Used Elisp forms:

  • ?0 and ?0 give the character codes for the wide zero char and the normal zero char (e.g., ASCII code 48 for ?0)
  • (- ?0 ?0) computes the offset between the wide digit zero char and the normal digit zero char
  • \& is replaced with the match string, (e.g., "1")
  • (string-to-char \&) delivers the char code of the first char in the match string, (in our case the match string only contains one digit char)
  • (+ (string-to-char \&) (- ?0 ?0))) adds the offset between wide digits and normal digits to the found digit char.
  • (string (+ (string-to-char \&) (- ?0 ?0)))) Generates a string containing only the wide digit char that we just have calculated.

The inverse is also possible:

Use: C-M-% [0-9] RET \,(string (+ (string-to-char \&) (- ?0 ?0))) RET to replace wide digit chars with normal digit chars. The essential differences to the query-replace above are:

  • we search for wide digit chars instead of normal digit chars
  • we have changed the sign of the offset (- ?0 ?0) instead of (- ?0 ?0)

The command M-x list-command-history RET reveals the actually called command:

(query-replace-regexp "[0-9]"
  (quote (replace-eval-replacement replace-quote
    (string (+ (string-to-char (match-string 0)) 48 (- 65296))))) nil nil nil nil nil)

As Elisp beginner you can just use that code in your Elisp function. (Even if the doc-string of query-replace-regexp says that this function is for interactive use only.)

If you want to avoid the query you can remove the prefix query- and all the nils at the tail of the argument list:

(replace-regexp "[0-9]"
  (quote (replace-eval-replacement replace-quote
    (string (+ (string-to-char (match-string 0)) 48 (- 65296))))))

Now a word about what the doc string of query-replace-regexp actually suggests.

It wants you to write a loop:

(goto-char (point-min)) ;; maybe first go to the beginning of the accessible buffer
(while (re-search-forward "[0-9]" nil t)
  (replace-match (string (+ (string-to-char (match-string 0)) (- ?0 ?0))))
  • This replaces normal characters 0 1 2 3 etc. with full-width 0123 etc. ? I actually need the other way round. And because [0-9] doesn't work in regex, we might need do specify the search elements individually: [0123] etc. – Sati Nov 12 at 14:51
  • How do we make sure the full and half-width digits map to each other? – Sati Nov 12 at 14:52
  • @Sati See my edit. You can also replace wide digit chars with normal digit chars. The character group [0-9] actually works. (Tested with GNU Emacs 26.3 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.22.30) of 2019-09-16 on WSL.) The characters for the numbers have consecutive codes in their natural order. – Tobias Nov 12 at 15:17
  • 1
    You can use ucs-normalize-NFKC-string to convert the full width 0 into the half width 0. – xuchunyang Nov 12 at 15:28
  • 1
    @Sati Just do what you need interactively and call M-x list-command-history aferwards. There you find the corresponding lisp forms whose you can use in your Elisp code. (E.g., define a one-keystroke command.) – Tobias Nov 12 at 15:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.