平方X 发表于 2018-1-10 15:42:45

[2501]OmegaT 校验机器翻译的结果


# 0x01 java 中改写 python 中的正则
python 中的
```
      chinese_pattern = r'([\u4e00-\u9fa5])'
      need_space_pattern = r'(|{\d}+|\'.+?\'|\".+?\")'
      cn = re.sub(chinese_pattern + need_space_pattern, r'\1 \2', cn)
      cn = re.sub(need_space_pattern + chinese_pattern, r'\1 \2', cn)
```
变为了
```
    public static String formatTranslation(String en, String cn) {
      // \u 需要转义
      String chinesePattern = "([\\u4e00-\\u9fa5])";
      // { 需要转义,但 } 不需要
      String needSpacePattern = "(|\\{\\d}|'.+?'|\".+?\")";
      // 要使用 $ 不能使用 \
      cn = cn.replaceAll(chinesePattern + needSpacePattern, "$1 $2");
      cn = cn.replaceAll(needSpacePattern + chinesePattern, "$1 $2");
      return cn;
    }
```
> `Note that backslashes (\) and dollar signs ($) in the replacement string may cause the results to be different than if it were being treated as a literal replacement string. Dollar signs may be treated as references to captured subsequences as described above, and backslashes are used to escape literal characters in the replacement string.`


# 0x02 java 中运行 python--jython
(https://github.com/jythontools/jython)
```
      PythonInterpreter interpreter = new PythonInterpreter();
      interpreter.execfile("lib/translation_inspection.py");
      PyFunction function = interpreter.get("inspect_space", PyFunction.class);
      PyObject result = function.__call__(new PyString(en), new PyString(cn));
```

## 2.1 声明编码
> SyntaxError: Non-ASCII character in file 'lib/translation_inspection.py', but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

## 2.2 删除不需要的 import
ImportError: No module named

## 2.3 删除不能调用的方法
main 方法
StandardTranslation.inspect_translation

## 2.4 java.lang.IllegalArgumentException: Cannot create PyString with non-byte value
一开始用的
```
      PyFunction function = interpreter.get("inspect_space", PyFunction.class);
      PyObject result = function.__call__(new PyString(en), new PyString(cn));
```
然后报错
java.lang.IllegalArgumentException: Cannot create PyString with non-byte value

又更新了 2.7.1 (不过后来换为 2.7.0 还是不行),结果 function 找不到了(原本就应该找不到才对)
于是改为
```
      PyClass pyClass = interpreter.get("TranslationInspection", PyClass.class);
      PyObject result = pyClass.invoke("inspect_space", new PyString(en), new PyString(cn));
```
还是报 java.lang.IllegalArgumentException: Cannot create PyString with non-byte value

又去翻了一下 issues ,
(https://github.com/jythontools/jython/issues/90)
好像有一些修复,但是换为 Py.newStringOrUnicode() 就可以了。

## 2.5 正则替换方法不正确
之前的经验,应该是后向引用的 \ 不正确,试了一下,python 中也可以使用 $ 后向引用。
于是用将 \\(?=\d) 替换为 \$
但是发现无效,已经进行转义了。好像是对中文的解析不正确,也可能是位置计算有错。
你好java语言 被处理为   
你好j av a语言
测试发现匹配出的结果是 ja ,而正确的应该是 好j

再后来发现,
[渐行渐远silence《Python正则匹配中文与编码总结》](http://blog.csdn.net/silence2015/article/details/60321873)
>关于Python正则表达式匹配中文,其实只要同意编码就行,我电脑用的py2.7,所以字符串前加u,在正则表达式前也加u即可。


>Jython follows closely the Python language and its reference implementation CPython, as created by Guido van Rossum. Jython 2.7 corresponds to CPython 2.7.

于下修改为 u,测试成功。
```
替换
r(?=\'.+?\[\\u4e00-\\u9fa5])

u
```

## 2.6 单例
测时发现 new 是一个耗时操作,约需要 2 点几秒。于是用单例保存了 function
```
PythonInterpreter interpreter = new PythonInterpreter();
```

测试发现,第一次两个插件都调用了该类,初始化只调用一次,但两次调用耗时都挺长。
原因应该是等待单例构造完毕。

然后第二次调用就很快了。



# 0x03 资源的加载
要使用 .py 文件,和之前使用 .js 一样,要考虑如何加载的问题。
放到目录中,或者压缩到 jar 包中,之前放到 jar 包中总是无法加载,这一次又学习了一下。
## 3.1 文件的引用
```
//路径,测试时是模块的根目录,运行项目时是项目的根目录
interpreter.execfile("lib/translation_inspection.py");
```
所以要使用文件的话,测试模块要在模块中新建目录存放,在运行项目时也要在项目中存放。

## 3.2 stream
之前使用过,
```
InputStream inputStream = GoogleTk.class.getResourceAsStream("resource/google_tk.js");
InputStream inputStream = ClassLoader.getSystemResourceAsStream("resource/google_tk.js");
```
当时使用前者不可以,使用后者是可以的,但是后者生成 jar 包后就无法加载了。
(后面会介绍原因是 resolveName )

### java.lang.ClassLoader#getSystemResourceAsStream
调用顺序为
```
java.lang.ClassLoader#getSystemResourceAsStream
java.lang.ClassLoader#getSystemResource
java.lang.ClassLoader#getResource
java.lang.ClassLoader#findResource
java.net.URLClassLoader#findResource
接下来调用
      URL url = AccessController.doPrivileged(
            new PrivilegedAction<URL>() {
                public URL run() {
                  return ucp.findResource(name, true);
                }
            }, acc);
这是是 native 方法,但是多次调用了下面的方法,
sun.misc.URLClassPath#findResource
它持有 loaders 字段
sun.misc.URLClassPath#loaders 中有多个 loader
```
发现 loaders 中只有 resources 、 classes 目录,并没有插件的 jar 包。

### java.lang.Class#getResourceAsStream
java.lang.Class#getResourceAsStream
```
   public InputStream getResourceAsStream(String name) {
      //被修改了名字
      name = resolveName(name);
      ClassLoader cl = getClassLoader0();
      if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
      }
      return cl.getResourceAsStream(name);
    }
...
后面一样调用到
sun.misc.URLClassPath#findResource
```
但是此时比前面多了一个 URLClassPath
第一个包含 jdk/jre/lib/ext/ 中的一些 jar 包;
第二个包含很多,应该是项目使用的, jdk 的,IDEA(用其运行)的 jar 包,以及输出 out/production/classes 和 resources
第三个终于是 plugins 中的 jar 包了

发现是 java.lang.Class#resolveName 修改了名字,于是加上 / 就成功了。
```
    InputStream in = BaseTranslatorX.class.getResourceAsStream("/translation_inspection.py");
    private String resolveName(String name) {
      if (name == null) {
            return name;
      }
      if (!name.startsWith("/")) {
            Class<?> c = this;
            while (c.isArray()) {
                c = c.getComponentType();
            }
            String baseName = c.getName();
            int index = baseName.lastIndexOf('.');
            if (index != -1) {
                name = baseName.substring(0, index).replace('.', '/')
                  +"/"+name;
            }
      } else {
            name = name.substring(1);
      }
      return name;
    }
```

## test 和项目都能使用
运行项目可以从 jar 包中加载,可运行 test ,只有 out/production 和 out/test
于是我就写了先看文件是否存在。
```

      //文件路径,不以 / 开头,测试时是模块的根目录,运行项目就是项目的根目录
      File file = new File("lib/translation_inspection.py");
      if (file.exists()) {
            try {
                interpreter.execfile(file.getAbsolutePath());
                System.out.println("load py from file");
            } catch (Exception e) {
                e.printStackTrace();
            }
      } else {
            //以 / 开头,不会被处理,最后从 jar 中加载到资源
            InputStream in = BaseTranslatorX.class.getResourceAsStream("/lib/translation_inspection.py");
            if (in != null) {
                try {
                  interpreter.execfile(in);
                  System.out.println("load py from input stream");
                  in.close();
                } catch (Exception e) {
                  e.printStackTrace();
                }
            } else {
                System.out.println("py input stream is null");
            }
      }
```
但是 out/production 中应该有 resources 目录的呀。
后来看了一下,这个 resources 目录对应 main/resources 与 main/java 同一级。
于是放进去就好了。
页: [1]
查看完整版本: [2501]OmegaT 校验机器翻译的结果