5

我正在用 Android 写一个文字游戏。这是我的第一个应用程序,所以我的知识几乎不存在。

我想做的是使用 JWI 访问 WordNet 字典。这需要指定 WordNet 字典的文件路径。

根据我的阅读,Android“资产”无法通过简单的文件路径获得,但 JWI 初始化 WordNet 字典 API 所需的是字典文件磁盘位置的 URL。

那么,最好的行动方案是什么?我应该在启动时将资产复制到 android 设备上的已知文件夹中吗?我想不出更好的方法,但这对我来说似乎完全愚蠢。

感激地收到任何帮助。

4

2 回答 2

0

我有同样的问题(但是对于码头 webapp 而不是 android)并尝试了这两种方法,但是没有成功:

JWNL.initialize(this.getClass().getClassLoader().getResourceAsStream("wordnet_properties.xml");
dict = Dictionary.getInstance();

在这里它成功加载了 wordnet_properties.xml,但它无法访问属性文件指向的字典。

直接使用字典文件夹:

String dictPath = "models/en/wordnet/dict/";
URL url = this.getClass().getClassLoader().getResource(dictPath);
System.out.println("loading wordnet from "+url);
dict = new RAMDictionary(url, ILoadPolicy.NO_LOAD);

在这里,我将字典 URL 设为jar:file:/home/myusername/.m2/repository/package/1.0-SNAPSHOT/commons-1.0-SNAPSHOT.jar!/models/en/wordnet/dict/. 但是 WordNet 不接受 jar 协议并给我错误:

java.lang.IllegalArgumentException: URL source must use 'file' protocol
    at edu.mit.jwi.data.FileProvider.toFile(FileProvider.java:693)
    at edu.mit.jwi.data.FileProvider.open(FileProvider.java:304)
    at edu.mit.jwi.DataSourceDictionary.open(DataSourceDictionary.java:92)
    at edu.mit.jwi.RAMDictionary.open(RAMDictionary.java:216)

我的下一个调查将是为 RAMDictionary 或类似的东西创建一个子类,请告诉我您是否在此期间找到了解决方案。

PS:在我尝试重写 FileProvider 以改用资源后,我刚给开发人员写了一封寻求帮助的邮件,但一两个小时后我放弃了,因为代码调用了很多其他代码,这些代码也只适用于文件。我会及时通知你!

PPS:我收到了开发人员的答复,说流基本上不可能,因为它们不提供必要的随机访问。但是,如果真的有必要,他提出实施一种解决方案,将其全部加载到 RAM 中,但这会占用大约 500 MB,我想这对于 android 应用程序来说太多了,所以我想最好还是把它解压到某个地方。

PS:这是我的解包解决方案(如果您使用日志记录,可以将 System.out.println 语句替换为 logger 语句,如果您不喜欢它们,可以将其删除):

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/** Allows WordNet to be run from within a jar file by unpacking it to a temporary directory.**/
public class WordNetUnpacker
{
    static final String ID = "178558556719"; // minimize the chance of interfering  with an existing directory  
    static final String jarDir = "models/en/wordnet/dict";

    /**If running from within a jar, unpack wordnet from the jar to a temp directory (if not already done) and return that.
     * If not running from a jar, just return the existing wordnet directory.
     * @see getUnpackedWordNetDir(Class)*/
    static File getUnpackedWordNetDir() throws IOException
    {return getUnpackedWordNetDir(WordNetUnpacker.class);}

    /**If running from within a jar, unpack wordnet from the jar to a temp directory (if not already done) and return that.
     * If not running from a jar, just return the existing wordnet directory.
     * @param clazz the class in whose classloader the wordnet resources are found.
     * @see getUnpackedWordNetDir()**/

    static File getUnpackedWordNetDir(Class clazz) throws IOException
    {
        String codeSource = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
        System.out.println("getUnpackedWordNetDir: using code source "+codeSource);
        if(!codeSource.endsWith(".jar"))
        {
            System.out.println("not running from jar, no unpacking necessary");
            try{return new File(WordNetUnpacker.class.getClassLoader().getResource(jarDir).toURI());}
            catch (URISyntaxException e) {throw new IOException(e);}
        }
        try(JarFile jarFile = new JarFile(codeSource))
        {
            String tempDirString = System.getProperty("java.io.tmpdir");
            if(tempDirString==null) {throw new IOException("java.io.tmpdir not set");}
            File tempDir = new File(tempDirString);
            if(!tempDir.exists()) {throw new IOException("temporary directory does not exist");}
            if(!tempDir.isDirectory()) {throw new IOException("temporary directory is a file, not a directory ");}
            File wordNetDir = new File(tempDirString+'/'+"wordnet"+ID);
            wordNetDir.mkdir();
            System.out.println("unpacking jarfile "+jarFile.getName());
            copyResourcesToDirectory(jarFile, jarDir, wordNetDir.getAbsolutePath());
            return wordNetDir;
        }       
    }
    /** Copies a directory from a jar file to an external directory. Copied from <a href="http://stackoverflow.com/a/19859453/398963">Stack Overflow</a>. */
    public static void copyResourcesToDirectory(JarFile fromJar, String jarDir, String destDir) throws IOException
    {
        int copyCount = 0;
        for (Enumeration<JarEntry> entries = fromJar.entries(); entries.hasMoreElements();)
        {
            JarEntry entry = entries.nextElement();
            if(!entry.getName().contains("models")) continue;
            if (entry.getName().startsWith(jarDir) && !entry.isDirectory()) {
                copyCount++;
                File dest = new File(destDir + "/" + entry.getName().substring(jarDir.length() + 1));
                File parent = dest.getParentFile();
                if (parent != null) {
                    parent.mkdirs();
                }

                FileOutputStream out = new FileOutputStream(dest);
                InputStream in = fromJar.getInputStream(entry);

                try {
                    byte[] buffer = new byte[8 * 1024];

                    int s = 0;
                    while ((s = in.read(buffer)) > 0) {
                        out.write(buffer, 0, s);
                    }
                } catch (IOException e) {
                    throw new IOException("Could not copy asset from jar file", e);
                } finally {
                    try {
                        in.close();
                    } catch (IOException ignored) {}
                    try {
                        out.close();
                    } catch (IOException ignored) {}
                }
            }
        }
        if(copyCount==0) System.out.println("Warning: No files copied!");
    }
}
于 2013-12-16T12:02:20.303 回答
0

您可以将所有 dict 文件从“assets”复制到应用程序的内部目录。只需在第一个应用程序启动时执行一次。从那时起,您可以像这样随意使用 JWI:

String path = getFilesDir() + "/dict";
URL url = new URL("file", null, path);
IDictionary dict = new Dictionary(url);
于 2018-11-09T11:30:47.607 回答