How to increment a serie of number on the same row in a configuration file?
Asked Answered
S

7

5

Let say I have a configuration file, and each lines contains space separated values. On on the column I have only zeros. Example:

... # there is more configuration before the interesting stuff:
0 0 file /dev/stdin 224.0.68.54:12131
0 0 file /dev/stdin 224.0.68.55:12102
0 0 file /dev/stdin 224.0.68.49:12333
0 0 file /dev/stdin 224.0.68.60:12184
0 0 file /dev/stdin 224.0.68.62:12888
0 0 file /dev/stdin 224.0.68.77:12001
0 0 file /dev/stdin 224.0.68.33:12973

Now I want to increment the second column with its index. that is I want this result:

0 0 file /dev/stdin 224.0.68.54:12131
0 1 file /dev/stdin 224.0.68.55:12102
0 2 file /dev/stdin 224.0.68.49:12333
0 3 file /dev/stdin 224.0.68.60:12184
0 4 file /dev/stdin 224.0.68.62:12888
0 5 file /dev/stdin 224.0.68.77:12001
0 6 file /dev/stdin 224.0.68.33:12973

How to to that in emacs lisp? Or any other Emacsen way of doing thing please?

Subastral answered 4/6, 2013 at 14:54 Comment(2)
when reaching "10" the line shall get one character longer?Tawnytawnya
@PeterMiehle yes it is.Subastral
W
7

You can use a macro with a counter to do that. You start defining a macro with F3 and end the definition with F4. While defining the macro, hitting F3 again will insert the value of the counter and increment it. After defining the macro, run it by hitting F4.

So in your case, move point to the beginning of the first interesting line, hit F3 C-f C-f C-d F3 C-n C-a F4 (i.e. remove the second zero, insert the counter, and move to the beginning of the next line). Then hit F4 as many times as needed to change all the lines.

If you need to change the value of the counter, use M-x kmacro-set-counter.

Wert answered 4/6, 2013 at 15:7 Comment(2)
Ok that's a nice answer. I didn't knew the <kbd>F3</kbd> tricks to have an incremented counter while defining a macro. Thnx for that. By the way, I would have used <kbd>M-D</kbd> instead of <kbd>C-D</kbd>. Because I can have numerics longer that one character. My example was not clear about, as a consequence your answer is perfectly valid :)Subastral
For the record: <kbd>F3</kbd> was not working for me. But <kbd>C-x C-k C-i</kbd> (`kmacro-insert-counter') was! :)Subastral
B
10

You can use the possibility to search and replace by an evaluated expression:

  • put the point on the first line to process
  • M-x query-replace-regexp RET
  • searched string: ^0 \([0-9]+\)
  • replace with: \,(format "0 %s" (+ \#1 \#))

The meaning is:

  • search a number preceded by a single zero at the beginning of the line
  • replace by the result of the evaluation where \#1 is the first matched group (like \1 but converted to number) and \# is the number of replacements already done (begins at 0 for the first replacement). The expression is evaluated for each match.

If the first number is not always a zero, I would use:

  • searched string: ^\([0-9]+\) \([0-9]+\)
  • replace with: \,(format "%s %s" \#1 (+ \#2 \#))
Braasch answered 4/6, 2013 at 15:39 Comment(0)
W
7

You can use a macro with a counter to do that. You start defining a macro with F3 and end the definition with F4. While defining the macro, hitting F3 again will insert the value of the counter and increment it. After defining the macro, run it by hitting F4.

So in your case, move point to the beginning of the first interesting line, hit F3 C-f C-f C-d F3 C-n C-a F4 (i.e. remove the second zero, insert the counter, and move to the beginning of the next line). Then hit F4 as many times as needed to change all the lines.

If you need to change the value of the counter, use M-x kmacro-set-counter.

Wert answered 4/6, 2013 at 15:7 Comment(2)
Ok that's a nice answer. I didn't knew the <kbd>F3</kbd> tricks to have an incremented counter while defining a macro. Thnx for that. By the way, I would have used <kbd>M-D</kbd> instead of <kbd>C-D</kbd>. Because I can have numerics longer that one character. My example was not clear about, as a consequence your answer is perfectly valid :)Subastral
For the record: <kbd>F3</kbd> was not working for me. But <kbd>C-x C-k C-i</kbd> (`kmacro-insert-counter') was! :)Subastral
D
7

Similarly to Jonathan Leech-Pepin's answer, you can also use the non-cua rectangle editing commands (albeit in two steps instead of one).

  • Set mark and point to the corners of the column rectangle
  • C-xrk to delete the rectangle.
  • C-uC-xrN to insert a number sequence in its place.

Also see related question:
emacs string-insert-rectangle vector of numbers?

Dynamotor answered 5/6, 2013 at 23:55 Comment(0)
U
4

Something you could do using org-mode (I'm not sure of how will you use the result, but this can be one way of doing it)

| N | X | file | stream     |                ip |
|---+---+------+------------+-------------------|
| 0 | 0 | file | /dev/stdin | 224.0.68.54:12131 |
| 0 | 1 | file | /dev/stdin | 224.0.68.55:12102 |
| 0 | 2 | file | /dev/stdin | 224.0.68.49:12333 |
| 0 | 3 | file | /dev/stdin | 224.0.68.60:12184 |
| 0 | 4 | file | /dev/stdin | 224.0.68.62:12888 |
| 0 | 5 | file | /dev/stdin | 224.0.68.77:12001 |
| 0 | 6 | file | /dev/stdin | 224.0.68.33:12973 |
#+TBLFM: $2=@#-2

Some explanation, or rather, how to get your original data and transform it into this table:

  • Copy your data into file config.org

  • Create the table by basic string replacement commands. (Just copy the text as you have it now, select all of it and then C-c |)

  • The #+TBLFM: part is the "formula" for this table, it means the following: assign to every cell in the second column the ordinal of it's row minus 2 (they start counting rows at 1, and this table has a header row).

  • Whenever you C-c C-c on the formula, it will re-apply it to the table (so you can append more rows as you modify the document, reapplying the formula will automatically update the table.)

Underpass answered 4/6, 2013 at 16:48 Comment(1)
This is a more convenient method.Boeotia
P
2

You can use cua-set-rectangle-mark.

Evaluate:

(cua-mode 't)

Then in the buffer you can use M-x cua-set-rectangle-mark at the second column then extend the rectangle down to the end of the list.

Then use M-n (sequence), the defaults of Start (0):, Increment (1): and Format (%d): all provide what you need in this case.

Predisposition answered 5/6, 2013 at 13:48 Comment(1)
I generally recommend having cua-selection-mode on permanently -- you get the nice rectangle features available by default, without all the cut/copy/paste stuff.Dynamotor
P
0

Here a very easy way to do this (using the same technique as Seki's answer but easier):

Select all the lines you want the increment to appear in, then issue

M-x replace-regexp "^0 0" "0 \#"

or in more detail:

  • M-x replace-regexp RET
  • Searched string: ^0 0
  • Replace with: 0 \#

This will give you the desired result, because \# replaces to the number of replacements done.

Phaedrus answered 18/5, 2019 at 7:27 Comment(0)
S
0

I run into this problem all the time, and I don't enjoy setting up complicated rectangles or macros. So I created an elisp function to do it for me in a single command. I call it...*drumroll*...the downcrementer

Placing your cursor on the second 0 in your example and running M-x downcrementer will sequentially increment all the numbers in the column in one shot.

(defun downcrementer--bounds-of-num-at-point ()
  (interactive)
  (let (num-start num-end)
    (save-excursion
      (skip-chars-backward "0-9")
      (setq num-start (point)))
    (save-excursion
      (skip-chars-forward "0-9")
      (setq num-end (point)))
    (list num-start num-end)))

(defun downcrementer (&optional prefix-arg)
  (interactive "P")
  (let ((current-number (string-to-number
                         (apply #'buffer-substring-no-properties
                                (downcrementer--bounds-of-num-at-point))))
        (stride 1)
        (orig-truncate-lines truncate-lines))
    (when prefix-arg
      (setq stride (string-to-number (read-string "Enter stride: "))))
    ;; Prevent wrapping from causing next-line to go to the wrong place
    (toggle-truncate-lines t)
    (next-line)
    (let ((first-column (current-column)))
      (while (thing-at-point 'number t)
        (save-excursion
          (apply #'delete-region (downcrementer--bounds-of-num-at-point))
          (setq current-number (+ current-number stride))
          (insert (number-to-string current-number)))
        (next-line)
        (move-to-column first-column))
      (previous-line)
      (move-to-column first-column))
    ;; Restore original truncate-line setting
    (toggle-truncate-lines orig-truncate-lines)))
Spermatophyte answered 22/3, 2022 at 4:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.