#emacs #org-mode #org-babel
#emacs #org-режим #org-babel
Вопрос:
Я пытаюсь создать организационную таблицу на основе содержимого файла CSV, который использует «;» в качестве разделителя.
Я думал, что было бы легко создать блок исходного кода для «cat» содержимого файла, а затем передать результат функции, которая создает таблицу, но я застрял: я не могу найти способ использовать «результаты» первого блока исходного кода. Функция, которую я знаю (org-table-convert-region), ожидает, что регион будет работать, и я не знаю, как передать текст «cat» в качестве региона.
# NAME: csvraw
# BEGIN_SRC sh :results raw
cat afile.csv
# END_SRC
Я был бы признателен за вашу помощь в создании блока кода, который создает org-table из моего csv-файла, который содержит строки, подобные следующим:
ID;Region;SubRegion;Area;No
1234;Asia;India;45;2
24251;Europe;Romania;456;67
Ответ №1:
Существует org-table-convert-region
(привязанный к C-c |
), который может выполнить преобразование довольно просто. Единственный трюк — указать ;
в качестве разделителя. Вы можете сделать это, вызвав его с соответствующим префиксным аргументом — строка документа гласит:
(org-table-convert-region BEG0 END0 amp;optional SEPARATOR)
Convert region to a table.
The region goes from BEG0 to END0, but these borders will be moved
slightly, to make sure a beginning of line in the first line is included.
SEPARATOR specifies the field separator in the lines. It can have the
following values:
(4) Use the comma as a field separator
(16) Use a TAB as field separator
(64) Prompt for a regular expression as field separator
integer When a number, use that many spaces, or a TAB, as field separator
regexp When a regular expression, use it to match the separator
nil When nil, the command tries to be smart and figure out the
separator in the following way:
- when each line contains a TAB, assume TAB-separated material
- when each line contains a comma, assume CSV material
- else, assume one or more SPACE characters as separator.
(64)
Значение всего три C-u
в строке, поэтому процесс выглядит следующим образом:
- вставьте файл CSV с
C-x i
помощью . C-x C-x
пометить вставленное содержимое как активную область.C-u C-u C-u C-c | ; RET
Что еще круче, оставляя пустую строку в CSV-файле между первой строкой и остальными строками, первая строка автоматически станет заголовком таблицы.
И вы также можете обернуть это в блок кода:
# begin_src elisp :var file="/tmp/foo.csv" :results raw
(defun csv-to-table (file)
(with-temp-buffer
(erase-buffer)
(insert-file file)
(org-table-convert-region (point-min) (point-max) ";")
(buffer-string)))
(csv-to-table file)
# end_src
# RESULTS:
| a | b | c |
|--- --- ---|
| d | e | f |
| g | h | i |
Комментарии:
1. Большое вам спасибо за ваш очень информативный ответ. Предлагаемое вами решение компактно и в то же время чрезвычайно «разборчиво».
Ответ №2:
(defun jea-convert-csv-to-org-table (fname)
(interactive "fCSV to convert: ")
(let ((result '("|-n")))
(with-temp-buffer
(save-excursion (insert-file-contents-literally fname))
(while (and (not (eobp)) (re-search-forward "^\(. \)$" nil t nil))
(push (concat "|" (replace-regexp-in-string ";" "|" (match-string 1)) "|n")
result))
(push '"|-n" result))
(concat (seq-mapcat #'identity (reverse result)))))
установите код elisp в свой файл ~/.emacs и перезапустите emacs. Или, что еще лучше, eval
его в существование (CTRL x, CTRL e или ALT x eval-last-sexp).
# NAME: csvraw
# BEGIN_SRC elisp :results raw
(jea-convert-csv-to-org-table "/Users/jamesanderson/Downloads/test1.csv")
# END_SRC
обратите внимание на изменение на elisp
from sh
в приведенном выше. вот gif-изображение этого в действии:
emacs преобразует csv в org table
Код не оптимизирован для больших файлов и, честно говоря, довольно быстро скомпонован. Но, после небольшого тестирования, похоже, работает.
Комментарии:
1. Вы могли бы повысить эффективность, просто изменив текст буфера и извлекая его все сразу,
(defun nvp-convert-csv-to-org-table (fname) (interactive (list (read-file-name "CSV to convert: "))) (with-temp-buffer (save-excursion (insert-file-contents-literally fname)) (while (not (eobp)) (insert "|") (while (search-forward ";" (line-end-position) 'move) (replace-match "|")) (insert "|") (forward-line)) (buffer-string)))
2. Джеймс, большое вам спасибо за ваш ответ и за то, что нашли время ответить на мой запрос. Я следовал вашим инструкциям, используя файл образца csv, упомянутый в моем вопросе, но что-то пошло не так: функция создает новый буфер и копирует туда содержимое файла csv, но зависает, заставляя меня
<kbd>Ctrl-g</kbd>
несколько раз восстанавливать контроль над emacs.3. @jenesaisquoi, спасибо за предложение улучшений. Я собираюсь попробовать вашу версию и опубликовать здесь, когда закончу.
4. @jenesaisquoi правильно: я использовал Edebug, чтобы выяснить, что происходит, и, действительно, цикл while продолжается вечно: он продолжает
|n
объединять строку вresult
.5. Джеймс, я внес изменения в предложенный вами код, добавив условие завершения для цикла while. Я отредактировал ваш ответ, чтобы отразить изменение: я думаю, мне придется подождать, пока поправка будет рассмотрена экспертами, чтобы она стала видимой.