有些字符计数两次

Some characters count twice

现在我正试图在文本中找到最长的句子并打印出字符数,包括空格和类似的东西。问题是当我遇到像“š”或“á”这样的字符时,它会将它们计数两次。在那些情况下,我尝试减去一个,但这似乎也不起作用,因为它也将它们减去两次。知道我该如何解决吗?这是计数器的代码。

for i:=1 to length(text) do
      case text[i] of 
        '.','!','?': begin
                        if len>p2 then p2:=len;
                        len:=0
                     end;
         else inc(len);
       end;

p2 是最长句子的计数器,len 是当前句子。

这对我来说适用于 ANSI 字符,包括带有变音符号的字符。由于您没有提到任何特定的字符集,并且您的问题被简单地标记为 ,它应该也适用于您。如果您正在处理其他字符集,那么您需要指出您正在使用哪个特定的 Pascal 编译器,因为对多字节字符的支持在各种 Pascal 方言之间有所不同。

function LongestSentenceCharCount(const Text: string): Integer;
var
  Len: Integer;
  LongLen: Integer;
  i, CurrLen: Integer;
begin
  Len := Length(Text);
  CurrLen := 0;
  LongLen := 0;
  for I := 1 to Len do
  begin
    if Text[i] in ['.', '!', '?'] then
    begin
      if CurrLen > LongLen then
        LongLen := CurrLen;
      CurrLen := 0;
    end
    else
      Inc(CurrLen);

  end;
  Result := LongLen;
end;

处理UTF-8、Unicode等多字节字符集-

根据 Seppy Bloom(当时是 Embarcadero RTL/VCL 的团队负责人)捐赠给 Cary Jensen 的白皮书 (PDF) Delphi Unicode Migration for Mere Mortals: Stories and Advice from the Front Lines 的一些代码,您可以使用一些自 Vista 及更高版本以来,Windows 中提供了规范化功能。我已经调整了上面的函数以使用来自 Seppy(包含在下面)的代码,以及一个示例应用程序来演示如何使用它。该代码是在 Delphi 10.1 Berlin 中开发、编译和测试的,因此如果您使用不同的编译器,则必须对其进行调整,如果您不是 运行 在 Windows Vista 或更高版本下。

program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, WinAPI.Windows;

const
  NormalizationOther = 0;
  NormalizationC     = 1;
  NormalizationD     = 2;
  NormalizationKC    = 5;
  NormalizationKD    = 6;

function IsNormalizedString(NormForm: Integer; lpString: LPCWSTR;
  cwLength: Integer): BOOL; stdcall; external 'Normaliz.dll';

function NormalizeString(NormForm: Integer; lpSrcString: LPCWSTR;
  cwSrcLength: Integer; lpDstString: LPWSTR;
  cwDstLength: Integer): Integer; stdcall; external 'Normaliz.dll';

function NormalizedStringLength(const Str: string): Integer;
var
  Buf: string;
begin
  if not IsNormalizedString(NormalizationC, PChar(Str), -1) then
  begin
    SetLength(Buf, NormalizeString(NormalizationC, PChar(Str),
                                   Length(Str), nil, 0));
    Result := NormalizeString(NormalizationC, PChar(Str),
                                   Length(Str), PChar(Buf), Length(Buf));
  end
  else
    Result := Length(Str);
end;

function LongestSentenceLen(const Text: string): Integer;
var
  Len: Integer;
  i, CurrLen: Integer;
begin
  Len := Length(Text);
  CurrLen := 0;
  Result := 0;
  for i := 1 to Len do
  begin
    // Replaces 'if Text[i] in ['.', '!', '?']', which will work
    // but generates a compiler warning.
    if CharInSet(Text[i], ['.', '!', '?']) then 
    begin
      if CurrLen > Result then
        Result := CurrLen;
      CurrLen := 0;
    end
    else
      Inc(CurrLen, NormalizedStringLength(Text[i]));
  end;
end;

var
  Test: string;

begin
  Test := 'Ahoj, jak se máš? Hello World.';
  WriteLn(Test);
  WriteLn(Format('Longest: %d', [LongestSentenceLen(Test)]));
  ReadLn;
end.

上面的输出是

Ahoj, jak se más? Hello World.
Longest: 16

最近我只在我提到的在线编译器中处理这个问题。我在其他任何地方都试过(免费的 Pascal 和 Turbo Pascal)它工作得很好。

谢谢你的帮助,我认为不同的编译器不会有什么不同。

您没有说明输入文本的表示方式,但您看到的症状与 UTF-8 输入一致。

ASCII 是一个 7 位字符集,不包含任何重音字母。您的变量 text 大概是一个字符数组。对于像 Ahoj, jak se mas? 这样的字符串,每个字符在数组中占据一个位置。对于像 Ahoj, jak se máš? 这样的字符串,'á''š' 字符在 ASCII 范围之外,每个字符表示为 2 个字节,因此在数组中有 2 个槽位。

Wikipedia article on UTF-8 解释了 UTF-8 编码的工作原理。

我建议暂时添加如下内容:

writeln('text[', i, '] = ''', text[i], ''' = ', ord(s[i]));

for 循环的 begin 之后,您可以看到每个字符的值。

这说明了您遇到的问题,但没有说明解决方法。这取决于您的 Pascal 实现对非 ASCII 文本的支持类型。据我所知,Pascal 语言本身没有这样的支持,但是一些特定的实现mmmight.