判断字符是否可打印

3

我希望开发一个十六进制转储视图,但在当前活动的ANSI代码页(CP_ACP)中无法打印的字符会出现问题。如何检测它们并打印一个点代替?

我的函数目前看起来像这样:

function HexChar(j: byte): AnsiChar;
begin
  if j < $20 then result := '.'

  // Dirty workaround which only supports the undefined characters of Windows-1252
  else if (GetACP=1252) and ((j=$81) or (j=$8D) or (j=$8F) or (j=$90) or (j=$9D)) then result := '.'

  else result := AnsiChar(j);
end;

使用Delphi XE4和Courier New字体时,字符$81、$8D、$8F、$90、$9D是不可见的。GetACP返回1252,所以我正在使用Windows-1252。根据维基百科的ISO_8859-1词条,我发现的这个编码范围在Windows-1252中没有定义。我该如何检查具有序号值j的字符是否已在当前活动代码页中定义?

你需要定义你的字符集。你的代码存在严重问题。Char 是一个两个字节的 UTF-16 字符,这不是你想要的。对于十六进制编辑器,你需要使用 ASCII 或者其中一个 ANSI 代码页。你需要在这方面做出一些决策。两个字节的 Char 毫无帮助。 - David Heffernan
有许多 ANSI 代码页可供选择。你想要哪一个?为什么要将8位数据存储在16位类型中?请注意,Chr(j) 并不像你想象的那样从 ANSI 转换为 Unicode。它会产生一个具有序数值 j 的 UTF-16 字符元素。 - David Heffernan
我想要获取用户系统上激活的ANSI字符集(CP_ACP)。这样他/她就能看到与大多数十六进制编辑器中所知道的输出完全相同。现在我将使用AnsiChar(j) - Daniel Marschall
Memo1.Text := AnsiChar($88) 在我的电脑上会产生插入符号,当且仅当 memo 的字体设置为 Courier New 时。不如您给我们提供一个 SSCCE。 - David Heffernan
你可以使用从 System.Win.Crtl 导入的 isprint 函数(或者该函数族中的其他函数),它们正是为此目的而设计的。如果你想要将空格也一并处理,可以使用 isgraph 函数,该函数用于确定字符在呈现时是否可见。 - TLama
显示剩余2条评论
2个回答

2

唉,IsCharAlphaNumeric也不会打印像^~这样的东西,因为它们既不是数字也不是字母。 - Daniel Marschall
感谢这个提示。GetStringType似乎非常可靠。这是我使用GetStringTypeW的代码(点击此处查看),同时也包含了已废弃的GetStringTypeA(点击此处查看)。我还有一个问题没有解决。在我的电脑上,$98映射到Unicode字符$02DC。因此,这个波形符将会自动与其邻居合并(导致十六进制转储不美观)。是否可以使用GetStringTypeW/CT_CTYPE3查询这种组合信息呢? - Daniel Marschall
你使用哪个函数来显示结果字符串?我认为如果你使用适当的函数输出作为输入到IsAnsiPrintable()的原始ANSI字符,那就不应该有任何问题... - ThinkJet
我将每个字符连接到一个WideString中,然后在TMemo中显示。 - Daniel Marschall
相同,但是转换在幕后使用 Delphi 默认的区域设置而不是在调用 GetSTringTypeA 时指定具体的区域设置。有关现代 Delphi 版本中 Unicode 支持的更多信息,请参阅 Embarcadero 网站上的此文档 - ThinkJet
显示剩余9条评论

1
使用 GetGlyphIndices 和 GGI_MARK_NONEXISTING_GLYPHS 来检查字体中是否存在特定字符。

以下是一个示例:

procedure ReplaceNonPrintableChars(var s: string);
var
  GlyphIndicesA: PWordArray;
  Len: Integer;
  I: Integer;
  Cnt: DWORD;
  DC: THandle;
  C: TCanvas;
begin
  DC := GetDC(0);
  try
    C := TCanvas.Create;
    try
      C.Handle := DC;
      C.Font.Name := 'Arial';
      Len := Length(S);
      GetMem(GlyphIndicesA, SizeOf(Word) * Len);
      try
        Cnt := GetGlyphIndices(C.Handle, PChar(S), Len, PWord(GlyphIndicesA), GGI_MARK_NONEXISTING_GLYPHS);
        if not (Cnt = GDI_ERROR) then
          for I := 0 to Cnt - 1 do
            if GlyphIndicesA[I] = $FFFF then
              S[I+1] := '.';
      finally
        Dispose(GlyphIndicesA);
      end;
    finally
      C.Free;
    end;

  finally
    ReleaseDC(0, DC);
  end;
end;

嗯...我该怎么使用它呢?var x: word; dc: hdc; begin dc := GetDc(Memo1.Handle); GetGlyphIndices(dc, PChar(Char(AnsiChar(j))), 1, pword(@x), GGI_MARK_NONEXISTING_GLYPHS)总是返回GDI_ERROR - Daniel Marschall
我已经添加了一个例子。 - Sebastian Z
你的代码可以检测字符是否定义了字体。但问题是该字符是否在代码页中定义。 - Elmue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接