我需要获取文件名和整数N,并返回给定文件中的前N个唯一单词。我们说input.txt
有这样的内容:
I like pancakes in my breakfast. Also, I like pancakes in my dinner.
以N = 13运行此项的输出可能是
I
like
pancakes
in
my
breakfast.
Also,
dinner.
我知道如何打开文件并逐行阅读,但除此之外,我不知道如何删除这些独特的单词。
答案 0 :(得分:3)
让我们先创建一个测试文件。
str =<<END
We like pancakes for breakfast,
but we know others like waffles.
END
FName = 'temp'
File.write(FName, str)
#=> 65 (characters written)
我们需要从名为nbr_unique
的文件中返回一个包含第一个file
个唯一单词的数组,所以让我们编写一个可以做到这一点的方法。
def unique_words(fname, nbr_unique)
<code needed here>
end
你需要为这个方法返回的数组添加唯一的单词,所以让我们先创建一个空数组,然后在方法的末尾返回该数组。
def unique_words(fname, nbr_unique)
arr = []
<code needed here>
arr
end
你知道如何逐行阅读文件,所以我们使用类方法IO::foreach 1 来做到这一点。
def unique_words(fname, nbr_unique)
arr = []
File.foreach(fname) do |line|
<code need here to process line>
end
arr
end
在读取第一行后,块变量line
等于"We like pancakes for breakfast,\n"
。首先,需要删除换行符。检查班级的方法
String看看是否有人可以这样做。
第二行包含单词"we"
。我认为"We"
和"we"
不应被视为唯一字词。这通常通过将字符串的所有字符转换为全部小写或全部大写来处理。您可以对每一行或每个单词执行此操作(在从一行中提取单词之后)。再次,在类String
中寻找合适的方法来执行此操作。
接下来,您需要从每一行中提取单词。再一次,找一个String
方法来做到这一点。
接下来,我们需要确定是否要将"like"
(或"LIKE"
)添加到数组arr
。查看类Array的实例方法以获取合适的方法。如果已添加,我们需要查看arr
现在是否包含nbr_unique
个字词。如果是这样,我们就不需要再阅读该文件的任何行,因此我们需要突破foreach
的块(可能使用关键字break
)。< / p>
还有一件事我们需要照顾。第一行包含"breakfast,"
,第二行包含"waffles."
。我们显然不希望返回的单词包含标点符号。有两种方法可以做到这一点。第一种是删除标点符号,第二种是只接受字母。
给定一个包含标点符号(一行或一个单词)的字符串,我们可以创建第二个字符串,该字符串等于删除了标点符号的原始字符串。一种方法是使用方法String#tr。假设字符串是"breakfast,"
。然后
"breakfast,".tr(".,?!;:'", "") #=> "breakfast"
要仅接受字母,我们可以使用以下任何正则表达式(全部返回"breakfast"
):
"breakfast,".gsub(/[a-zA-Z]+/, "")
"breakfast,".gsub(/[a-z]+/i, "")
"breakfast,".gsub(/[[:alphaa:]]+/, "")
"breakfast,".gsub(/\p{L}+/, "")
前两个仅使用ASCII字符。使用Unicode的第三个(POSIX)和第四个工作( \ p {}构造)(在Regexp内搜索)。
请注意,在提取单词之前从行中删除标点符号会更有效。
额外赠送金额:使用Enumerator#with_object
每当你看到一个对象(这里arr
)被初始化为空,被操纵然后在方法结束时返回时,你应该考虑使用方法Enumerator#with_object
或(更常见的), Enumerable#each_with_object。这两个都返回方法名称中引用的对象。
方法IO::foreach
在没有块时返回枚举器(类Enumerator
的实例)(请参阅doc)。因此,我们可以写
def unique_words(fname, nbr_unique)
File.foreach(fname).with_object([]) do |line, arr|
<code need here to process line>
end
end
我们已经删除了两行(arr = []
和arr
),但也限制了arr
的范围。这不是什么大问题,而是Ruby方式。
更多额外信用:使用班级Set
的方法假设我们写了以下内容。
require 'set'
def unique_words(fname, nbr_unique)
File.foreach(fname).with_object(Set.new) do |line, set|
<code need here to process line>
end.to_a
end
当我们从第二行提取单词"we"
时,我们需要检查它是否应该添加到集合中。由于集合具有独特的元素,我们可以尝试这样做。我们无法做到这一点,因为set
已经包含了文件第一行中的那个单词。这样做的一个方便的方法是Set#add?:
set.add?("we")
#=> nil
此处方法返回nil
,表示该集已包含该单词。它还告诉我们,我们不需要检查该集合现在是否包含nbr_unique
个单词。如果我们能够将单词添加到集合中,则会返回set
(带有添加的单词)。
该块返回set
(一组)的值。方法Set#to_a将该集转换为数组,该数组由方法返回。
1请注意,我已在下面编写IO::foreach
来调用类方法File.foreach(fname)...
。这是允许的,因为File
是IO
(File.superclass #=> IO
)的子类。我本来可以写IO.foreach(fname)...
,但更常见的是使用File
作为接收者。