Clojure: hak van de eerste spatie gescheiden tekens

Ik wil een bestand analyseren en filteren dat er als volgt uitziet:

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

Ik heb het filteren van de rijen kunnen uitvoeren met de volgende code:

(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)))

Deze code geeft mij echter (als sel-row = 1):

1 Row one.

Ik zou die 1 en de spatie erna willen afhakken, om zo te hebben:

Row one.

Ik denk dat er wat sequentiemagie is om dit te doen. Ik kan gewoon geen elegante oplossing verzinnen.

2

3 antwoord

Ik zou de functie op de volgende manier definiëren:

(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))))

De combinatie van line-seq en reader geeft me een reeks regels uit het invoerbestand. met open zorgt ervoor dat het bestand correct wordt gesloten als ik klaar ben. Ik gebruik een regex op elke regel die zoekt naar @@ gevolgd door een nummer en enkele spaties.

re-find returns a vector with three items:

  • de hele overeenkomende regel
  • het nummer (de eerste groep in de regex)
  • de rest van de regel (de tweede groep in de regex)

Ik bind deze aan nummer en regel met destructurering in een voor -instructie (ik ben niet geïnteresseerd in de hele overeenkomende regel, dus ik negeer dat ). Ik filter voor de geselecteerde sel-rij met : wanneer en lever alleen de (rest van de) regel op.

Omdat ik slechts één overeenkomst in het bestand verwacht, retourneer ik alleen het eerste item uit de reeks die is gemaakt met voor . Vanwege de luiheid van voor , kaart en line-seq stopt dit ook met het lezen van het bestand nadat het item is gevonden.

Als je veel opzoekingen doet voor rijen, zou ik willen voorstellen het hele bestand in het geheugen te laden in plaats van het elke keer te lezen.

2
toegevoegd
De reader moet echt worden gemaakt in een omringend met open -formulier.
toegevoegd de auteur Alex Taggart, de bron
Natuurlijk. Slechte Ik! Bewerkt.
toegevoegd de auteur Christian Berg, de bron
Bedankt. Ik ben nieuw om te clojure, kun je meer informatie geven over het laden in het geheugen? Misschien wat documenten?
toegevoegd de auteur kfk, de bron

Een andere oplossing is om een ​​functionele parser-bibliotheek te gebruiken, zoals dj-peg (die ik heb geschreven).

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

Dan kun je dit schrijven:

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

De functie parseren gebruikt de parser geretourneerd door p/token om de tekst in de rij te ontleden. Het retourneert een vector met de eerste waarde als resultaat van de ontleding en de tweede is de resterende invoer. Daarom, als we de tweede noemen, krijgen we de rest van de lijn. Dit wordt geretourneerd

 "the remaining line\n"

Ik raad aan om de bibliotheek te bekijken. Het is geschreven in pseudo-geletterde programmeerstijl, dus de broncode leest vrij soepel. U zou het parseermodel moeten kunnen begrijpen na het doorlopen van de broncode.

1
toegevoegd

Het eerder gegeven antwoord met line-seq en destructurering van een regex-groep werkt goed voor de gegeven use-case.

In a general situation where all you want is string manipulation clojure.core includes thesubs function. http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/subs

subs is implemented using java interop and the substring method of the java String class.


user=> (subs "abcdef" 1)
"bcdef"
user=> (subs "abcdef" 2 4)
"cd"
0
toegevoegd