用记事本打开一张 JPG 图片,将所有“文本”粘贴到一个新的记事本文件中,更改为 .JPG 并且不再打开。为什么?

Ngu*_*anh 84 windows notepad jpeg

这种现象一直让我有问题要问。

这是详细的实验,我的操作系统是 Windows 7 x64 SP1:

  • 我通过简单地更改其扩展名将图片 (JPG) 文件更改为 TXT(或者可以选择使用记事本打开 JPG,同样的事情)

它应该看起来像这样,看起来很奇怪的文本序列,其中一些(非常罕见)实际上是有意义的,就像下面的截图“creator: dg-jpeg v1.0 ...”

示例 JPG 文本

  • 我禁用了换行并使用 Ctrl+A 选择了所有文本(以确保没有遗漏任何内容)
  • 我将复制的文本粘贴到另一个空白 TXT 文件并将其另存为 JPG,并将新文件大小与原始 JPG 进行比较。所有这些(原始 JPG、转换后的 TXT 文件和新创建的 TXT 文件)的大小完全相同,以字节为单位。

当我尝试打开时,Windows 会说“Windows 照片查看器无法打开此图片,因为文件似乎已损坏、损坏或太大”

我什至尝试使用另一种方法对其进行测试:用记事本打开 JPG,我从一个容易记住的位置(如第二行的第一个字符)剪切一个已知字符,然后保存文件。查看器当然会显示相同的消息。然后我再次打开它并将字符粘贴到确切位置(记事本会记住它的退出状态,如窗口位置、换行、字体大小......所以我没有问题)

而且还是同样的错误。你可以试试这个来获得想法,记得选择一张小图片,否则记事本会像一个生锈的老人。

造成这种现象的原因可能是什么?

man*_*per 82

根据用于打开文件的编码,您可能会看到不同的行为。我的 Windows 7 记事本允许以 ANSI、UTF-8、Unicode 或 Unicode big endian 打开文件。

我已经用 gimp 创建的小 2x2 像素 jpeg 图像测试了这个问题,并使用 ANSI 编码打开和保存图像文件。使用十六进制编辑器打开原始图像和保存的图像,我看到所有 00 序列(两个十六进制数字,NUL 控制字符)都已转换为 20(空格字符)。

在十六进制编辑器中将所有 20 x 00 替换回图像格式。

我用谷歌搜索了一下,我没有找到任何解释它为什么这样做的参考资料。只有对警告它的帖子的引用(谷歌缓存链接,页面不可用)。

如果您将文件保存/打开为 UTF-8,它似乎仍会将 NUL 字符转换为空格,但由于从单字节字符转换为 UTF-8 多字节序列,它也会增加生成的文件大小。

如果您将文件保存/打开为 Unicode,它似乎仍会将 NUL 字符转换为空格,但还会在文件开头添加一个字节,即BOM

  • 我怀疑 notepad.exe 是 .NET 可执行文件。 (25认同)
  • 0x00 是 C 字符串中的字符串终止符。他们可能已经替换了它们,因为文本文件不应该包含它们。记事本是一个非常古老的程序。 (22认同)
  • @Bakuriu AC 字符串肯定可以存在于文件中;我可以想到许多包含它们的文件格式。Windows 应用程序附带的绝大多数应用程序都是本机的,而不是 .NET。也就是说,记事本不会将以空字符结尾的字符串写入文件。 (11认同)
  • @SJuan76 嗯?C++ 没有定义名为 `byte` 的数据类型。也许您正在考虑使用其他语言。并且应用程序开发人员可以处理他们认为合适的二进制数据,包括使用 C 字符串,如果他们愿意的话。正如我之前所说,我可以想到许多包含 C 字符串的二进制文件格式。 (5认同)
  • @Bakuriu:Windows 程序通常不是用 .Net 编写的。它的核心是 C/C++ 和本机。微软开发的 .Net 应用程序之一是 live writer,现已停产。 (4认同)
  • 如果您将文件加载到 ram 中,然后使用字符串指针引用它,则它只会查找第一个 0x00。记事本早于 .NET,不会使用 CLR。(我知道事实上,如果它没有坏,它只会得到最少的维护,不要修复它) (3认同)
  • “C 字符串可以存在于文件中”--> 好吧,如果你将它们*序列化* 到磁盘,它们就可以;但是将数据从内存数据结构序列化到块设备上的文件的过程,有点让它*不再是 C 字符串*,因为 C 本身不再能够在不调用操作系统的情况下本地读取/写入序列化数据用于读取和写入文件的例程。此外,当 C 字符串写入磁盘时,它*通常* 以某种文本格式编码,例如 UTF-8 或 ASCII。这可能涉及(以各种方式)转换构成虚拟内存中的 C 字符串缓冲区的原始位。 (2认同)
  • 任何在语义上以“\0”结尾的字符数组都可以称为 C 字符串(取决于其用途),因此它们绝对可以在文件中找到!不要将 C 字符串与字符串文字混淆。 (2认同)
  • 原因是因为所有的文本文件都是二进制文件,但并非所有的二进制文件都是文本文件。文本文件遵循规则,称为字符编码。如果文件不遵守规则,它将被更改以匹配规则。JPEG 文件遵循特定于 JPEG 的规则,并且不符合任何字符编码,如果这样做会很浪费(字符编码不使用每个字节中的所有可能位)。转换为字符编码会导致数据丢失/损坏。不过,一些文本编辑器确实支持二进制。 (2认同)
  • 记事本是旧的,用什么语言编写都没有关系。记事本使用标准编辑控件,标准编辑控件文本通过 [`WM_SETTEXT`](http://msdn.microsoft.com/) 设置en-us/library/windows/desktop/ms632644(v=vs.85).aspx),并且此消息假定字符串以空字符结尾,并且不提供字符串长度参数。记事本可以在其编辑器中包含空值的唯一方法是它是否使用自定义组件和自定义消息来设置文本,而它(理所当然地)没有这样做。C/C++/VB/.NET 的讨论完全无关紧要。 (2认同)

bha*_*era 39

为什么失败:

记事本(ASCII code 32)NUL 等字符创建空格字符,(ASCII code 0)因为 Windows API 的文本框只允许以空字符结尾的char * ASCIIZ(字符数组、指针)。它在第一个 NUL 处被切断。

发生这种情况是因为Windows API主要是用C语言编写的,而空终止字符串是常见的功能之一。即使现代 Windows 和 Unicode 被认为是相同的空终止字符串也会发生。所以记事本只需用空格替换它们,这样您就可以查看完整的文件。

因此,当您保存文件时,它已损坏。

wikipedia-null 终止的字符串


如何做进一步的研究:

您可以使用类似beyond compare (commercial,trial) 之类的比较器来查看字符替换效果。另请参阅其他二进制比较工具

十六进制比较

:(20) 16 = (32) 10


记事本对大文件动作缓慢的原因

它检查每个字符并用空格替换特殊字符。其他软件不进行内存转换(至少不像记事本那样原始)。它们只是以不同的方式呈现特殊字符。他们使用先进的缓冲技术。


查看 Notepad.exe(XP 32 位)

(我假设它仍然是用 C++ 编写的,或者至少使用一个类似的链接器

记事本

我正在使用PEiD工具(它随着 PE+/64 exe 的引入而停止了开发)

PEiD 可以在Universal Extractor的 bin 文件夹中找到

我提取了记事本。ex_ 文件显然来自 Windows xp iso。试试看。这是使用 7z 的 cab 文件提取。

警告 !您的病毒扫描程序可能会将 Universal Extractor/PEiD 检测为黑客工具或病毒。不信不要下载!!


有关 Windows API 的更多信息

学分:杰森 C

这不仅仅是文本框;WM_SETTEXT通常不提供用于指定字符串长度的参数,并且始终假定字符串以空值终止。您始终可以使用指定字符串长度的自定义消息创建自定义文本框,但记事本和大多数其他程序合理地不会这样做。此外,函数SetWindowText也不提供长度参数。

  • 这不仅仅是文本框;[`WM_SETTEXT`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms632644(v=vs.85).aspx) 一般不提供指定字符串长度的参数,而字符串总是假设终止于 null。您始终可以使用指定字符串长度的自定义消息创建自定义文本框,但记事本和大多数其他程序合理地不会这样做。 (2认同)

Joh*_*hnC 28

记事本不会完全按原样保留所有特殊/扩展字符。我手头没有关于这种行为的参考,但我发现这是例如 UNIX 风格的行尾 LF 的情况,记事本将转换为 CRLF 和 null (0x00),它将忽略。在诸如 JPG 之类的二进制文件中,记事本不保留的字符可能会随机出现。用一个支持 HEX 的编辑器尝试你的实验,然后它应该可以工作。如果我找到了很好的参考资料,并且一旦我测试了 HEX 编辑器,我会更新我的答案。

更新:我尝试了一些著名的程序员编辑器,但其中只有一个立即生效,即 Maël Hörz 的 HxD。我以前从未使用过 HxD,但由于对这篇 Stack 文章的回答,Notepad++ 的十六进制查看器/编辑器插件找到了它。

其他经过几分钟努力后仍无法工作的编辑器是 Notepad++、Notepad2 和 UltraEdit(v17.3,旧版本)。其中一些在前几个字节的复制/粘贴方面存在问题,JPEG文件签名幻数FF D8 FF。也许他们会比我目前有时间更多地摆弄。

  • 实际上,比记事本更频繁地将 LF 转换为 CRLF,它会保留 LF 原样并显示文本,就好像根本没有换行符一样! (3认同)

CJ *_*nis 6

过去,您曾经可以使用 Write back 来做到这一点。它是 Windows 3.1 中的标准程序,但我不记得 Windows 95 是否包含它。写入将允许对其可以打开的任何文件进行二进制安全编辑(可能非常有限的文件大小)。记事本绝对不是二进制安全的(文本保持不变,但非文本字符的实际字节[例如控制代码] 可能会改变),这就是您的 JPG 示例不起作用的原因。尝试获取 Write 的副本(以及非常旧的 Windows)并再次尝试您的实验!

根据维基百科的“Windows Write”文章, Write 被包含到 Windows NT 3.5 中。从 Windows 95 开始,它被 Wordpad 取代。write.exe仍然存在于 Windows 目录中,但只是用于打开写字板的包装器。


sbe*_*ker 5

我认为这不是编码问题,也是字符集问题。JPG 格式基本上是一个字节流。因此允许不可打印的字符,如 NUL、ETX、STX、SOH、DLE 等。

Microsoft 记事本无法显示那些不可打印的字符。它可能会显示某种占位符,例如空字符的空格。因此,使用记事本打开文件不会显示实际内容,而是通过所选编码(utf-8、utf-16 等)解码并通过特定字符集(unicode、ascii 等)显示的内容,不包括非可打印字符。

选择所有显示的文本并将文本复制到剪贴板时,您只会复制包括占位符在内的可打印字符。因此自动将空字符转换为空格并完全忽略其他不可打印的字符。

所以基本上你只是失去了这样做的内容。如果您改用十六进制编辑器,它将完全复制所有内容。


更新:Bhathiya Pereras 的回答是正确的:https ://superuser.com/a/782885/322784 将文本复制到剪贴板时不会忽略不可打印的字符。