adding modules
git-svn-id: https://russianmorphology.googlecode.com/svn/trunk@49 d817d54c-26ab-11de-abc9-2f7d1455ff7a
This commit is contained in:
parent
786ce92ae0
commit
710384987c
22
dictionary-reader/pom.xml
Normal file
22
dictionary-reader/pom.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<project>
|
||||||
|
<parent>
|
||||||
|
<artifactId>morpholgy</artifactId>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<artifactId>dictionary-reader</artifactId>
|
||||||
|
<name>dictionary-reader</name>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<artifactId>morph</artifactId>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -14,9 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.dictonary;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.RussianSuffixDecoderEncoder;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@ -72,7 +71,7 @@ public class DictonaryReader {
|
|||||||
List<FlexiaModel> models = wordsFlexias.get(Integer.valueOf(wd[1]));
|
List<FlexiaModel> models = wordsFlexias.get(Integer.valueOf(wd[1]));
|
||||||
FlexiaModel flexiaModel = models.get(0);
|
FlexiaModel flexiaModel = models.get(0);
|
||||||
if (models.size() > 0 && !ingnoredForm.contains(flexiaModel.getCode())) {
|
if (models.size() > 0 && !ingnoredForm.contains(flexiaModel.getCode())) {
|
||||||
WordCard card = new WordCard(cleanString(flexiaModel.create(wordBase)), cleanString(wordBase), flexiaModel.getSuffix());
|
WordCard card = new WordCard(flexiaModel.create(wordBase), wordBase, flexiaModel.getSuffix());
|
||||||
for (FlexiaModel fm : models) {
|
for (FlexiaModel fm : models) {
|
||||||
card.addFlexia(fm);
|
card.addFlexia(fm);
|
||||||
}
|
}
|
||||||
@ -81,9 +80,6 @@ public class DictonaryReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanString(String s) {
|
|
||||||
return s.replace((char) (34 + RussianSuffixDecoderEncoder.RUSSIAN_SMALL_LETTER_OFFSET), (char) (6 + RussianSuffixDecoderEncoder.RUSSIAN_SMALL_LETTER_OFFSET));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sckipBlock(BufferedReader reader) throws IOException {
|
private void sckipBlock(BufferedReader reader) throws IOException {
|
||||||
String s = reader.readLine();
|
String s = reader.readLine();
|
||||||
@ -123,7 +119,7 @@ public class DictonaryReader {
|
|||||||
System.out.println(line);
|
System.out.println(line);
|
||||||
// flexiaModelArrayList.add(new FlexiaModel(fl[1], cleanString(fl[0].toLowerCase()), cleanString(fl[2].toLowerCase())));
|
// flexiaModelArrayList.add(new FlexiaModel(fl[1], cleanString(fl[0].toLowerCase()), cleanString(fl[2].toLowerCase())));
|
||||||
}
|
}
|
||||||
if (fl.length == 2) flexiaModelArrayList.add(new FlexiaModel(fl[1], cleanString(fl[0].toLowerCase()), ""));
|
if (fl.length == 2) flexiaModelArrayList.add(new FlexiaModel(fl[1], fl[0].toLowerCase(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.dictonary;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represent inofrmation of how word form created form it imutible part.
|
* Represent inofrmation of how word form created form it imutible part.
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.dictonary;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.dictonary;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.dictonary;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
@ -14,15 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.dictonary.FlexiaModel;
|
import org.apache.lucene.morphology.Heuristic;
|
||||||
import org.apache.lucene.russian.morphology.dictonary.GrammaReader;
|
import org.apache.lucene.morphology.LetterDecoderEncoder;
|
||||||
import org.apache.lucene.russian.morphology.dictonary.WordCard;
|
import org.apache.lucene.morphology.Morph;
|
||||||
import org.apache.lucene.russian.morphology.dictonary.WordProccessor;
|
|
||||||
import org.apache.lucene.russian.morphology.informations.Heuristic;
|
|
||||||
import org.apache.lucene.russian.morphology.informations.Morph;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -33,6 +30,7 @@ public class StatiticsCollector implements WordProccessor {
|
|||||||
private Map<Set<Heuristic>, Integer> ruleInverIndex = new HashMap<Set<Heuristic>, Integer>();
|
private Map<Set<Heuristic>, Integer> ruleInverIndex = new HashMap<Set<Heuristic>, Integer>();
|
||||||
private List<Set<Heuristic>> rules = new ArrayList<Set<Heuristic>>();
|
private List<Set<Heuristic>> rules = new ArrayList<Set<Heuristic>>();
|
||||||
private GrammaReader grammaReader;
|
private GrammaReader grammaReader;
|
||||||
|
private LetterDecoderEncoder decoderEncoder;
|
||||||
|
|
||||||
|
|
||||||
public StatiticsCollector(GrammaReader grammaReader) {
|
public StatiticsCollector(GrammaReader grammaReader) {
|
||||||
@ -40,6 +38,7 @@ public class StatiticsCollector implements WordProccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void proccess(WordCard wordCard) throws IOException {
|
public void proccess(WordCard wordCard) throws IOException {
|
||||||
|
wordCard = cleanWordCard(wordCard);
|
||||||
String normalStringMorph = wordCard.getWordsFroms().get(0).getCode();
|
String normalStringMorph = wordCard.getWordsFroms().get(0).getCode();
|
||||||
String word = wordCard.getBase() + wordCard.getCanonicalSuffix();
|
String word = wordCard.getBase() + wordCard.getCanonicalSuffix();
|
||||||
if (word.contains("-")) return;
|
if (word.contains("-")) return;
|
||||||
@ -56,6 +55,10 @@ public class StatiticsCollector implements WordProccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private WordCard cleanWordCard(WordCard wordCard) {
|
||||||
|
return wordCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void saveHeuristic() throws IOException {
|
public void saveHeuristic() throws IOException {
|
||||||
|
|
||||||
@ -98,7 +101,7 @@ public class StatiticsCollector implements WordProccessor {
|
|||||||
for (String key : inversIndex.keySet()) {
|
for (String key : inversIndex.keySet()) {
|
||||||
Set<Heuristic> currentSet = inversIndex.get(key);
|
Set<Heuristic> currentSet = inversIndex.get(key);
|
||||||
if (!currentSet.equals(prevSet)) {
|
if (!currentSet.equals(prevSet)) {
|
||||||
ints[count] = RussianSuffixDecoderEncoder.encodeToArray(key);
|
ints[count] = decoderEncoder.encodeToArray(key);
|
||||||
rulesId[count] = (short) ruleInverIndex.get(currentSet).intValue();
|
rulesId[count] = (short) ruleInverIndex.get(currentSet).intValue();
|
||||||
count++;
|
count++;
|
||||||
prevSet = currentSet;
|
prevSet = currentSet;
|
||||||
@ -136,5 +139,9 @@ public class StatiticsCollector implements WordProccessor {
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String cleanString(String s) {
|
||||||
|
return decoderEncoder.cleanString(s);
|
||||||
|
//return s.replace((char) (34 + RussianSuffixDecoderEncoder.RUSSIAN_SMALL_LETTER_OFFSET), (char) (6 + RussianSuffixDecoderEncoder.RUSSIAN_SMALL_LETTER_OFFSET));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.dictonary;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -53,4 +53,20 @@ public class WordCard {
|
|||||||
public List<FlexiaModel> getWordsFroms() {
|
public List<FlexiaModel> getWordsFroms() {
|
||||||
return wordsFroms;
|
return wordsFroms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCanonicalFrom(String canonicalFrom) {
|
||||||
|
this.canonicalFrom = canonicalFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBase(String base) {
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCanonicalSuffix(String canonicalSuffix) {
|
||||||
|
this.canonicalSuffix = canonicalSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWordsFroms(List<FlexiaModel> wordsFroms) {
|
||||||
|
this.wordsFroms = wordsFroms;
|
||||||
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.dictonary;
|
package org.apache.lucene.morpholgy.dictionary;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
23
morph/pom.xml
Normal file
23
morph/pom.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<project>
|
||||||
|
<parent>
|
||||||
|
<artifactId>morpholgy</artifactId>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<artifactId>morph</artifactId>
|
||||||
|
<name>morph</name>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.russian.morphology.informations;
|
package org.apache.lucene.morphology;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2009 Alexander Kuznetsov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.lucene.morphology;
|
||||||
|
|
||||||
|
|
||||||
|
public interface LetterDecoderEncoder {
|
||||||
|
public Integer encode(String string);
|
||||||
|
|
||||||
|
public int[] encodeToArray(String s);
|
||||||
|
|
||||||
|
public String decodeArray(int[] array);
|
||||||
|
|
||||||
|
public String decode(Integer suffixN);
|
||||||
|
|
||||||
|
public boolean checkCharacter(char c);
|
||||||
|
|
||||||
|
public String cleanString(String s);
|
||||||
|
}
|
@ -1,14 +1,29 @@
|
|||||||
package org.apache.lucene.russian.morphology.informations;
|
/**
|
||||||
|
* Copyright 2009 Alexander Kuznetsov
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.lucene.morphology;
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.RussianSuffixDecoderEncoder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
public class LuceneMorph extends Morph{
|
public class LuceneMorph extends Morph {
|
||||||
|
LetterDecoderEncoder decoderEncoder;
|
||||||
|
|
||||||
public LuceneMorph(String fileName) throws IOException {
|
public LuceneMorph(String fileName) throws IOException {
|
||||||
super(fileName);
|
super(fileName);
|
||||||
@ -17,7 +32,7 @@ public class LuceneMorph extends Morph{
|
|||||||
@Override
|
@Override
|
||||||
public List<String> getMorhInfo(String s) {
|
public List<String> getMorhInfo(String s) {
|
||||||
ArrayList<String> result = new ArrayList<String>();
|
ArrayList<String> result = new ArrayList<String>();
|
||||||
int[] ints = RussianSuffixDecoderEncoder.encodeToArray(revertWord(s));
|
int[] ints = decoderEncoder.encodeToArray(revertWord(s));
|
||||||
int ruleId = findRuleId(ints);
|
int ruleId = findRuleId(ints);
|
||||||
for (Heuristic h : rules[rulesId[ruleId]]) {
|
for (Heuristic h : rules[rulesId[ruleId]]) {
|
||||||
result.add(h.transofrmWord(s));
|
result.add(h.transofrmWord(s));
|
||||||
@ -43,14 +58,14 @@ public class LuceneMorph extends Morph{
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Heuristic[] modeifyHeuristic(Heuristic[] heuristics){
|
private Heuristic[] modeifyHeuristic(Heuristic[] heuristics) {
|
||||||
ArrayList<Heuristic> result = new ArrayList<Heuristic>();
|
ArrayList<Heuristic> result = new ArrayList<Heuristic>();
|
||||||
for(Heuristic heuristic:heuristics){
|
for (Heuristic heuristic : heuristics) {
|
||||||
boolean isAdded = true;
|
boolean isAdded = true;
|
||||||
for(Heuristic ch:result){
|
for (Heuristic ch : result) {
|
||||||
isAdded = isAdded && !(ch.getActualNormalSuffix().equals(heuristic.getActualNormalSuffix()) && (ch.getActualSuffixLengh() == heuristic.getActualSuffixLengh()));
|
isAdded = isAdded && !(ch.getActualNormalSuffix().equals(heuristic.getActualNormalSuffix()) && (ch.getActualSuffixLengh() == heuristic.getActualSuffixLengh()));
|
||||||
}
|
}
|
||||||
if(isAdded){
|
if (isAdded) {
|
||||||
result.add(heuristic);
|
result.add(heuristic);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,9 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.russian.morphology.informations;
|
package org.apache.lucene.morphology;
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.RussianSuffixDecoderEncoder;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
@ -30,6 +29,7 @@ public class Morph {
|
|||||||
protected short[] rulesId;
|
protected short[] rulesId;
|
||||||
protected Heuristic[][] rules;
|
protected Heuristic[][] rules;
|
||||||
protected String[] grammaInfo;
|
protected String[] grammaInfo;
|
||||||
|
LetterDecoderEncoder decoderEncoder;
|
||||||
|
|
||||||
|
|
||||||
public Morph(String fileName) throws IOException {
|
public Morph(String fileName) throws IOException {
|
||||||
@ -61,7 +61,7 @@ public class Morph {
|
|||||||
|
|
||||||
public List<String> getMorhInfo(String s) {
|
public List<String> getMorhInfo(String s) {
|
||||||
ArrayList<String> result = new ArrayList<String>();
|
ArrayList<String> result = new ArrayList<String>();
|
||||||
int[] ints = RussianSuffixDecoderEncoder.encodeToArray(revertWord(s));
|
int[] ints = decoderEncoder.encodeToArray(revertWord(s));
|
||||||
int ruleId = findRuleId(ints);
|
int ruleId = findRuleId(ints);
|
||||||
for (Heuristic h : rules[rulesId[ruleId]]) {
|
for (Heuristic h : rules[rulesId[ruleId]]) {
|
||||||
result.add(h.transofrmWord(s) + "|" + grammaInfo[h.getFormMorphInfo()]);
|
result.add(h.transofrmWord(s) + "|" + grammaInfo[h.getFormMorphInfo()]);
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology;
|
package org.apache.lucene.morphology;
|
||||||
|
|
||||||
|
|
||||||
public class SuffixToLongException extends RuntimeException {
|
public class SuffixToLongException extends RuntimeException {
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology;
|
package org.apache.lucene.morphology;
|
||||||
|
|
||||||
|
|
||||||
public class WrongCharaterException extends RuntimeException {
|
public class WrongCharaterException extends RuntimeException {
|
@ -14,14 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.analayzer;
|
package org.apache.lucene.morphology.analayzer;
|
||||||
|
|
||||||
import org.apache.lucene.analysis.Analyzer;
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
import org.apache.lucene.analysis.LowerCaseFilter;
|
import org.apache.lucene.analysis.LowerCaseFilter;
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
import org.apache.lucene.analysis.TokenStream;
|
||||||
import org.apache.lucene.analysis.standard.StandardFilter;
|
import org.apache.lucene.analysis.standard.StandardFilter;
|
||||||
import org.apache.lucene.analysis.standard.StandardTokenizer;
|
import org.apache.lucene.analysis.standard.StandardTokenizer;
|
||||||
import org.apache.lucene.russian.morphology.informations.LuceneMorph;
|
import org.apache.lucene.morphology.LuceneMorph;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
@ -14,16 +14,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.analayzer;
|
package org.apache.lucene.morphology.analayzer;
|
||||||
|
|
||||||
import org.apache.lucene.analysis.Token;
|
import org.apache.lucene.analysis.Token;
|
||||||
import org.apache.lucene.analysis.TokenFilter;
|
import org.apache.lucene.analysis.TokenFilter;
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
import org.apache.lucene.analysis.TokenStream;
|
||||||
import org.apache.lucene.russian.morphology.informations.LuceneMorph;
|
import org.apache.lucene.morphology.LuceneMorph;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
public class RussianMorphlogyFilter extends TokenFilter {
|
public class RussianMorphlogyFilter extends TokenFilter {
|
24
pom.xml
24
pom.xml
@ -1,16 +1,19 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.apache.lucene</groupId>
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
<artifactId>russian-morpholgy</artifactId>
|
<artifactId>morpholgy</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>0.7-SNAPSHOT</version>
|
<version>0.7-SNAPSHOT</version>
|
||||||
<name>russian-morpholgy</name>
|
<name>morpholgy</name>
|
||||||
<url>http://maven.apache.org</url>
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
<connection>scm:svn:http://russianmorphology.googlecode.com/svn/tags/russian-morpholgy-0.6</connection>
|
<connection>scm:svn:http://russianmorphology.googlecode.com/svn/tags/russian-morpholgy-0.7</connection>
|
||||||
<developerConnection>scm:svn:https://russianmorphology.googlecode.com/svn/tags/russian-morpholgy-0.6</developerConnection>
|
<developerConnection>scm:svn:https://russianmorphology.googlecode.com/svn/tags/russian-morpholgy-0.7
|
||||||
|
</developerConnection>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
|
|
||||||
@ -108,10 +111,10 @@
|
|||||||
<header>etc/header.txt</header>
|
<header>etc/header.txt</header>
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>**/*.txt</exclude>
|
<exclude>**/*.txt</exclude>
|
||||||
|
<exclude>**/pom.xml</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
<includes>
|
<includes>
|
||||||
<include>**/src/**</include>
|
<include>**/src/**</include>
|
||||||
<include>**/pom.xml</include>
|
|
||||||
</includes>
|
</includes>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
||||||
@ -127,4 +130,9 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>morph</module>
|
||||||
|
<module>dictionary-reader</module>
|
||||||
|
<module>russian</module>
|
||||||
|
</modules>
|
||||||
</project>
|
</project>
|
31
russian/pom.xml
Normal file
31
russian/pom.xml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>morpholgy</artifactId>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<artifactId>russian</artifactId>
|
||||||
|
<name>russian</name>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.lucene.morpholgy</groupId>
|
||||||
|
<artifactId>dictionary-reader</artifactId>
|
||||||
|
<version>0.7-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.4</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -14,12 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology;
|
package org.apache.lucene.morphology.russian;
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.dictonary.DictonaryReader;
|
import org.apache.lucene.morpholgy.dictionary.*;
|
||||||
import org.apache.lucene.russian.morphology.dictonary.FrequentyReader;
|
|
||||||
import org.apache.lucene.russian.morphology.dictonary.GrammaReader;
|
|
||||||
import org.apache.lucene.russian.morphology.dictonary.IgnoredFormReader;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
@ -14,7 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology;
|
package org.apache.lucene.morphology.russian;
|
||||||
|
|
||||||
|
import org.apache.lucene.morphology.LetterDecoderEncoder;
|
||||||
|
import org.apache.lucene.morphology.SuffixToLongException;
|
||||||
|
import org.apache.lucene.morphology.WrongCharaterException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@ -24,7 +28,7 @@ import java.util.ArrayList;
|
|||||||
* Assumed that suffix contains only small russian letters and dash.
|
* Assumed that suffix contains only small russian letters and dash.
|
||||||
* Also assumed that letter <EFBFBD> and <EFBFBD> coinsed.
|
* Also assumed that letter <EFBFBD> and <EFBFBD> coinsed.
|
||||||
*/
|
*/
|
||||||
public class RussianSuffixDecoderEncoder {
|
public class RussianSuffixDecoderEncoder implements LetterDecoderEncoder {
|
||||||
public static final int RUSSIAN_SMALL_LETTER_OFFSET = 1071;
|
public static final int RUSSIAN_SMALL_LETTER_OFFSET = 1071;
|
||||||
static public int SUFFIX_LENGTH = 6;
|
static public int SUFFIX_LENGTH = 6;
|
||||||
public static final int EE_CHAR = 34;
|
public static final int EE_CHAR = 34;
|
||||||
@ -32,7 +36,7 @@ public class RussianSuffixDecoderEncoder {
|
|||||||
public static final int DASH_CHAR = 45;
|
public static final int DASH_CHAR = 45;
|
||||||
public static final int DASH_CODE = 33;
|
public static final int DASH_CODE = 33;
|
||||||
|
|
||||||
static public Integer encode(String string) {
|
public Integer encode(String string) {
|
||||||
if (string.length() > 6) throw new SuffixToLongException("Suffix length should not be greater then " + 12);
|
if (string.length() > 6) throw new SuffixToLongException("Suffix length should not be greater then " + 12);
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (int i = 0; i < string.length(); i++) {
|
for (int i = 0; i < string.length(); i++) {
|
||||||
@ -51,7 +55,7 @@ public class RussianSuffixDecoderEncoder {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public int[] encodeToArray(String s) {
|
public int[] encodeToArray(String s) {
|
||||||
ArrayList<Integer> integers = new ArrayList<Integer>();
|
ArrayList<Integer> integers = new ArrayList<Integer>();
|
||||||
while (s.length() > 6) {
|
while (s.length() > 6) {
|
||||||
integers.add(encode(s.substring(0, 6)));
|
integers.add(encode(s.substring(0, 6)));
|
||||||
@ -67,7 +71,7 @@ public class RussianSuffixDecoderEncoder {
|
|||||||
return ints;
|
return ints;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public String decodeArray(int[] array) {
|
public String decodeArray(int[] array) {
|
||||||
String result = "";
|
String result = "";
|
||||||
for (int i : array) {
|
for (int i : array) {
|
||||||
result += decode(i);
|
result += decode(i);
|
||||||
@ -76,7 +80,7 @@ public class RussianSuffixDecoderEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static public String decode(Integer suffixN) {
|
public String decode(Integer suffixN) {
|
||||||
String result = "";
|
String result = "";
|
||||||
while (suffixN > 33) {
|
while (suffixN > 33) {
|
||||||
int c = suffixN % 34 + RUSSIAN_SMALL_LETTER_OFFSET;
|
int c = suffixN % 34 + RUSSIAN_SMALL_LETTER_OFFSET;
|
||||||
@ -94,11 +98,15 @@ public class RussianSuffixDecoderEncoder {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public boolean checkCharacter(char c) {
|
public boolean checkCharacter(char c) {
|
||||||
int code = 0 + c;
|
int code = 0 + c;
|
||||||
if (code == 45) return true;
|
if (code == 45) return true;
|
||||||
code -= RUSSIAN_SMALL_LETTER_OFFSET;
|
code -= RUSSIAN_SMALL_LETTER_OFFSET;
|
||||||
if (code > 0 && code < 33) return true;
|
if (code > 0 && code < 33) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String cleanString(String s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
}
|
}
|
@ -13,10 +13,11 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.russian.morphology;
|
package org.apache.lucene.morphology.russian;
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.informations.Heuristic;
|
|
||||||
import org.apache.lucene.russian.morphology.informations.Morph;
|
import org.apache.lucene.morphology.Heuristic;
|
||||||
|
import org.apache.lucene.morphology.Morph;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
@ -1,87 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2009 Alexander Kuznetsov
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.analayzer;
|
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.RussianSuffixDecoderEncoder;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
|
|
||||||
public class SuffixHeuristic {
|
|
||||||
private long[] keys;
|
|
||||||
private long[] values;
|
|
||||||
|
|
||||||
public void readFromFile(String fileName) throws IOException {
|
|
||||||
BufferedReader reader = new BufferedReader(new FileReader(fileName));
|
|
||||||
readFromBufferedRreader(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public SuffixHeuristic() throws IOException {
|
|
||||||
readFromResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SuffixHeuristic(String fileName) throws IOException {
|
|
||||||
readFromFile(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readFromResource() throws IOException {
|
|
||||||
InputStream stream = this.getClass().getResourceAsStream("/org/apache/lucene/russian/morphology/russianSuffixesHeuristic.txt");
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream));
|
|
||||||
readFromBufferedRreader(bufferedReader);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readFromBufferedRreader(BufferedReader reader) throws IOException {
|
|
||||||
int size = Integer.valueOf(reader.readLine());
|
|
||||||
keys = new long[size];
|
|
||||||
values = new long[size];
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
String[] s = reader.readLine().split(" ");
|
|
||||||
keys[i] = Long.valueOf(s[0]);
|
|
||||||
values[i] = Long.valueOf(s[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCanonicalForm(String form) {
|
|
||||||
int startSymbol = form.length() > RussianSuffixDecoderEncoder.SUFFIX_LENGTH ? form.length() - RussianSuffixDecoderEncoder.SUFFIX_LENGTH : 0;
|
|
||||||
String suffixS = form.substring(startSymbol);
|
|
||||||
|
|
||||||
if (!chechSuffix(suffixS)) return form;
|
|
||||||
|
|
||||||
Integer suffix = RussianSuffixDecoderEncoder.encode(suffixS);
|
|
||||||
|
|
||||||
int index = Arrays.binarySearch(keys, suffix);
|
|
||||||
if (index < -1) {
|
|
||||||
System.out.println(" " + form);
|
|
||||||
return form;
|
|
||||||
} else {
|
|
||||||
String nSuffix = RussianSuffixDecoderEncoder.decode((int) values[index]);
|
|
||||||
return startSymbol > 0 ? form.substring(0, startSymbol) + nSuffix : nSuffix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean chechSuffix(String suffix) {
|
|
||||||
for (int i = 0; i < suffix.length(); i++) {
|
|
||||||
if (!RussianSuffixDecoderEncoder.checkCharacter(suffix.charAt(i))) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2009 Alexander Kuznetsov
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology;
|
|
||||||
|
|
||||||
import static org.hamcrest.core.IsEqual.equalTo;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
|
|
||||||
public class RussianSuffixDecoderEncoderTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldCorretDecodeEncode() throws IOException {
|
|
||||||
InputStream stream = this.getClass().getResourceAsStream("/org/apache/lucene/russian/morphology/decoder-test-data.txt");
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
|
|
||||||
String s = bufferedReader.readLine();
|
|
||||||
while (s != null) {
|
|
||||||
String[] qa = s.trim().split(" ");
|
|
||||||
Integer ecodedSuffix = RussianSuffixDecoderEncoder.encode(qa[0]);
|
|
||||||
assertThat(RussianSuffixDecoderEncoder.decode(ecodedSuffix), equalTo(qa[1]));
|
|
||||||
s = bufferedReader.readLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldCorretDecodeEncodeStringToArray() throws IOException {
|
|
||||||
InputStream stream = this.getClass().getResourceAsStream("/org/apache/lucene/russian/morphology/decoder-test-data-for-array.txt");
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
|
|
||||||
String s = bufferedReader.readLine();
|
|
||||||
while (s != null) {
|
|
||||||
String[] qa = s.trim().split(" ");
|
|
||||||
int[] ecodedSuffix = RussianSuffixDecoderEncoder.encodeToArray(qa[0]);
|
|
||||||
assertThat(RussianSuffixDecoderEncoder.decodeArray(ecodedSuffix), equalTo(qa[1]));
|
|
||||||
s = bufferedReader.readLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = SuffixToLongException.class)
|
|
||||||
public void shouldThrownExeptionIfSuffixToLong() {
|
|
||||||
RussianSuffixDecoderEncoder.encode("1234567890123");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = WrongCharaterException.class)
|
|
||||||
public void shouldThrownExeptionIfSuffixContainWrongCharater() {
|
|
||||||
RussianSuffixDecoderEncoder.encode("1");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2009 Alexander Kuznetsov
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.lucene.russian.morphology;
|
|
||||||
|
|
||||||
import org.apache.lucene.russian.morphology.informations.Morph;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
|
|
||||||
public class SpeedTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getTestOfSpeed() throws IOException {
|
|
||||||
Morph splitter = new Morph("sep.txt");
|
|
||||||
InputStream stream = Test.class.getResourceAsStream("/org/apache/lucene/russian/morphology/analayzer/russian-text.txt");
|
|
||||||
BufferedReader stream1 = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
|
|
||||||
String s = stream1.readLine().trim().toLowerCase();
|
|
||||||
for (String w : s.split(" ")) {
|
|
||||||
try {
|
|
||||||
System.out.println(w);
|
|
||||||
System.out.println(splitter.getMorhInfo(w));
|
|
||||||
} catch (WrongCharaterException e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Long startTime = System.currentTimeMillis();
|
|
||||||
// RussianMorphlogyAnalayzer morphlogyAnalayzer = new RussianMorphlogyAnalayzer();
|
|
||||||
// System.out.println("To build analayzer take " + (System.currentTimeMillis() - startTime) + " ms.");
|
|
||||||
// InputStream stream = this.getClass().getResourceAsStream("/org/apache/lucene/russian/morphology/text.txt");
|
|
||||||
// BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// final Token reusableToken = new Token();
|
|
||||||
//
|
|
||||||
// Token nextToken;
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// startTime = System.currentTimeMillis();
|
|
||||||
// Integer count = 0;
|
|
||||||
// TokenStream in = morphlogyAnalayzer.tokenStream(null, reader);
|
|
||||||
// for (; ;) {
|
|
||||||
// nextToken = in.next(reusableToken);
|
|
||||||
// count++;
|
|
||||||
// if (nextToken == null) {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// System.out.println("It takes " + (System.currentTimeMillis() - startTime) + " ms. To proccess " + count + " words." );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2009 Alexander Kuznetsov
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.analayzer;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
|
||||||
import org.apache.lucene.analysis.Token;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class RussianMorphlogyAnalayzerTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldCorrectProccessText() throws IOException {
|
|
||||||
RussianMorphlogyAnalayzer morphlogyAnalayzer = new RussianMorphlogyAnalayzer();
|
|
||||||
InputStream stream = this.getClass().getResourceAsStream("/org/apache/lucene/russian/morphology/analayzer/russian-text.txt");
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
|
|
||||||
|
|
||||||
InputStream tokeStream = this.getClass().getResourceAsStream("/org/apache/lucene/russian/morphology/analayzer/token-of-russian-text.txt");
|
|
||||||
BufferedReader tokenReader = new BufferedReader(new InputStreamReader(tokeStream, "UTF-8"));
|
|
||||||
|
|
||||||
final Token reusableToken = new Token();
|
|
||||||
|
|
||||||
Token nextToken;
|
|
||||||
|
|
||||||
|
|
||||||
TokenStream in = morphlogyAnalayzer.tokenStream(null, reader);
|
|
||||||
for (; ;) {
|
|
||||||
nextToken = in.next(reusableToken);
|
|
||||||
|
|
||||||
if (nextToken == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println(nextToken.term());
|
|
||||||
// assertThat(nextToken.term(), equalTo(tokenReader.readLine().trim()));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2009 Alexander Kuznetsov
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.analayzer;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
|
|
||||||
public class SuffixHeuristicTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldDefineCorretCononicalWordForm() throws IOException {
|
|
||||||
// SuffixHeuristic suffixHeuristic = new SuffixHeuristic();
|
|
||||||
// InputStream stream = this.getClass().getResourceAsStream("/org/apache/lucene/russian/morphology/analayzer/suffix-heuristic-test-data.txt");
|
|
||||||
// BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
|
|
||||||
// String s = bufferedReader.readLine();
|
|
||||||
// while (s != null) {
|
|
||||||
// String[] qa = s.trim().split(" ");
|
|
||||||
// assertThat(suffixHeuristic.getCanonicalForm(qa[0]), equalTo(qa[1]));
|
|
||||||
// s = bufferedReader.readLine();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2009 Alexander Kuznetsov
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.russian.morphology.utils;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class UtilsTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCompate() {
|
|
||||||
System.out.println((byte) 255);
|
|
||||||
//
|
|
||||||
// assertThat(Utils.compate((byte)3,(byte)2),equalTo(1));
|
|
||||||
// assertThat(Utils.compate((byte)2,(byte)3),equalTo(-1));
|
|
||||||
// assertThat(Utils.compate((byte)200,(byte)2),equalTo(1));
|
|
||||||
// assertThat(Utils.compate((byte)2,(byte)200),equalTo(-1));
|
|
||||||
// assertThat(Utils.compate((byte)255,(byte)254),equalTo(1));
|
|
||||||
// assertThat(Utils.compate((byte)254,(byte)255),equalTo(-1));
|
|
||||||
// assertThat(Utils.compate((byte)200,(byte)200),equalTo(0));
|
|
||||||
// assertThat(Utils.compate((byte)2,(byte)2),equalTo(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStringTyByteArray() {
|
|
||||||
// Add your code here
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testByteArrayToString() {
|
|
||||||
// Add your code here
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
тест тест
|
|
||||||
ёж еж
|
|
||||||
естера естера
|
|
||||||
что-то что-то
|
|
||||||
а а
|
|
||||||
яяяяяя яяяяяя
|
|
||||||
яяяя яяяя
|
|
||||||
аа аа
|
|
||||||
аааааа аааааа
|
|
||||||
аааааааааааа аааааааааааа
|
|
||||||
аааааааааааааааааа аааааааааааааааааа
|
|
||||||
ааааааааааааааааа ааааааааааааааааа
|
|
||||||
йфячыцувс йфячыцувс
|
|
@ -1,8 +0,0 @@
|
|||||||
тест тест
|
|
||||||
ёж еж
|
|
||||||
естера естера
|
|
||||||
что-то что-то
|
|
||||||
а а
|
|
||||||
яяяяяя яяяяяя
|
|
||||||
яяяя яяяя
|
|
||||||
аа аа
|
|
@ -1,160 +0,0 @@
|
|||||||
Морфологический анализатор для русского языка — это что-то заумное? Программа, которая приводит слово к начальной форме, определяет падеж, находит словоформы — непонятно, как и подступиться? А на самом деле все не так и сложно. В статье — как я писал аналог mystem, lemmatizer и phpmorphy на Python, и что из этого получилось.
|
|
||||||
|
|
||||||
Сразу скажу, получилась библиотека для морфологического анализа на Python, которую Вы можете использовать и дорабатывать согласно лицензии MIT.
|
|
||||||
|
|
||||||
Первый вопрос — зачем все это?
|
|
||||||
|
|
||||||
Потихоньку делаю один хобби-проект, назрело решение писать его на Python. А для проекта был жизненно необходим более-менее качественный морфологический анализатор. Вариант с mystem не подошел из-за лицензии и отсутствием возможности поковыряться, lemmatizer и phpmorphy меня слегка напрягали своими словарями не в юникоде, а приличного аналога на python я почему-то не нашел. Вообщем причин много, все, если честно, не очень серьезные, на самом деле просто захотелось. Решил я, в итоге, изобрести велосипед и написать свою реализацию.
|
|
||||||
|
|
||||||
За основу взял наработки с сайта aot.ru. Словари (LGPL) для русского и английского, а также идеи — оттуда. Там были описаны и конкретные алгоритмы реализации, но в терминах теории автоматов. Я знаком с теорией автоматов (даже диплом по формальным грамматикам защитил), но мне показалось, что тут можно прекрасно обойтись без нее. Поэтому реализация — независимая, что имеет как плюсы, так и минусы.
|
|
||||||
|
|
||||||
Для начала, библиотека должна, как минимум, уметь:
|
|
||||||
1. Приводить слово к нормальной форме (например, в ед.ч., И.п. для существительных) — для слова «ЛЮДЕЙ» она должна вернуть «ЧЕЛОВЕК»
|
|
||||||
2. Определять форму слова (или формы). Например, для слова «ЛЮДЕЙ» она должна как-то дать знать, что это существительное, во множественном числе, может оказаться в родительном или винительном падежах.
|
|
||||||
|
|
||||||
Разбираемся со словарями
|
|
||||||
|
|
||||||
Словари с сайта aot.ru содержат следующую информацию:
|
|
||||||
1. парадигмы слов и конкретные правила образования;
|
|
||||||
2. ударения;
|
|
||||||
3. пользовательские сессии;
|
|
||||||
4. набор префиксов (продуктивных приставок);
|
|
||||||
5. леммы (незменяемые части слова, основы);
|
|
||||||
и еще есть 6. грамматическая информация — в отдельном файле.
|
|
||||||
|
|
||||||
Из этого всего нам интересны правила образования слов, префиксы, леммы и грамматическая информация.
|
|
||||||
|
|
||||||
Все слова образуются по одному принципу:
|
|
||||||
[префикс]+[приставка]+[основа]+[окончание]
|
|
||||||
|
|
||||||
Префиксы — это всякие «мега», «супер» и т.д. Набор префиксов хранится просто списком.
|
|
||||||
|
|
||||||
Приставки — имеются в виду приставки, присущие грамматической форме, но не присущие неизменяемой части слова («по»,«наи»). Например, «наи» в слове «наикрасивейший», т.к. без превосходной степени будет «красивый».
|
|
||||||
|
|
||||||
Правила образования слов — это то, что надо приписать спереди и сзади основы, чтобы получить какую-то форму. В словаре хранятся пары «приставка — окончание», + «номер» записи о грамматической информации (которая хранится отдельно).
|
|
||||||
|
|
||||||
Правила образования слов объединены в парадигмы. Например, для какого-нибудь класса существительных может быть описано, как слово выглядит во всех падежах и родах. Зная, что существительное принадлежит к этому классу, мы сможем правильно получить любую его форму. Такой класс — это и есть парадигма. Первой в парадигме всегда идет нормальная форма слова (если честно, вывод эмпирический))
|
|
||||||
|
|
||||||
Леммы — это неизменяемые части слов. В словаре хранится информация о том, какой лемме соответствуют какие парадигмы (какой набор правил для образования грамматических форм слова). Одной лемме может соответствовать несколько парадигм.
|
|
||||||
|
|
||||||
Грамматическая информация — просто пары («номер» записи, грам. информация). «Номер» в кавычках, т.к. это 2 буквы, просто от балды, но все разные.
|
|
||||||
|
|
||||||
Файл со словарем — обычный текстовый, для каждого раздела сначала указано число строк в нем, а потом идут строки, формат их описан, так что написать парсер труда не составило.
|
|
||||||
|
|
||||||
Поняв структуру словаря, уже несложно написать первую версию морфологического анализатора.
|
|
||||||
|
|
||||||
Пишем морфологический анализатор
|
|
||||||
|
|
||||||
По сути, нам дано слово, и его надо найти среди всех разумных комбинаций вида
|
|
||||||
<префикс>+<приставка>+<лемма>+<окончание> и
|
|
||||||
<приставка>+<лемма>+<окончание>
|
|
||||||
|
|
||||||
Дело упрощает то, что оказалось (как показала пара строчек на питоне), что «приставок» у нас в языке (да и в английском вроде тоже) всего 2. А префиксов в словаре — порядка 20 для русского языка. Поэтому искать можно среди комбинаций
|
|
||||||
<префикс>+<лемма>+<окончание>, объединив в уме список приставок и префиксов, а затем выполнив небольшую проверочку.
|
|
||||||
|
|
||||||
С префиксом разберемся по-свойски: если слово начинается с одного из возможных префиксов, то мы его (префикс) отбрасываем и пытаемся морфологически анализировать остаток (рекурсивно), а потом просто припишем отброшенный префикс к полученным формам.
|
|
||||||
|
|
||||||
В итоге получается, что задача сводится к поиску среди комбинаций
|
|
||||||
<лемма>+<окончание>, что еще лучше. Ищем подходящие леммы, потом смотрим, есть ли для них подходящие окончания*.
|
|
||||||
|
|
||||||
Тут я решил сильно упростить себе жизнь, и задействовать стандартный питоновский ассоциативный массив, в который поместил все леммы. Получился словарь вида lemmas: {base -> [rule_id]}, т.е. ключ — это лемма, а значение — список номеров допустимых парадигм. А дальше поехали — сначала считаем, что лемма — это первая буква слова, потом, что это 2 первых буквы и т.д. По лемме пытаемся получить список парадигм. Если получили, то в каждой допустимой парадигме пробегаем по всем правилам и смотрим, получится ли наше слово, если правило применить. Получается — добавляем его в список найденных форм.
|
|
||||||
|
|
||||||
*Еще был вариант — составить сразу словарь всех возможных слов вида <лемма>+<окончание>, получалось в итоге где-то миллионов 5 слов, не так и много, но вариант, вообщем, мне не очень понравился.
|
|
||||||
|
|
||||||
Дописываем морфологический анализатор
|
|
||||||
|
|
||||||
По сути — все готово, мы написали морфологический анализатор, за исключением некоторых деталей, которые заняли у меня 90% времени)
|
|
||||||
|
|
||||||
Деталь первая
|
|
||||||
Если вспомнить пример, который был в начале, про «ЛЮДЕЙ» — «ЧЕЛОВЕК», то станет понятно, что есть слова, у которых неизменяемая часть отсутствует. И где тогда искать формы этих слов — непонятно. Пробовал искать среди всех окончаний (точно так же, как и среди лемм), ведь для таких слов и «ЛЮДЕЙ», и «ЧЕЛОВЕКУ» в словаре будут храниться как окончания. Для некоторых слов работало, для некоторых выдавало, кроме правильного результата, еще и кучу мусора. В итоге, после долгих экспериментов выяснилось, что есть в словаре такая хитрая магическая лемма "#", которая и соответствует всем пустым леммам. Ура, задача решена, ищем каждый раз еще и там.
|
|
||||||
|
|
||||||
Деталь вторая
|
|
||||||
Хорошо бы иметь «предсказатель», который смог бы работать и со словами, которых нет в словаре. Это не только неизвестные науке редкие слова, но и просто описки, например.
|
|
||||||
|
|
||||||
Тут есть 2 несложных, но вполне работающих подхода:
|
|
||||||
1. Если слова отличаются только тем, что к одному из них приписано что-то спереди, то, скорее всего, склоняться они будут однаково
|
|
||||||
2. Если 2 слова оканчиваются одинаково, то и склоняться они, скорее всего, будут одинаково.
|
|
||||||
|
|
||||||
Первый подход — это угадывание префикса. Реализуется очень просто: пробуем считать сначала одну первую букву слова префиксом, потом 2 первых буквы и т.д. А то, что осталось, передаем морфологическому анализатору. Ну и делаем это только для не очень длинных префиксов и не очень коротких остатков.
|
|
||||||
|
|
||||||
Второй (предсказание по концу слова) подход чуть сложнее в реализации (так-то сильно сложнее, если нужна хорошая реализация)) и «поумнее» в плане предсказаний.
|
|
||||||
Первая сложность связана с тем, что конец слова может состоять не только из окончания, но и из части леммы. Тут я тоже решил «срезать углы» и задействовал опять ассоциативный массив с предварительно подготовленными всеми возмоными окончаниями слов (до 5 букв). Не так и много их получилось, несколько сот тысяч. Ключ массива — конец слова, значение — список возможных правил. Дальше — все как при поиске подходящей леммы, только у слова берем не начало, а 1, 2, 3, 4, 5-буквенные концы, а вместо лемм у нас теперь новый монстромассив.
|
|
||||||
Вторая сложность — получается много заведомого мусора. Мусор этот отсекается, если учесть, что полученные слова могут быть только существительными, прилагательными, наречиями или глаголами.
|
|
||||||
Даже после этого у нас остается слишком много не-мусорных правил. Для определенности, для каждой части речи оставляем только самое распространенное правило.
|
|
||||||
По идее, если слово не было предсказано как существительное, хорошо бы добавить вариант с неизменяемым существительным в ед.ч. и.п., но это в TODO.
|
|
||||||
|
|
||||||
Идеальный текст для проверки работы предсказателя — конечно же, «Сяпала Калуша по напушке», про то, как она там увазила бутявку и что из этого вышло:
|
|
||||||
|
|
||||||
Сяпала Калуша по напушке и увазила бутявку. И волит:
|
|
||||||
— Калушата, калушаточки! Бутявка!
|
|
||||||
Калушата присяпали и бутявку стрямкали. И подудонились.
|
|
||||||
А Калуша волит:
|
|
||||||
— Оее, оее! Бутявка-то некузявая!
|
|
||||||
Калушата бутявку вычучили.
|
|
||||||
Бутявка вздребезнулась, сопритюкнулась и усяпала с напушки.
|
|
||||||
А Калуша волит:
|
|
||||||
— Бутявок не трямкают. Бутявки дюбые и зюмо-зюмо некузявые. От бутявок дудонятся.
|
|
||||||
А бутявка волит за напушкой:
|
|
||||||
— Калушата подудонились! Калушата подудонились! Зюмо некузявые! Пуськи бятые!
|
|
||||||
|
|
||||||
|
|
||||||
Из текста предсказатель не справился с именем собственным Калуша, с «Калушата»(они стали мужиками «Калуш» и «Калушат»), с междометием «Оее», загадочным «зюмо-зюмо», и вместо «Пуська» опять выдал мужика «Пусек», остальное все, на мой взгляд, определил правильно. Вообщем есть куда стремиться, но уже неплохо.
|
|
||||||
|
|
||||||
О ужас, «хабрахабр» предсказывается тоже никак. А вот «хабрахабра» — уже понимает, что «хабрахабр».
|
|
||||||
|
|
||||||
Тут можно было бы, в принципе, подвести итог, если бы компьютеры работали мгновенно. Но это не так, поэтому есть
|
|
||||||
|
|
||||||
Деталь №3: ресурсы процессора и оперативной памяти
|
|
||||||
Скорость работы первого варианта меня вполне устроила. Получалось, по прикидкам, тыщ до 10 слов в секунду для простых русских слов, около тыщи для навороченных. Для английского — еще быстрее. Но было 2 очевидных (еще до начала разработки) «но», связанных с тем, что все данные грузились в оперативную память (через pickle/cPickle).
|
|
||||||
1. первоначальная загрузка занимала 3-4 секунды
|
|
||||||
2. кушалось порядка 150 мегабайт оперативной памяти с psyco и порядка 100 без ( +удалось немного сократить, когда привел всякие там питоновские set и list к tulpe, где возможно)
|
|
||||||
|
|
||||||
Не долго думая, провел небольшой рефакторинг и вынес все данные в отдельную сущность. А дальше мне на помощь пришла магия Python и duck typing. Вкратце — в алгоритмах использовались данные в виде ассоциативных и простых массивов. И все будет работать без переписывания алгоритмов, если вместо «настоящих» массивов подсунуть что-нибудь, что ведет себя как массив, а конкретнее, для нашей задачи, — поддерживает [] и in. Все) В стандартной поставке питона обнаружились такие «массивные» интерфейсы к нескольким нереляционным базам данных. Эти базы (bsddb, ndbm, gdbm), по сути, и есть ассоциативные массивы на диске. Как раз то, что нужно. Еще там обнаружилась пара высокоуровневых надстроек над этим хозяйством (anydbm и shelve). Остановился на том, что унаследовался от DbfilenameShelf и добавил поддержку ключей в юникоде и ключей-целых чисел. А, еще добавил кеширование результата, которое почему-то есть в Shelf, но убито в DbfilenameShelf.
|
|
||||||
|
|
||||||
Данные по скорости на тестовых текстах (995 слов, русский словарь, macbook):
|
|
||||||
get_pickle_morph('ru', predict_by_prefix = False): 0.214 CPU seconds
|
|
||||||
get_pickle_morph('ru'): 0.262 CPU seconds
|
|
||||||
get_shelve_morph('ru', predict_by_prefix = False): 0.726 CPU seconds
|
|
||||||
get_shelve_morph('ru'): 0.874 CPU seconds
|
|
||||||
|
|
||||||
Памяти вариант shelve, можно сказать, не кушал совсем.
|
|
||||||
|
|
||||||
Варианты shelve похоже, работали, когда словари уже сидели в дисковом кеше. Без этого время может быть и 20 секунд с включенным предсказателем. Еще замечал, что медленнее всего работает предсказание по префиксу: до того, как прикрутил кеширование к своим наследникам от DbfilenameShelf, тормозило это предсказание раз в 5 больше, чем все остальное вместе взятое. А в этих тестах вроде не сильно уже.
|
|
||||||
|
|
||||||
Кстати, пользуясь случаем, хочу спросить, вдруг кто-то знает, как в питоне можно узнать количество занятой текущим процессом памяти. По возможности кроссплатформенно как-нибудь. А то ставил в конец скрипта задержку и мерил по списку процессов.
|
|
||||||
|
|
||||||
Пример использования
|
|
||||||
|
|
||||||
import pymorphy
|
|
||||||
morph = pymorphy.get_shelve_morph('ru')
|
|
||||||
#слова должны быть в юникоде и ЗАГЛАВНЫМИ
|
|
||||||
info = morph.get_graminfo(unicode('Вася').upper())
|
|
||||||
|
|
||||||
|
|
||||||
Так все-таки, зачем?
|
|
||||||
|
|
||||||
В самом начале я уже объяснил, зачем стал писать морфологический анализатор.
|
|
||||||
Теперь время объяснить, почему я стал писать эту статью. Я ее написал в надежде на то, что такая библиотека интересна и полезна, и вместе мы эту библиотеку улучшим. Дело в том, что функционал, который уже реализован, вполне достаточен для моих нужд. Я буду ее поддерживать и исправлять, но не очень активно. Поэтому эта статья — не подробная инструкция по применению, а попытка описать, как все работает.
|
|
||||||
|
|
||||||
В поставке
|
|
||||||
|
|
||||||
pymorphy.py — сама библиотека
|
|
||||||
shelve_addons.py — наследники от shelf, может кому пригодится
|
|
||||||
encode_dicts.py — утилита, которая преобразовывает словари из формата AOT в форматы pymorphy. Без параметров, работает долго, ест метров 200 памяти, запускается 1 раз. Сконвертированные словари не распространяю из-за возможной бинарной несовместимости и большого объема.
|
|
||||||
test.py — юнит-тесты для pymorphy
|
|
||||||
example.py — небольшой пример и тексты с теми 995 словами
|
|
||||||
dicts/src — папка с исходными словарями, скачанными с aot.ru
|
|
||||||
dicts/converted — папка, куда encode_dicts.py будет складывать сконвертированные словари
|
|
||||||
|
|
||||||
Напоследок
|
|
||||||
|
|
||||||
ссылки:
|
|
||||||
www.assembla.com/wiki/show/pymorphy
|
|
||||||
hg.assembla.com/pymorphy
|
|
||||||
trac-hg.assembla.com/pymorphy
|
|
||||||
|
|
||||||
Лицензия — MIT.
|
|
||||||
|
|
||||||
проверял только под Python 2.5.
|
|
||||||
|
|
||||||
Замечания, предложения, вопросы по делу — приветствуются. Если интересно — подключайтесь к разработке.
|
|
Loading…
x
Reference in New Issue
Block a user