草庐IT

java - 从Firefox复制并在Ubuntu中使用Java读取时,剪贴板内容困惑

coder 2023-08-27 原文

背景

我正在尝试使用Java以HTML数据格式获取剪贴板数据。因此,我将它们从浏览器复制到剪贴板。然后我正在使用java.awt.datatransfer.Clipboard来获取它们。

在Windows系统中可以正常使用。但是在Ubuntu中有一些奇怪的问题。最糟糕的是从Firefox浏览器将数据复制到剪贴板。

重现行为的示例

Java代码:

import java.io.*;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;

public class WorkingWithClipboadData {

 static void doSomethingWithBytesFromClipboard(byte[] dataBytes, String paramCharset, int number) throws Exception {

  String fileName = "Result " + number + " " + paramCharset + ".txt";

  OutputStream fileOut = new FileOutputStream(fileName);
  fileOut.write(dataBytes, 0, dataBytes.length);
  fileOut.close();

 }

 public static void main(String[] args) throws Exception {

  Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

  int count = 0;

  for (DataFlavor dataFlavor : clipboard.getAvailableDataFlavors()) {

System.out.println(dataFlavor);

   String mimeType = dataFlavor.getHumanPresentableName();
   if ("text/html".equalsIgnoreCase(mimeType)) {
    String paramClass = dataFlavor.getParameter("class");
    if ("java.io.InputStream".equals(paramClass)) {
     String paramCharset = dataFlavor.getParameter("charset");
     if (paramCharset != null  && paramCharset.startsWith("UTF")) {

System.out.println("============================================");
System.out.println(paramCharset);
System.out.println("============================================");

      InputStream inputStream = (InputStream)clipboard.getData(dataFlavor);

      ByteArrayOutputStream data = new ByteArrayOutputStream();

      byte[] buffer = new byte[1024];
      int length = -1;
      while ((length = inputStream.read(buffer)) != -1) {
       data.write(buffer, 0, length);
      }
      data.flush();
      inputStream.close();

      byte[] dataBytes = data.toByteArray();
      data.close();

      doSomethingWithBytesFromClipboard(dataBytes, paramCharset, ++count);

     }
    }
   }
  }
 }

}

问题描述

我正在做的是在Firefox中打开URL https://en.wikipedia.org/wiki/Germanic_umlaut。然后,我确实选择了“letters:ä”并将其复制到剪贴板。然后运行我的Java程序。之后,生成的文件(仅其中一些作为示例)如下所示:
axel@arichter:~/Dokumente/JAVA/poi/poi-3.17$ xxd "./Result 1 UTF-16.txt" 
00000000: feff fffd fffd 006c 0000 0065 0000 0074  .......l...e...t
00000010: 0000 0074 0000 0065 0000 0072 0000 0073  ...t...e...r...s
00000020: 0000 003a 0000 0020 0000 003c 0000 0069  ...:... ...<...i
00000030: 0000 003e 0000 fffd 0000 003c 0000 002f  ...>.......<.../
00000040: 0000 0069 0000 003e 0000                 ...i...>..

OK,开始时的FEFF看起来像UTF-16BE字节顺序标记。但是FFFD是什么?为什么在单个字母之间有那些0000字节? UTF-16l编码仅是006C。似乎所有字母都以32位编码。但这对于UTF-16是错误的。并且所有非ASCII字符都使用FFFD 0000编码,因此会丢失。
axel@arichter:~/Dokumente/JAVA/poi/poi-3.17$ xxd "./Result 4 UTF-8.txt" 
00000000: efbf bdef bfbd 6c00 6500 7400 7400 6500  ......l.e.t.t.e.
00000010: 7200 7300 3a00 2000 3c00 6900 3e00 efbf  r.s.:. .<.i.>...
00000020: bd00 3c00 2f00 6900 3e00                 ..<./.i.>.

这里EFBF BDEF BFBD看起来不像任何已知的字节顺序标记。并且所有字母似乎都以16位编码,这是UTF-8中所需位的两倍。因此,根据需要,使用的位似乎总是重复计数。请参见上面的UTF-16示例。并且所有非ASCII字母都被编码为EFBFBD,因此也丢失了。
axel@arichter:~/Dokumente/JAVA/poi/poi-3.17$ xxd "./Result 7 UTF-16BE.txt" 
00000000: fffd fffd 006c 0000 0065 0000 0074 0000  .....l...e...t..
00000010: 0074 0000 0065 0000 0072 0000 0073 0000  .t...e...r...s..
00000020: 003a 0000 0020 0000 003c 0000 0069 0000  .:... ...<...i..
00000030: 003e 0000 fffd 0000 003c 0000 002f 0000  .>.......<.../..
00000040: 0069 0000 003e 0000                      .i...>..

与上述示例中的图片相同。所有字母均使用32位编码。除使用代理对的补充字符外,UTF-16中仅应使用16位。而且所有非ASCII字母都使用FFFD 0000编码,因此会丢失。
axel@arichter:~/Dokumente/JAVA/poi/poi-3.17$ xxd "./Result 10 UTF-16LE.txt" 
00000000: fdff fdff 6c00 0000 6500 0000 7400 0000  ....l...e...t...
00000010: 7400 0000 6500 0000 7200 0000 7300 0000  t...e...r...s...
00000020: 3a00 0000 2000 0000 3c00 0000 6900 0000  :... ...<...i...
00000030: 3e00 0000 fdff 0000 3c00 0000 2f00 0000  >.......<.../...
00000040: 6900 0000 3e00 0000                      i...>...

仅为了完整。与上图相同。

因此得出的结论是,从Firefox复制某些内容后,Ubuntu剪贴板完全被弄乱了。至少对于HTML数据风格以及使用Java读取剪贴板时。

使用的其他浏览器

当我使用Chromium浏览器作为数据源执行相同的操作时,问题会变小。

所以我要在Chromium中打开URL https://en.wikipedia.org/wiki/Germanic_umlaut。然后,我确实选择了“letters:ä”并将其复制到剪贴板。然后运行我的Java程序。

结果看起来像:
axel@arichter:~/Dokumente/JAVA/poi/poi-3.17$ xxd "./Result 1 UTF-16.txt" 
00000000: feff 003c 006d 0065 0074 0061 0020 0068  ...<.m.e.t.a. .h
...
00000800: 0061 006c 003b 0022 003e 00e4 003c 002f  .a.l.;.".>...<./
00000810: 0069 003e 0000                           .i.>..

Chromium在剪贴板中的HTML数据样式中具有更多围绕所选HTML的样式。但是编码看起来正确。也适用于非ASCII的ä = 00E4。但是还有一个小问题,结尾处还有其他字节0000,这些字节不应该存在。在UTF-16中,末尾还有2个额外的00字节。
axel@arichter:~/Dokumente/JAVA/poi/poi-3.17$ xxd "./Result 4 UTF-8.txt" 
00000000: 3c6d 6574 6120 6874 7470 2d65 7175 6976  <meta http-equiv
...
000003f0: 696f 6e2d 636f 6c6f 723a 2069 6e69 7469  ion-color: initi
00000400: 616c 3b22 3ec3 a43c 2f69 3e00            al;">..</i>.

同上。对于UTF-8,编码看起来正确。但是,在末尾还有一个额外的00字节,该字节不应存在。

环境
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.4 LTS"


Mozilla Firefox 61.0.1 (64-Bit)


java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)

问题

我在代码中做错了吗?

有人可以建议如何避免剪贴板中的内容困惑吗?由于非ASCII字符丢失了,至少从Firefox复制时,我认为我们无法修复此内容。

这是一个已知问题吗?有人可以确认相同的行为吗?如果是这样,Firefox中是否已经有关于此的错误报告?

还是仅在Java代码读取剪贴板内容时才会出现的问题?好像好像。因为如果我从Firefox复制内容并将其粘贴到Libreoffice Writer中,则Unicode会正确显示。而且,如果我随后将内容从Writer复制到剪贴板并使用Java程序读取它,那么UTF编码是正确的,除了末尾的其他00字节。因此,从Writer复制的剪贴板内容的行为类似于从Chromium浏览器复制的内容。

新见解

字节0xFFFD似乎是Unicode字符“REPLACEMENT CHARACTER”(U + FFFD)。因此0xFDFF是此字符的小端序表示,而0xEFBFBD是此字符的UTF-8编码。因此,所有结果似乎都是错误解码和重新编码Unicode的结果。

似乎来自Firefox的剪贴板内容始终是UTF-16LEBOM。但是,然后Java将其作为UTF-8获取。因此2个字节的BOM变成两个困惑的字符,用0xEFBFBD替换,每个附加的0x00序列变成它们自己的NUL字符,所有不正确的UTF-8字节序列的字节序列都变成困惑的字符,用0xEFBFBD替换。然后,此伪UTF-8将被重新编码。现在垃圾已完成。

例:

具有BOM的UTF-16LE中的aɛaüa序列为0xFFFE 6100 5B02 6100 FC00 6100

视为UTF-8(0xEFBFBD =不正确的UTF-8字节序列)=
0xEFBFBD 0xEFBFBD a NUL [ STX a NUL 0xEFBFBD NUL a NUL

重新编码为UTF-16LE的伪ASCII将是:0xFDFF FDFF 6100 0000 5B00 0200 6100 0000 FDFF 0000 6100 0000
重新编码为UTF-8的伪ASCII将是0xEFBF BDEF BFBD 6100 5B02 6100 EFBF BD00 6100
而这正是发生的情况。

其他例子:
 = 0x00C2 =伪UTF-8中的UTF-16LE中的C200 = 0xEFBFBD00
= 0x80C2 =伪UTF-8中的UTF-16LE中的C280 = 0xC280

因此,我认为不是Firefox的原因,而是UbuntuJava的运行时环境。而且由于从Firefox复制/粘贴到Writer可以在Ubuntu中进行,所以我认为Java的运行时环境无法正确处理Ubuntu剪贴板中的Firefox数据类型。

新见解:

我已经比较了自己的flavormap.propertiesWindows 10Ubuntu文件,但有所不同。在Ubuntu中,text/html的本地名称是UTF8_STRING,而在Windows中,则是HTML Format。所以我认为这可能是问题所在。所以我加了线
HTML\ Format=text/html;charset=utf-8;eoln="\n";terminators=0
flavormap.properties中的Ubuntu文件中。

之后:
Map<DataFlavor,String> nativesForFlavors = SystemFlavorMap.getDefaultFlavorMap().getNativesForFlavors(
   new DataFlavor[]{
   new DataFlavor("text/html;charset=UTF-16LE")
   });

System.out.println(nativesForFlavors);

版画
{java.awt.datatransfer.DataFlavor[mimetype=text/html;representationclass=java.io.InputStream;charset=UTF-16LE]=HTML Format}

但是,用Java读取时,Ubuntu剪贴板内容的结果没有变化。

最佳答案

看了很多之后,看起来像是a longstanding bug with Java(甚至更老的报告here)。

X11 Java组件似乎希望剪贴板数据始终使用UTF-8编码,而Firefox使用UTF-16编码。由于这些假设,Java通过强制将UTF-16解析为UTF-8来使文本变形。我曾尝试过,但找不到解决此问题的好方法。 “text/html”的“text”部分似乎向Java指示从剪贴板接收的字节应始终首先解释为文本,然后以各种形式提供。我找不到从X11访问预转换的字节数组的任何直接方法。

关于java - 从Firefox复制并在Ubuntu中使用Java读取时,剪贴板内容困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51454654/

有关java - 从Firefox复制并在Ubuntu中使用Java读取时,剪贴板内容困惑的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐