从包含匹配字符串的两个文件中选择行

时间:2015-10-03 22:19:41

标签: pattern-matching match text-processing

假设我有两个文件A和B. A的内容:

foo1 foo2
bar1 bar3

B的内容:

bar2 bar3
foo3 foo4

如何从A中选择第二行,从B中选择第一行?没有搜索字符串。我需要选择包含所有可能的公共字符串的行。

请注意,我不是在寻找两个不同文件的匹配行。所需的行不相同,但包含一个共同的字符串。

任何帮助将不胜感激。谢谢!

1 个答案:

答案 0 :(得分:1)

TXR Lisp中的解决方案:

$ txr common-word-lines.tl file1 file2
bar1 bar3
bar2 bar3

common-word-lines.tl中的代码:

(defun hash-file-words (name)
  (with-stream (s (record-adapter #/\s+/ (open-file name "r")))
    (hash-list (get-lines s) :equal-based)))

(defun lines-containing-words-in-both-hashes (name hash1 hash2)
  (let ((s (open-file name "r")))
    (mappend*
      (op if [some (tok-str @1 #/\S+/) (andf hash1 hash2)]
        (list @1))
      (get-lines s))))

(tree-case *args*
  ((file1 file2 extra . junk) (throwf 'error "too many arguments"));
  ((file1 file2)
   (let ((hash1 (hash-file-words file1))
         (hash2 (hash-file-words file2)))
     (put-lines (lines-containing-words-in-both-hashes file1 hash1 hash2))
     (put-lines (lines-containing-words-in-both-hashes file2 hash1 hash2))))
  (else (throwf 'error "insufficient arguments")))

这使得两次传递文件。在第一遍中,我们构建了两个文件中所有以空格分隔的单词的哈希值。在第二遍中,我们打印每个文件中的每一行,其中至少包含一个出现在两个哈希中的单词。

使用了惰性列表处理,因此虽然看起来我们正在读取整个文件,但事实上并非如此。 get-lines返回一个惰性列表。在hash-file-words中,实际上正在读取文件,因为hash-list函数正在向传入其中的惰性列表向前移动。在lines-containing-words-in-both-hashes中,使用了mappend*,它会懒惰地过滤列表并附加各个部分。

什么是(andf hash1 hash2)?首先,andf是组合子。它需要多个参数,这些参数都是函数,并返回一个函数,它是这些函数的短路和组合。 (andf a b c)生成一个函数,将其参数传递给函数a。如果返回nil(false),它将停止并返回nil。否则,它将其参数传递给b,并应用相同的逻辑。如果它一直到c,则返回c返回的任何值。其次,尽管hash1hash2是哈希表,但它们可以用作TXR Lisp中的函数。哈希表表现为单参数函数,它在哈希表中查找其参数,并返回相应的值,或者nil。因此(andf hash1 hash2)只使用AND组合器来构建一个函数,如果它的参数存在于两个哈希表中(与非nil值相关联),则该函数返回true。

因此,[some (tok-str @1 #/\S+/) (andf hash1 hash2)]表示将该行标记为单词,并报告其中一些是否在两个哈希中#34;。 @1(op ...)宏生成的匿名函数的隐式参数。为(get-lines)生成的列表中的每个元素调用该函数;即文件的每一行。所以@1先后表示每一行。

更通用的版本:更短,并处理两个或更多参数:

(defun hash-file-words (name)
  (with-stream (s (record-adapter #/\s+/ (open-file name "r")))
    (hash-list (get-lines s) :equal-based)))

(defun lines-containing-words-in-all-hashes (name hashes)
  (let ((s (open-file name "r")))
    (mappend*
      (op if [some (tok-str @1 #/\S+/) (andf . hashes)]
        (list @1))
      (get-lines s))))

(unless *args*
  (put-line `specify one or more files`)
  (exit 1))

(let ((word-hashes [mapcar hash-file-words *args*]))
  (each ((file *args*))
    (put-lines (lines-containing-words-in-all-hashes file word-hashes))))
相关问题