Đây là giải pháp có thể sử dụng Apache Lucene. Tôi không sử dụng phiên bản cuối cùng nhưng là 3.6.2 one, vì đây là phiên bản tôi biết rõ nhất. Bên cạnh /lucene-core-x.x.x.jar
, đừng quên thêm /contrib/analyzers/common/lucene-analyzers-x.x.x.jar
từ lưu trữ đã tải xuống vào dự án của bạn: nó chứa các trình phân tích ngôn ngữ cụ thể (đặc biệt là trình phân tích tiếng Anh trong trường hợp của bạn).
Lưu ý rằng điều này sẽ chỉ tìm tần suất của các từ văn bản nhập dựa trên gốc tương ứng. So sánh các tần số này với số liệu thống kê tiếng Anh sẽ được thực hiện sau đó (this answer có thể giúp bạn).
Các mô hình dữ liệu
Một từ khóa cho một gốc. Các từ khác nhau có thể có cùng một gốc, do đó tập hợp terms
. Tần suất từ khóa được tăng lên mỗi lần một thuật ngữ mới được tìm thấy (ngay cả khi nó đã được tìm thấy - một tập hợp tự động loại bỏ các bản sao).
public class Keyword implements Comparable<Keyword> {
private final String stem;
private final Set<String> terms = new HashSet<String>();
private int frequency = 0;
public Keyword(String stem) {
this.stem = stem;
}
public void add(String term) {
terms.add(term);
frequency++;
}
@Override
public int compareTo(Keyword o) {
// descending order
return Integer.valueOf(o.frequency).compareTo(frequency);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof Keyword)) {
return false;
} else {
return stem.equals(((Keyword) obj).stem);
}
}
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] { stem });
}
public String getStem() {
return stem;
}
public Set<String> getTerms() {
return terms;
}
public int getFrequency() {
return frequency;
}
}
Utilities
Để ngăn chặn từ:
public static String stem(String term) throws IOException {
TokenStream tokenStream = null;
try {
// tokenize
tokenStream = new ClassicTokenizer(Version.LUCENE_36, new StringReader(term));
// stem
tokenStream = new PorterStemFilter(tokenStream);
// add each token in a set, so that duplicates are removed
Set<String> stems = new HashSet<String>();
CharTermAttribute token = tokenStream.getAttribute(CharTermAttribute.class);
tokenStream.reset();
while (tokenStream.incrementToken()) {
stems.add(token.toString());
}
// if no stem or 2+ stems have been found, return null
if (stems.size() != 1) {
return null;
}
String stem = stems.iterator().next();
// if the stem has non-alphanumerical chars, return null
if (!stem.matches("[a-zA-Z0-9-]+")) {
return null;
}
return stem;
} finally {
if (tokenStream != null) {
tokenStream.close();
}
}
}
Để tìm kiếm vào một bộ sưu tập (sẽ được sử dụng bởi danh sách các từ khóa tiềm năng):
public static <T> T find(Collection<T> collection, T example) {
for (T element : collection) {
if (element.equals(example)) {
return element;
}
}
collection.add(example);
return example;
}
Lõi
Dưới đây là phương pháp đầu vào chính:
public static List<Keyword> guessFromString(String input) throws IOException {
TokenStream tokenStream = null;
try {
// hack to keep dashed words (e.g. "non-specific" rather than "non" and "specific")
input = input.replaceAll("-+", "-0");
// replace any punctuation char but apostrophes and dashes by a space
input = input.replaceAll("[\\p{Punct}&&[^'-]]+", " ");
// replace most common english contractions
input = input.replaceAll("(?:'(?:[tdsm]|[vr]e|ll))+\\b", "");
// tokenize input
tokenStream = new ClassicTokenizer(Version.LUCENE_36, new StringReader(input));
// to lowercase
tokenStream = new LowerCaseFilter(Version.LUCENE_36, tokenStream);
// remove dots from acronyms (and "'s" but already done manually above)
tokenStream = new ClassicFilter(tokenStream);
// convert any char to ASCII
tokenStream = new ASCIIFoldingFilter(tokenStream);
// remove english stop words
tokenStream = new StopFilter(Version.LUCENE_36, tokenStream, EnglishAnalyzer.getDefaultStopSet());
List<Keyword> keywords = new LinkedList<Keyword>();
CharTermAttribute token = tokenStream.getAttribute(CharTermAttribute.class);
tokenStream.reset();
while (tokenStream.incrementToken()) {
String term = token.toString();
// stem each term
String stem = stem(term);
if (stem != null) {
// create the keyword or get the existing one if any
Keyword keyword = find(keywords, new Keyword(stem.replaceAll("-0", "-")));
// add its corresponding initial token
keyword.add(term.replaceAll("-0", "-"));
}
}
// reverse sort by frequency
Collections.sort(keywords);
return keywords;
} finally {
if (tokenStream != null) {
tokenStream.close();
}
}
}
Ví dụ
Sử dụng phương pháp guessFromString
trên Java wikipedia article introduction part, đây là danh sách 10 từ khóa thường gặp nhất đầu tiên (tức xuất phát) đã được tìm thấy:
java x12 [java]
compil x5 [compiled, compiler, compilers]
sun x5 [sun]
develop x4 [developed, developers]
languag x3 [languages, language]
implement x3 [implementation, implementations]
applic x3 [application, applications]
run x3 [run]
origin x3 [originally, original]
gnu x3 [gnu]
lặp trên danh sách đầu ra để biết được là gốc tìm thấy từ cho mỗi gốc bằng cách nhận các terms
bộ (hiển thị giữa dấu ngoặc [...]
trong ví dụ trên).
gì tiếp theo
So sánh tần gốc/tần số tổng hợp tỷ lệ với số liệu thống kê ngôn ngữ người Anh, và giữ cho tôi trong vòng lặp nếu bạn quản lý nó: tôi có thể là khá quan tâm quá :)
Vì vậy, như tôi hiểu, mã phải chạy trên một máy chủ apache. Điều gì xảy ra nếu phần mềm của tôi được cho là địa phương? – Shay
@Shay Tại sao nó cần một máy chủ apache? Tôi chỉ cần đặt 'KeywordGuesser.guessFromString (" input ")' vào phương thức 'public static void (String [] args)' để tạo ra ví dụ. – sp00m
Tôi không quen với Lucene và tôi thấy rằng mã này phụ thuộc rất nhiều vào nó nên tôi đã giả định rằng đây là trường hợp. Bất kỳ ý tưởng nào về từ điển tiếng Anh của thân cây có thể được tìm thấy? – Shay