在 32位Ubuntu 计算机上, JDK 1.7.0 ,我无法打印宽字符。
这是我的代码:
public class JNIFoo {
public native void nativeFoo();
static {
System.loadLibrary("foo");
}
public void print () {
nativeFoo();
System.out.println("The end");
}
public static void main(String[] args) {
(new JNIFoo()).print();
return;
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "JNIFoo.h"
JNIEXPORT void JNICALL Java_JNIFoo_nativeFoo (JNIEnv *env, jobject obj)
{
fwprintf(stdout, L"using fWprintf\n");
fflush(stdout);
}
然后我执行以下命令:
javac JNIFoo.java
javah -jni JNIFoo
gcc -shared -fpic -o libfoo.so -I/path/to/jdk/include -I/path/to/jdk/include/linux foo.c
以下是取决于用于执行程序的JDK的结果:
jdk1.6.0_45 / bin / java -Djava.library.path = / path / to / jni_test JNIFoo
使用fWprintf
结束
jdk1.7.0 / bin / java -Djava.library.path = / path / to / jni_test JNIFoo
结束
jdk1.8.0_25 / bin / java -Djava.library.path = / path / to / jni_test JNIFoo
结束
如您所见,使用JDK 1.7和JDK 1.8,fwprintf无效!
所以我的问题是我能错过使用JDK 1.7(和1.8)来使用宽字符?
注意:如果我调用 fprintf 而不是 fwprintf ,那么没有问题,所有内容都正确打印出来。
根据James的评论,我创建了一个main.c文件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <wchar.h>
#include "JNIFoo.h"
int main(int argc, char* argv[])
{
fwprintf(stdout, L"In the main\n");
Java_JNIFoo_nativeFoo(NULL, NULL);
return 0;
}
然后我就这样编译:
gcc -Wall -L/path/to/jni_test -I/path/to/jdk1.8.0_25/include -I/pat/to/jdk1.8.0_25/include/linux main.c -o main -lfoo
并设置LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/path/to/jni_test
它运作正常:
In the main
using fWprintf
所以问题可能不是来自C。
注意:它在64位计算机上正常运行。 我使用Linux Mint 32位也有类似的问题。
答案 0 :(得分:1)
您不得将窄字符和宽字符的打印混合到同一个流中。 C99引入了流方向的概念,其中I / O流可以是面向对象的或面向字节的(在C99之前,宽字符)在C语言标准中不存在)。从C99§7.19.2/ 4-5:
4)每个流都有方向。在流与外部文件关联之后,但在对其执行任何操作之前,该流没有方向。将宽字符输入/输出功能应用于没有方向的流后,该流将成为面向广泛的流。类似地,一旦将字节输入/输出函数应用于没有方向的流,该流就变成面向字节的流。只有调用
freopen
函数或fwide
函数才能改变流的方向。 (成功调用freopen
会删除任何方向。) 233)5)字节输入/输出功能不应用于面向广泛的流,宽字符输入/输出功能不应用于面向字节的流。 [...]
233)三个预定义的流
stdin
,stdout
和stderr
在程序启动时未定向。
C99标准将窄字符和宽字符函数混合为未定义行为。在实践中,GNU C库说“没有发布诊断。应用程序行为只会很奇怪,或者应用程序只会崩溃。fwide
函数可以帮助避免这种情况。”(source)< / p>
由于JRE负责程序启动,因此它负责stdin
,stdout
和stderr
流,因此也负责它们的方向。 Your JNI code is a guest in its house, don't go changing its carpets.在实践中,这意味着您必须处理您给出的任何流方向,您可以使用fwide(3)
功能检测到这种方向。如果要将宽字符打印到面向字节的流,太糟糕了。您需要通过说服JRE使用面向广播的流,或将宽字符转换为UTF-8或其他内容来解决这个问题。
例如,此代码应适用于所有情况:
JNIEXPORT void JNICALL Java_JNIFoo_nativeFoo (JNIEnv *env, jobject obj)
{
if (fwide(stdout, 0) >= 0) {
// The stream is wide-oriented or unoriented, so it's safe to print wide
// characters
fwprintf(stdout, L"using fWprintf\n");
} else {
// The stream is narrow oriented. Convert to UTF-8 (and hopefully the
// terminal (or wherever stdout is going) can handle that)
char *utf8_string = convert_to_utf8(L"my wide string");
printf("%s", utf8_string);
}
fflush(stdout);
}
答案 1 :(得分:0)
@dalf, 问题出在JDK之外。它是32位版本的GLIBC。 请尝试在您的机器上重现它:
I)创建3个文件:
---foo.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
void foo() {
fwprintf(stdout, L"using fWprintf\n");
fflush(stdout);
}
----main.c:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
void (*foo)();
char *error;
handle = dlopen("libfoo.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&foo) = dlsym(handle, "foo");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
(*foo)();
dlclose(handle);
exit(EXIT_SUCCESS);
}
----mapfile:
SomethingPrivate {
local:
*;
};
II)运行命令:
$ gcc -m32 -shared -fpic -o libfoo.so foo.c
$ gcc -m32 -Xlinker -version-script=mapfile -o main main.c -ldl
$ export LD_LIBRARY_PATH="."
$ ./main
并查看它输出的内容