1

the title gives it all away:

I want this

9874599842006432.08

To become this

9,874,599,842,006,432.08

with 1 single replace regexp operation and flexible, so that the same string also would work on a shorter or longer number like for example 10777524,62 .

Context: https://en.wikipedia.org/wiki/Decimal_separator#Digit_grouping

I've worked really hard on figuring it out, including turning to manuals and tutorials, but sadly in vein.

Example 1: The closed I could come up with is this here, where I simplified by expressing 3 digit groups with a simple letter from the alphabet, to have it easier to understand what I'm doing there.

Query replace regexp (default \([0-9]\)\(n\)\(n\)\(n\)\(n\)\(n\)\(.\) -> \1,\2,\3,\4,\5,\6\7)

The idea: Grouping all 3 digit groups separately and referably, then replacing the whole match by reconstruction of it extended by desired changes - possible by spliting all relevant parts up in grouping constructs with round brackets.

But the problem is: It's fixed. Give the string a number with 5 n's - it works. Give it a smaller number, like for example, with just 3 n's - and it fails by not detecting it for a replace operation.

Example 2: Another example

Query replace regexp (default \([0-9]\)\(n\)\(.\) -> \1,\2\3):

Turns

1n.07

into

1,n.07

Nice. But what about bigger numbers? Dealing with just 1 n is hard-wired into the string.

And what about applying '*' into it?

Giving a string like

1nnn.57

Query replace regexp (default \([0-9]\)\(n*\)\(.\) -> \1,\2\3):

At least grabs the relevant part in one operation, no matter how many n's are part of it,

1nnn.

But instead of turning it into this

1,n,n,n.57

its output is this

1,nnn.57

So, you see: Regarding the finding part, it is capable to detect any number, no matter how many n's it consists of, but regarding the replacing part, it fails to apply the modification not just for 1 n, but for all n's it detected by * - here 3 times at once.

Any better ideas?

1
  • It sounds like you're trying to find a single-pass solution to a problem which necessitates multiple passes, and inevitably failing. Unless you hard-code the number of sub-groups or use elisp in the replacement, I don't see how a single search/replace could achieve this. You need to match the digits first, and then iterate over the matches to add each of the commas.
    – phils
    Jan 31 at 22:55

1 Answer 1

4

I am not sure about your use-case, but anyway I would recommend using any of the methods mentioned in this reddit post.

So, if you'd like to 'commify' all numbers in some buffer, then you could use the add-number-grouping function from the Emacs wiki, and create a custom function to replace all numbers in a buffer as follows:

(defun add-number-grouping (number &optional separator)
  "Add commas to NUMBER (a string) and return it as a string.
    Optional SEPARATOR is the string to use to separate groups.
    It defaults to a comma."
  (let ((op (or separator ",")))
    (while (string-match "\\(.*[0-9]\\)\\([0-9][0-9][0-9].*\\)" number)
      (setq number (concat 
            (match-string 1 number) op
            (match-string 2 number))))
    number))

(defun commify-numbers ()
  (interactive)
  (while (search-forward-regexp " \\([0-9][0-9.]+\\)" nil t)
    (let ((from (match-beginning 1))
          (to (match-end 1))
          ;; bind match-data now as it gets updated by calling
          ;; `add-number-grouping'
          (num (match-string-no-properties 1)))
      ;; because `add-number-grouping' updates the match-data, we can not use
      ;; replace-match directly (and instead use `replace-region-contents`)
      (replace-region-contents from to (lambda () (add-number-grouping num))))))

Note that here the add-number-grouping function has been modified to work on a (number-)string (instead of a number directly).

You probably can adapt the code to apply to your use-case.

1
  • 1
    Thanks alot, dalanicolai, that was just what I needed to get things rollin' once again in my personal quest. I think from here on I can cope with things for now.
    – starquake
    Feb 1 at 22:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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