Clojure: отбивка первых пробелов, разделенных символами - PullRequest
2 голосов
/ 06 декабря 2011

Я хочу проанализировать и отфильтровать файл, который выглядит следующим образом:

@@1 Row one. 
@@2 Row two.

Я смог выполнить фильтрацию строк с помощью следующего кода:

(defn parse-text-cms [sel-row]
  (let [f_data  (st/split  #"@@" (slurp "cms/tb_cms.txt"))] 
  ;(prn (map #(take 1 %) f_data))))
  (filter  #(= (first (take 1 %)) sel-row) f_data)))

Тем не менее, этот код дает мне (если sel-row = 1):

1 Row one.

Я хотел бы отрубить эту 1 и пробел после, чтобы иметь:

Row one.

Я думаю, что есть некоторая магия последовательности, чтобы сделать это.Я просто не могу придумать элегантное решение.

Ответы [ 3 ]

2 голосов
/ 06 декабря 2011

Я бы определил функцию следующим образом:

(defn parse-text-cms [sel-row]
  (with-open [input (clojure.java.io/reader "cms/tb_cms.txt")]
    (first
     (for [[_ number line] (map (partial re-find #"@@(\d)+\s+(.*)")
                                (line-seq input))
           :when (= number (str sel-row))]
       line))))

Сочетание line-seq и reader дает мне последовательность строк из входного файла.with-open гарантирует, что файл будет правильно закрыт, когда я закончу.Я применяю регулярное выражение к каждой строке, которая ищет @@, за которой следуют число и несколько пробелов.

re-find возвращает вектор с тремя элементами:

  • вся совпадающая строка
  • номер (первая группа в регулярном выражении)
  • остальная часть строки (вторая группа в регулярном выражении)

Я связываю их с number и line, используя деструктуризацию в выражении for (меня не интересуетвся согласованная линия, поэтому я игнорирую это).Я фильтрую для выбранного sel-row, используя :when, и получаю только (остальные) line.

Поскольку я ожидаю только одно совпадение в файле, я возвращаю только первый элемент из последовательностипостроено for.Из-за лени for, map и line-seq это также останавливает чтение файла после того, как элемент найден.

Если вы выполняете много операций поиска строк, я бы предложил загрузитьвесь файл в память вместо того, чтобы читать его каждый раз.

1 голос
/ 07 декабря 2011

Другое решение заключается в использовании функциональной библиотеки синтаксического анализатора, такой как dj-peg (которую я написал).

https://github.com/bmillare/dj-peg

Тогда вы можете написать это:

 (require '[dj-peg :as p])
 (let [line "@@1 the remaining line\n"
       initial (p/token #"@@\d+\s+)]
       (second (p/parse initial line)))

Функция parse использует синтаксический анализатор, возвращаемый p / token, для разбора текста в строке. Он вернет вектор с первым значением в результате анализа, а второе - это оставшиеся входные данные. Следовательно, если мы вызываем second, мы получаем остаток строки. Запуск этого возвращает

 "the remaining line\n"

Я рекомендую проверить библиотеку. Он написан в псевдо-грамотном стиле программирования, поэтому исходный код читается довольно гладко. Вы должны понимать модель синтаксического анализа после прохождения исходного кода.

0 голосов
/ 07 декабря 2011

Предыдущий ответ с использованием line-seq и деструктуризация групп регулярных выражений хорошо работает для данного варианта использования.

В общей ситуации, где все, что вам нужно, это манипулирование строками clojure.core включает в себя функцию subs. http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/subs

subs реализовано с использованием взаимодействия Java и метода substring класса Java String.


user=> (subs "abcdef" 1)
"bcdef"
user=> (subs "abcdef" 2 4)
"cd"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...