Java 为什么path.toString()的结果在Linux上无法显示所有字符,但在windows上显示为ok

Java 为什么path.toString()的结果在Linux上无法显示所有字符,但在windows上显示为ok,java,character-encoding,java-io,Java,Character Encoding,Java Io,在我的Java代码中,我使用FileVisitor遍历文件系统并创建路径结构,然后稍后将其转换为json对象以在html中呈现 在Windows上运行时,即使在linux文件系统上也可以正常运行;在linux上运行时,在同一个(现在是本地)文件系统上,在路径上调用toString()时,它无法正确呈现特殊字符 i、 e Windows调试输出 CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte Lôbo- Requiem

在我的Java代码中,我使用FileVisitor遍历文件系统并创建路径结构,然后稍后将其转换为json对象以在html中呈现

在Windows上运行时,即使在linux文件系统上也可以正常运行;在linux上运行时,在同一个(现在是本地)文件系统上,在路径上调用toString()时,它无法正确呈现特殊字符

i、 e Windows调试输出

CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte Lôbo- Requiem
html显示ok为

Duarte Lôbo- Requiem
但是linux调试输出提供了

CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte L??bo- Requiem
html显示为两个带问号的黑钻石,而不是字符

为什么会发生这种情况,路径是由FileVisitor类提供的,因此必须正确构造(即我自己没有对其进行黑客攻击),然后在路径上调用toString()

这是字体问题吗?我在linux系统上遇到了一些字体问题,但这里我只是将字符串返回到html,因此无法看到连接

可能是编码问题,但我看不到有明确设置编码的地方

下面的大部分代码,显示linux无效输出的调试在createJsonData()方法中

Edit:我已经修复了日志记录问题,以便将输出写入UTF-8

  FileHandler fe = new FileHandler(logFileName, LOG_SIZE_IN_BYTES, 10, true);
  fe.setEncoding(StandardCharsets.UTF_8.name());
因此,我们现在看到Windows正在正确输出

CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte Lôbo- Requiem
但是Linux正在输出

CreateFolderTree:createJsonData:SEVERE: AddingNode(1):Duarte L��bo- Requiem
如果我在HexEditor中查看它,它会给出Lï½ï½bo的输出

4CEF BF BDEF BF BD 62 6F

编辑:部分解决方案

我偶然发现

并发现建议添加此项

 -Dsun.jnu.encoding=UTF-8
它起作用了,现在文件显示正常

不幸的是,若用户点击了这样一个文件并将其发送回服务器,我现在就得到了这个错误

java.lang.NullPointerException
    at java.base/sun.nio.fs.UnixPath.normalizeAndCheck(Unknown Source)
    at java.base/sun.nio.fs.UnixPath.<init>(Unknown Source)
    at java.base/sun.nio.fs.UnixFileSystem.getPath(Unknown Source)
    at java.base/java.nio.file.Paths.get(Unknown Source)
    at com.jthink.songkong.server.callback.ServerFixSongs.configureFileMapping(ServerFixSongs.java:59)
    at com.jthink.songkong.server.callback.ServerFixSongs.startTask(ServerFixSongs.java:88)
    at com.jthink.songkong.server.CmdRemote.lambda$null$36(CmdRemote.java:107) 

对于html,您需要设置一个与您的需要相匹配的适当字符集,或者最好坚持使用ASCII,并对所有非ASCII字符使用html编码。 即使没有为您的html显示定义特定的字符集,这也可以工作


调试输出似乎在字符集之间经过多次转换。您发送到控制台的文本似乎使用
UTF-8
作为编码转换为字节,从而从
ô
转换为
Ã
。然后,似乎有另一个从
字节
-数据到使用系统字符集的字符的转换。Windows的控制台使用
cp1252
作为字符集,而Linux在每个安装基础上有不同的设置。在您的例子中,似乎是ASCII导致UTF-8编码数据转换为两个
,因为这些字节具有ASCII中未定义的值


我不知道您正在使用的日志框架,也不知道您正在使用的日志记录器的具体设置,因此我无法告诉您如何修复该问题,但对于Linux变体,您可以检查控制台的字符集并将其更改为UTF-8,以查看其是否达到预期效果。

因此,与编码问题一样,这需要进行大量调试。不仅有很多不同的事情会影响它,它们在不同的时间也会影响它,所以第一个任务总是检查它首先在哪里出错

正如处理
ïïï½
所显示的那样,一旦出错,它可能会出现更多错误,如果您尝试从最终结果开始调试,这就像从腐烂的洋葱上剥皮一样


在这种情况下,问题的根源在于操作系统区域设置,该区域设置为
POSIX
。这个旧标准使您的操作系统看起来像是70年代的操作系统,带有
ASCII
编码和其他过时的细节。ASCII编码将阻止操作系统理解文件名、文本或任何包含更多外来字符的内容。这会导致奇怪的问题,因为JVM本身做得很好,但任何时候它与操作系统通信(打印到文本文件,要求打开具有特定名称的文件),都有可能发生损坏,因为操作系统不理解JVM在说什么

就好像有人在和你说话,每隔一段时间他就会在里面放一个中文单词。你在用英语写下他说的话,但你用“不明白?”替换掉的每一个中文单词


区域设置(在
/etc/default/locale
中)通常包含正常的默认值,但正如我们在这里看到的,您不能总是相信这一点。对于任何现代系统,您都需要像
en\u en.UTF-8
这样的区域设置值。在这个时代,你永远不想看到POSIX。

我不认为这能回答你的全部问题,但可能会有帮助。@Remit我认为这解释了为什么调试输出不太正确,但没有解释为什么json数据实际上是不同的。很抱歉,我会继续四处寻找。我不明白为什么从linux机器返回的数据与从Windows机器返回的数据不同(除了明显的/,\differences)@Kayaman:问题只是在您的日志输出中?您的两个操作系统的系统字符集完全不同(Linux最有可能是UTF8)。不,日志输出并不重要,除了表示存在差异之外,问题是html的显示方式不同,我假设是因为json不同。或者我错过了输出的第二部分@日志输出很重要,因为在windows上它显示UTF-8字节被解释为CP1252,而在Linux上它确实不清楚发生了什么。看起来好像有一个“三重”编码正在发生,所以
UTF-8
字节首先被解释为
ISO-8859-1
,而这些字节又被解释为
UTF-8
,导致替换字符
@Kayaman我修复了记录器问题,以便您可以看到toString()实际返回的是什么,这有帮助吗?我不明白为什么这被否决了。这是正确的(尽管它不能解决问题,只是解释了它)。@Lothar-Ive-fi
   import com.google.common.base.Strings;
    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import com.jthink.songkong.analyse.analyser.Counters;
    import com.jthink.songkong.analyse.general.Errors;
    import com.jthink.songkong.cmdline.SongKong;
    import com.jthink.songkong.fileloader.RecycleBinFolderNames;
    import com.jthink.songkong.server.fs.Data;
    import com.jthink.songkong.server.fs.PathWalker2;
    import com.jthink.songkong.server.fs.State;
    import com.jthink.songkong.ui.MainWindow;
    import com.jthink.songkong.ui.progressdialog.FixSongsCounters;
    import spark.Request;
    import spark.Response;

    import java.io.IOException;
    import java.net.UnknownHostException;
    import java.nio.file.*;
    import java.nio.file.attribute.BasicFileAttributes;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    import java.util.logging.Level;


    /**
     * Count the number of files that can be loaded, for information purposes only
     */
    public class CreateFolderTree
    {
        private Path treeRoot;

        Set<Path> keys = new HashSet<Path>();


        public static class VisitFolder
                extends SimpleFileVisitor<Path>
        {

            private Set<Path> keys;
            private Integer maxDepth;
            private int depth;

            public VisitFolder(Set<Path> keys, Integer maxDepth)
            {
                this.keys=keys;
                this.maxDepth = maxDepth;
            }

            /**
             *
             * @param dir
             * @param attrs
             * @return
             * @throws IOException
             */
             /*
             * Ignore some dirs
             * @param dir
             * @param attrs
             * @return
             * @throws IOException
             */
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                    throws IOException
            {
                try
                {
                    if (dir.toFile().getName().equals(".AppleDouble"))
                    {
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    else if (dir.toString().equals("/proc"))
                    {
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    else if (dir.toString().equals("/dev"))
                    {
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    else if (RecycleBinFolderNames.isMatch(dir.toFile().getName()))
                    {
                        MainWindow.logger.log(Level.SEVERE, "Ignoring " + dir.toString());
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    else if (dir.toString().toLowerCase().endsWith(".tar"))
                    {
                        return FileVisitResult.SKIP_SUBTREE;
                    }

                    depth++;

                    if(depth > maxDepth)
                    {
                        depth--;
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    keys.add(dir);
                    return super.preVisitDirectory(dir, attrs);
                }
                catch(IOException e)
                {
                    MainWindow.logger.warning("Unable visit dir:"+dir + ":"+e.getMessage());
                    return FileVisitResult.SKIP_SUBTREE;
                }
            }


            /**
             *
             * Tar check due to http://stackoverflow.com/questions/14436032/why-is-java-7-files-walkfiletree-throwing-exception-on-encountering-a-tar-file-o/14446993#14446993
             * SONGKONG-294:Ignore exceptions if file is not readable
             *
             * @param file
             * @param exc
             * @return
             * @throws IOException
             */
            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
            {

                if (file.toString().endsWith(".tar")) {
                    //We dont log to reports as this is a bug in Java that we are handling not a problem in SongKong
                    MainWindow.logger.log(Level.SEVERE, exc.getMessage());
                    return FileVisitResult.CONTINUE;
                }

                try
                {
                    FileVisitResult result = super.visitFileFailed(file, exc);
                    return result;
                }
                catch(IOException e)
                {
                    MainWindow.logger.warning("Unable to visit file:"+file + ":"+e.getMessage());
                    return FileVisitResult.CONTINUE;
                }
            }

            /**
             * SONGKONG-294:Ignore exception if folder is not readable
             *
             * @param dir
             * @param exc
             * @return
             * @throws IOException
             */
            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                    throws IOException
            {
                depth--;
                try
                {
                    FileVisitResult result = super.postVisitDirectory(dir, exc);
                    return result;
                }
                catch(IOException e)
                {
                    MainWindow.logger.warning("Unable to count files in dir(2):"+dir);
                    return FileVisitResult.CONTINUE;
                }
            }
        }

        public CreateFolderTree(Path treeRoot)
        {
            this.treeRoot = treeRoot;
        }

        public String start(int depth)
        {
            VisitFolder visitFolder;
            try
            {

                if(treeRoot==null)
                {
                    for (Path path : FileSystems.getDefault().getRootDirectories())
                    {
                        visitFolder = new VisitFolder(keys, depth);
                        Files.walkFileTree(path, visitFolder);
                    }
                }
                else
                {
                    visitFolder = new VisitFolder(keys, depth);
                    Files.walkFileTree(treeRoot, visitFolder);
                }

                PathWalker2 pw = new PathWalker2();
                for (Path key : keys)
                {
                    //SONGKONG-505: Illegal character in Filepath problem prevented reportFile creation
                    try
                    {
                        pw.addPath(key);
                    }
                    catch (InvalidPathException ipe)
                    {
                        MainWindow.logger.log(Level.SEVERE, ipe.getMessage(), ipe);
                    }
                }
                Gson gson = new GsonBuilder().create();
                return gson.toJson(createJsonData(pw.getRoot()));
            }
            catch (Exception e)
            {
                handleException(e);
            }
            return "";
        }

        public void handleException(Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Unable to count files:"+e.getMessage(), e);
            Errors.addError("Unable to count files:"+e.getMessage());
            MainWindow.logger.log(Level.SEVERE, e.getMessage());
            Counters.getErrors().getCounter().incrementAndGet();
            SongKong.refreshProgress(FixSongsCounters.SONGS_ERRORS);
        }

        /**
         * Add this node and recursively its children,  returning json data representing the tree
         *
         * @param node
         * @return
         */
        private Data createJsonData(PathWalker2.Node node)
        {
            Data data = new Data();
            if(node.getFullPath()!=null)
            {
                data.setId(node.getFullPath().toString());
                if(node.getFullPath().getFileName()!=null)
                {
                    MainWindow.logger.severe("AddingNode(1):"+node.getFullPath().getFileName().toString());
                    data.setText(node.getFullPath().getFileName().toString());
                }
                else
                {
                    MainWindow.logger.severe("AddingNode(2):"+node.getFullPath().toString());
                    data.setText(node.getFullPath().toString());
                }
            }
            else
            {
                try
                {
                    data.setText(java.net.InetAddress.getLocalHost().getHostName());
                    data.setId("#");
                    State state = new State();
                    state.setOpened(true);
                    data.setState(state);
                }
                catch(UnknownHostException uhe)
                {
                    data.setText("Server");
                }
            }

            //Recursively add each child folder of this node
            Map<String, PathWalker2.Node> children = node.getChildren();
            if(children.size()>0)
            {
                data.setChildren(new ArrayList<>());
                for (Map.Entry<String, PathWalker2.Node> next : children.entrySet())
                {
                    data.getChildren().add(createJsonData(next.getValue()));
                }
            }
            else
            {
                data.setBooleanchildren(true);
            }
            return data;
        }

        public static String createFolderJsonData(Request request, Response response)
        {
            if(Strings.nullToEmpty(request.queryParams("id")).equals("#"))
            {
                CreateFolderTree cft = new CreateFolderTree(null);
                String treeData = cft.start(1).replace("booleanchildren", "children");
                return treeData;
            }
            else
            {
                CreateFolderTree cft = new CreateFolderTree(Paths.get(request.queryParams("id")));
                String treeData = cft.start(2    ).replace("booleanchildren", "children");
                return treeData;
            }
        }

    }


    import java.nio.file.Path;
    import java.util.Collections;
    import java.util.Map;
    import java.util.TreeMap;

    /** Constructs a tree of folders based on a list of filepaths
     *
     * i.e a give it a list if all folder that  contain files that have been modified and it creates a hierachy
     * that can then be used to generate a data structure for use by jstree
     *
     */
    public class PathWalker2
    {
        private final Node root;


        public PathWalker2()
        {
            root = new Node();
        }

        public Node getRoot()
        {
            return root;
        }

        /**
         * Represent a node on the tree (may/not have children)
         */
        public static class Node
        {
            //Keyed on name and node
            private final Map<String, Node> children = new TreeMap<>();

            private Path fullPath;

            public Node addChild(String name)
            {

                if (children.containsKey(name))
                    return children.get(name);

                Node result = new Node();
                children.put(name, result);
                return result;
            }

            public Map<String, Node> getChildren()
            {
                return Collections.unmodifiableMap(children);
            }

            public void setFullPath(Path fullPath)
            {
                this.fullPath = fullPath;
            }

            public Path getFullPath()
            {
                return fullPath;
            }
        }

        /**
         * @param path
         */
        public void addPath(Path path)
        {
            Node node = root.addChild((path.getRoot().toString().substring(0, path.getRoot().toString().length() - 1)));

            //For each segment of the path add as child if not already added
            for (int i = 0; i < path.getNameCount(); i++)
            {
                node = node.addChild(path.getName(i).toString());
            }

            //Set full path of this node
            node.setFullPath(path);
        }


    }