在 java 中使用 hadoop Wordcount 删除标点符号和 HTML 实体
Remove punctuation and HTML entity with hadoop Wordcount in java
我尝试使用 java 来自 hadoop Apache (https://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html)。如果我只删除带有分隔符的标点符号,它会很好地工作,就像我从 StringEscapeUtils 包中删除带有 unescapeHtml(word) 的 HTML 实体一样。
但是当我 运行 将它们放在一起时,HTML 实体仍然存在,我看不出我的代码有什么问题。
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString(),".,;:!?()[]\t\n\r",true);
while (itr.hasMoreTokens()) {
String next_word = itr.nextToken();
if(next_word.contains("&")){
next_word = StringEscapeUtils.unescapeHtml(next_word);
}
word.set(next_word);
context.write(word, one);
}
}
}
谁能帮我解释一下这是什么问题?
这是使用 regular expressions 从输入文件中的文本中过滤掉 HTML 实体和标点符号的典型案例。
为此,我们需要创建两个正则表达式,分别用于匹配 HTML 实体和标点符号,并将它们从文本中移除,最终设置为键值对剩余的有效词进行配对。
从 HTML 实体开始,例如
、<
和 >
,我们可以发现这些标记总是以 &
开头字符并以 ;
字符结尾,中间有一些字母字符。所以根据 RegEx 语法(你可以自己研究,如果你还没有,它真的很有价值),下面的表达式匹配所有这些标记:
&.*?\w+;
(因为我们也可以使用在线正则表达式测试器进行测试 here):
接下来对于标点符号,我们可以通过简单地查找既不是字母也不是数字(当然也不是空格)的字符来简单地匹配它们,例如下一个正则表达式:
[^a-zA-Z0-9 ]
(删除与之前的正则表达式匹配的 HTML 个实体后,再次使用在线正则表达式测试器 here 进行测试):
因此,为了使用这些正则表达式,我们可以简单地使用 replaceAll()
方法,该方法基于第一个参数的正则表达式,将与其匹配的所有标记更改为第二个参数字符串。在这里,我们可以将所有匹配的标记更改为一个简单的空格,并在最后继续删除所有双空格,因此只有有效的单词保留为映射器的键值对中的键。
所以程序现在看起来像这样:
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordCount
{
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>
{
private final static IntWritable one = new IntWritable(1);
public void map(Object key, Text value, Context context) throws IOException, InterruptedException
{
String line = value.toString();
// clean up the text of the line by removing...
line = line.replaceAll("&.*?\w+;", " ") // HTML entities...
.replaceAll("[^a-zA-Z0-9 ]", " ") // punctuation...
.replaceAll("\s+", " "); // and getting rid of double spaces
// if the line has remaining words after the cleanup...
if(line != null && !line.trim().isEmpty())
{
String[] words = line.split(" "); // split the text to words
// set each word as key to the key-value pair
for(String word : words)
context.write(new Text(word), one);
}
}
}
public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable>
{
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException
{
int sum = 0;
for (IntWritable val : values)
{
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception
{
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
并使用以下文本文件内容作为输入:
hello people! how are you?
i am better than ever how about < you >?
i just found three € on the floor....
so damn lucky good for you..!
thank you @@@@@ :)
这是给定的输出:
我尝试使用 java 来自 hadoop Apache (https://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html)。如果我只删除带有分隔符的标点符号,它会很好地工作,就像我从 StringEscapeUtils 包中删除带有 unescapeHtml(word) 的 HTML 实体一样。
但是当我 运行 将它们放在一起时,HTML 实体仍然存在,我看不出我的代码有什么问题。
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString(),".,;:!?()[]\t\n\r",true);
while (itr.hasMoreTokens()) {
String next_word = itr.nextToken();
if(next_word.contains("&")){
next_word = StringEscapeUtils.unescapeHtml(next_word);
}
word.set(next_word);
context.write(word, one);
}
}
}
谁能帮我解释一下这是什么问题?
这是使用 regular expressions 从输入文件中的文本中过滤掉 HTML 实体和标点符号的典型案例。
为此,我们需要创建两个正则表达式,分别用于匹配 HTML 实体和标点符号,并将它们从文本中移除,最终设置为键值对剩余的有效词进行配对。
从 HTML 实体开始,例如
、<
和 >
,我们可以发现这些标记总是以 &
开头字符并以 ;
字符结尾,中间有一些字母字符。所以根据 RegEx 语法(你可以自己研究,如果你还没有,它真的很有价值),下面的表达式匹配所有这些标记:
&.*?\w+;
(因为我们也可以使用在线正则表达式测试器进行测试 here):
接下来对于标点符号,我们可以通过简单地查找既不是字母也不是数字(当然也不是空格)的字符来简单地匹配它们,例如下一个正则表达式:
[^a-zA-Z0-9 ]
(删除与之前的正则表达式匹配的 HTML 个实体后,再次使用在线正则表达式测试器 here 进行测试):
因此,为了使用这些正则表达式,我们可以简单地使用 replaceAll()
方法,该方法基于第一个参数的正则表达式,将与其匹配的所有标记更改为第二个参数字符串。在这里,我们可以将所有匹配的标记更改为一个简单的空格,并在最后继续删除所有双空格,因此只有有效的单词保留为映射器的键值对中的键。
所以程序现在看起来像这样:
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordCount
{
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>
{
private final static IntWritable one = new IntWritable(1);
public void map(Object key, Text value, Context context) throws IOException, InterruptedException
{
String line = value.toString();
// clean up the text of the line by removing...
line = line.replaceAll("&.*?\w+;", " ") // HTML entities...
.replaceAll("[^a-zA-Z0-9 ]", " ") // punctuation...
.replaceAll("\s+", " "); // and getting rid of double spaces
// if the line has remaining words after the cleanup...
if(line != null && !line.trim().isEmpty())
{
String[] words = line.split(" "); // split the text to words
// set each word as key to the key-value pair
for(String word : words)
context.write(new Text(word), one);
}
}
}
public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable>
{
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException
{
int sum = 0;
for (IntWritable val : values)
{
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception
{
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
并使用以下文本文件内容作为输入:
hello people! how are you?
i am better than ever how about < you >?
i just found three € on the floor....
so damn lucky good for you..!
thank you @@@@@ :)
这是给定的输出: