【问题标题】:Converting a null-terminated memory stream to unicode string将 null 终止的内存流转换为 unicode 字符串
【发布时间】:2011-01-19 17:39:48
【问题描述】:

在 Delphi XE 中,我从剪贴板捕获 CF_UNICODETEXT 数据。结果是一个以两个空字节结束的流。要获取复制到剪贴板的实际字符串,我需要去除空值。

This similar so question 包含一个从 TMemoryStream 转换为 Delphi 的 unicode 字符串的好方法:

function MemoryStreamToString(M: TMemoryStream): string;
begin
  SetString(Result, M.Memory, M.Size div SizeOf(Char));
end;

然而,在我的例子中,这会产生一个包含尾随空值的字符串。我可以通过限制大小来解决这个问题:

function ClipboardMemoryStreamToString(M: TMemoryStream): string;
begin
  SetString(Result, M.Memory, (M.Size - SizeOf(Char)) div SizeOf(Char));
end;

...但这感觉很难看,“特殊情况”。我想知道是否有一种更简洁的方法来编写代码,这样以后查看代码的任何人(我!)都不会立即问“为什么从流中删除尾随字符?”

编辑:先发制人的问题的一种方法是添加评论。但是,除此之外呢?

【问题讨论】:

  • 你提到 TMemoryStream 很有趣。由于它的内存碎片实现,我今天花了我的整个代码库将它从我的整个代码库中删除(剩下的引用不多)!
  • @David:这很有趣。你的首选替代品是什么?
  • 我自己亲手制作的TBlockAllocatedMemoryStream = class(TStream) 完成了这项工作,没有碎片。它也不需要连续的地址空间,因此避免了 TMemoryStream 的另一个陷阱。

标签: delphi memorystream unicode-string null-terminated


【解决方案1】:

Clipboard.AsText 有什么问题?它为您做所有事情,无需流、戳字节、处理空终止符等。

至于你提出的具体问题,我只想写:

SetString(Result, M.Memory, M.Size div SizeOf(Result[1]) - 1);

【讨论】:

  • 我避免使用 clipbrd 单元,因为我需要存储和放回所有可用的格式,所以我只处理 API 和 Windows 在剪贴板上提供的确切数据缓冲区。实际上,Clipboard.AsText 做了一些巧妙的事情来解决我的问题:Result := PChar(GlobalLock(Data))。以 null 结尾的流确实符合 PChar 格式,但我没想到我会以这种方式获得副本。
  • 如果您希望避免使用 Clipboard 单元,那么我相信您现在已经找到了规范的解决方案。但是,Clipboard 单元可以很容易地用于读取和写入多种格式。显然你不需要使用它,但我认为没有特别的理由避免它。
  • @Mood,保存/恢复剪贴板是可能的,但并非没有不必要的副作用。您将无法 100% 准确地恢复复杂的格式,并且您无法在不给其他应用程序造成麻烦的情况下操作剪贴板。请看我对这个问题的回复:stackoverflow.com/questions/4735559/…
  • @Chris:您自己的知名产品(我是其中的注册用户)在您所说的不能(或不应该)做的事情上做得很好。怎么可能? :) 我的目标是更简单的更多 - 只是cf_text 和cf_unicodetext。我没有接触位图、OLE 对象甚至 RTF。
  • @mood - ;) 好的,谢谢!在选项对话框中查看我的应用程序配置文件。从那里,我让用户选择捕获的格式。默认值通常只是文本、HTML 和位图。它从不试图得到一切。而且我有很多重试逻辑,因为我预计它会失败。但最大的不同是,我不会(通常)在数据到达后立即尝试将数据放回剪贴板。即我避免在剪贴板事件期间创建剪贴板事件。
【解决方案2】:

如果你以 CF_UNICODETEXT 为目标,则需要具体指定 unicode 字符串:

// For old Delphi versions
{$IFNDEF UNICODE}
type
  UnicodeString = WideString;
{$ENDIF}

// For CF_TEXT
function MemoryStreamToAnsiString(M: TMemoryStream): AnsiString;
begin
  SetString(Result, M.Memory, M.Size);
  if (Result <> '') and (Result[Length(Result)] = #0) then
    SetLength(Result, Length(Result) - 1);
end;

// For CF_UNICODETEXT
function MemoryStreamToUnicodeString(M: TMemoryStream): UnicodeString;
begin
  SetString(Result, M.Memory, M.Size div SizeOf(WideChar));
  if (Result <> '') and (Result[Length(Result)] = #0) then
    SetLength(Result, Length(Result) - 1);
end;

// I'm not sure that you should use this form
function MemoryStreamToString(M: TMemoryStream): String;
begin
  SetString(Result, M.Memory, M.Size div SizeOf(Char));
  if (Result <> '') and (Result[Length(Result)] = #0) then
    SetLength(Result, Length(Result) - 1);
end;

如果您 100% 确定该字符串是以零结尾的,那么:

// For CF_TEXT
function MemoryStreamToAnsiString(M: TMemoryStream): AnsiString;
begin
  SetString(Result, M.Memory, M.Size - 1);
end;

// For CF_UNICODETEXT
function MemoryStreamToUnicodeString(M: TMemoryStream): UnicodeString;
begin
  SetString(Result, M.Memory, (M.Size div SizeOf(WideChar)) - 1);
end;

function MemoryStreamToString(M: TMemoryStream): String;
begin
  SetString(Result, M.Memory, (M.Size div SizeOf(Char)) - 1);
end;

【讨论】:

  • 好吧,OP 正在使用 XE,并且假设没有尝试编写在其他版本的 Delphi 上编译的代码。因此它不需要像这样复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 2016-11-03
  • 1970-01-01
  • 1970-01-01
  • 2017-03-04
相关资源
最近更新 更多