sábado, 15 de setembro de 2012

Utilizando o Corretor Ortográfico do Google

Olá amigons!

Recentemente tive a necessidade de implementar um corretor ortográfico em uma solução Delphi.

Pedi algumas indicações e me sugeriram:

http://www.devmedia.com.br/corretor-ortografico-no-delphi/2544
http://devexpress.com/Products/VCL/ExSpellChecker/

Não cheguei a testá-los mas foram muito bem recomendados.

Mas durante minhas pesquisas acabei me deparando com uma solução do Google.

Eu não tenho certeza, e se algum leitor for mais bem informado e puder fazer a gentileza de colocar as suas considerações nos comentários, mas o Google tem um recurso chamado Google Tool Bar Proxy e um dos recursos oferecidos é justamente o de correção ortográfica.

A URL do serviço é:

http://www.google.com/tbproxy/spell?lang=pt&hl=pt

Basicamente deve-se fazer um POST (HTTP) com um conteúdo XML e analisar o retorno.

Vamos imaginar então a palavra "coracao". O XML de requisição seria:



 coracao


E uma resposta válida seria algo como:



 coração


Indo ao ponto, desenvolvi o código abaixo devidamente comentado. É um projeto do tipo console.

É um ótimo exemplo pois tem uma pitada de XML e Expressão Regular.
Confesso que levei algumas boas horas para desenvolver este exemplo.

Me atrapalhei com o TXMLDocument e o UTF-8. Graças a este post de Caio Oliveira consegui resolver e me dei conta de um aparente bug. Achei estranho, mas de fato foi a solução.

Neste projeto foi desenvolvido a classe TCorretor onde efetivamente esta a solução.

program CorretorOrtografico;

{$APPTYPE CONSOLE}
{$R *.res}

uses System.SysUtils,
  Xml.XMLDoc,
  Xml.XMLIntf,
  IdHTTP,
  System.Classes,
  WinApi.ActiveX,
  System.RegularExpressions,
  IdGlobal;

type

  /// 
  /// Implementa o recurso de correção ortográfica oferecido pelo Google
  /// 
  /// 
  /// Descende de TComponent para servir de base para o XMLDocument
  /// 
  /// 
  /// José Mário Silva Guedes - jmarioguedes@gmail.com - eugostododelphi.blogspot.com
  /// 
  TCorretor = class(TComponent)
  private
    /// 
    /// Estrutura do documento XML
    /// 
    FXML: TXMLDocument;
    /// 
    /// Comunicação HTTP
    /// 
    FHTTP: TidHTTP;
  public
    procedure AfterConstruction; override;
    /// 
    /// Processa efetivamente o corretor ortográfico
    /// 
    ///     /// Texto de entrada
    ///     ///     /// Possibilidades de correção
    ///     ///     /// Motivo de um eventual insucesso
    ///     /// 
    /// Indica o sucesso da operação
    /// 
    function Processar(AEntrada: string; ASaida: TStrings; AMotivo: string): Boolean;
  end;

var
  bRet     : Boolean;
  oCorretor: TCorretor;
  sEntrada : string;
  slSaida  : TStringList;
  sMotivo  : string;
  sPalavra : string;
  cSaida   : Char;
  { TCorretor }

procedure TCorretor.AfterConstruction;
begin
  inherited;
  Self.FHTTP := TidHTTP.Create(Self);
  Self.FXML := TXMLDocument.Create(TDataModule.Create(Self));
end;

function TCorretor.Processar(AEntrada: string; ASaida: TStrings; AMotivo: string): Boolean;
const
  C_URL = 'http://www.google.com/tbproxy/spell?lang=pt&hl=pt';
var
  oRequest  : TStringStream;
  oResponse : TStringStream;
  sResponse : string;
  sRequest  : string;
  sResultado: string;
  _raiz     : IXMLNode;
  _texto    : IXMLNode;
  _resultado: IXMLNode;
  aPartes   : TArray;
begin
  oRequest := nil;
  oResponse := nil;
  try
{$REGION 'Preparando o pedido'}
    Self.FXML.Active := False;
    Self.FXML.Active := True;
    Self.FXML.Version := '1.0';
    Self.FXML.Encoding := 'UTF-16';

    _raiz := Self.FXML.AddChild('spellrequest');
    _raiz.Attributes['textalreadyclipped'] := '0';
    _raiz.Attributes['ignoredups'] := '0';
    _raiz.Attributes['ignoredigits'] := '1';
    _raiz.Attributes['ignoreallcaps'] := '0';

    _texto := Self.FXML.CreateNode('text');
    _texto.Text := AEntrada;
    _raiz.ChildNodes.Add(_texto);
{$ENDREGION}
{$REGION 'Gambiarra do UTF-8'}
    Self.FXML.SaveToXML(sRequest);
    // http://www.caiooliveira.com.br/?p=422
    // http://qc.embarcadero.com/wc/qcmain.aspx?d=37433
    sRequest := TRegEx.Replace(sRequest, 'UTF-16', 'UTF-8');
    oRequest := TStringStream.Create;
    oRequest.WriteString(sRequest);
    oRequest.Seek(0, 0);
{$ENDREGION}
{$REGION 'Request\Response'}
    Self.FHTTP.Request.Accept := 'text/xml';
    Self.FHTTP.Request.ContentType := 'text/xml';
    Self.FHTTP.Request.ContentEncoding := 'utf-8';
    oResponse := TStringStream.Create;
    Self.FHTTP.Post(C_URL, oRequest, oResponse);
    sResponse := UTF8ToString(RawByteString(oResponse.DataString));
    Self.FXML.Active := False;
    Self.FXML.LoadFromXML(sResponse);
{$ENDREGION}
{$REGION 'Analisando a resposta'}
    _resultado := Self.FXML.DocumentElement.ChildNodes.FindNode('c');
    while (Assigned(_resultado)) do
    begin
      sResultado := _resultado.Text;
      aPartes := TRegEx.Split(sResultado, #9);
      for sResultado in aPartes do
      begin
        ASaida.Add(sResultado);
      end;
      _resultado := _resultado.NextSibling;
    end;
    Result := True;
{$ENDREGION}
  finally
    if (Assigned(oRequest)) then
    begin
      oRequest.Free;
    end;
    if (Assigned(oResponse)) then
    begin
      oResponse.Free;
    end;
  end;
end;

begin
  oCorretor := nil;
  slSaida := nil;
  ReportMemoryLeaksOnShutdown := True;
  CoInitialize(nil);

  try
    Writeln('/--------------------------------------------------------------\');
    Writeln('|  .:: DEMONSTRAÇÃO DE USO DO CORRETOR ORTOGRÁFICO GOOGLE ::.  |');
    Writeln('| jmarioguedes@gmail.com - http://eugostododelphi.blogspot.com |');
    Writeln('\--------------------------------------------------------------/');
    Writeln('');
    Writeln('');

    oCorretor := TCorretor.Create(nil);
    slSaida := TStringList.Create;
    repeat
      write('Digite um texto: ');
      Readln(sEntrada);
      Writeln('');

      slSaida.Clear;
      bRet := oCorretor.Processar(sEntrada, slSaida, sMotivo);
      if (bRet) then
      begin
        if (slSaida.Count > 0) then
        begin
          for sPalavra in slSaida do
          begin
            Writeln('-> ' + sPalavra);
          end;
          Writeln('');
        end
        else
        begin
          Writeln('Não houve resultados');
        end;
      end
      else
      begin
        Writeln(Format('Insucesso na correção: ', [sMotivo]));
      end;

      Write('Tecle X para cancelar ou qualquer outra tecla para continuar: ');
      ReadLn(cSaida);

    until (CharInSet(cSaida, ['X', 'x']));

  finally
    if (Assigned(oCorretor)) then
    begin
      oCorretor.Free;
    end;
    if (Assigned(slSaida)) then
    begin
      slSaida.Free;
    end;

    Writeln('- Fim do Sistema - ');
    Readln;
  end;

end.

Minha lista de blogs