{"id":1431,"date":"2022-05-05T15:32:20","date_gmt":"2022-05-05T18:32:20","guid":{"rendered":"https:\/\/gladiston.net.br\/?page_id=1431"},"modified":"2022-11-03T14:51:51","modified_gmt":"2022-11-03T17:51:51","slug":"como-extrair-apenas-o-texto-de-um-conteudo-rtf","status":"publish","type":"page","link":"https:\/\/gladiston.net.br\/en\/banco-de-dados\/como-extrair-apenas-o-texto-de-um-conteudo-rtf\/","title":{"rendered":"Como extrair apenas o texto de um conte\u00fado RTF"},"content":{"rendered":"<p>Qualquer que seja a linguagem de programa\u00e7\u00e3o, para ela ser considerada boa suficiente precisa ter os comandos necess\u00e1rios de estrutura de loop, condicionantes, vari\u00e1veis e alguns outros elementos. O mesmo vale para linguagens procedurais SQL(psql daqui em diante).<\/p>\n\n\n\n<p>Talvez um excelente exemplo para provar o valor da linguagem psql do banco de dados FirebirdSQL seja como extrair de um conte\u00fado RTF apenas o texto. Eu fiz o exemplo abaixo alguns anos atr\u00e1s, e ele continua muito eficiente.<\/p>\n\n\n\n<p>Qual o objetivo de extrair o conte\u00fado textual de um RTF? Voc\u00ea deve saber que no Windows, quase sempre texto rico, isto \u00e9, texto com negrito\/it\u00e1lico\/sublinhado, alinhamentos, bullets e afins s\u00e3o editados e armazenados no formato RichText, tamb\u00e9m conhecido como RTF. Um problema com este formato \u00e9 quando precisamos analisar as informa\u00e7\u00f5es contidas al\u00ed dentro os c\u00f3digos de controle est\u00e3o concatenados ao texto impedindo uma leitura suave.<\/p>\n\n\n\n<p>O c\u00f3digo abaixo far\u00e1 essa extra\u00e7\u00e3o, mas ao inv\u00e9s de voc\u00ea apenas &#8220;copiar&#8221; e &#8220;colar&#8221; para suas atividades, estude-o, veja a l\u00f3gica utilizada. Se entend\u00ea-lo, poder\u00e1 adaptar-se a situa\u00e7\u00f5es diferentes, por exemplo extrair texto de conte\u00fados HTML.<\/p>\n\n\n\n<p>O psql abaixo est\u00e1 no formato do FirebirdSQL para SQL Functions, SQL Functions s\u00e3o fun\u00e7\u00f5es que retornam apenas um tipo de informa\u00e7\u00e3o, no exemplo abaixo ela se chamar\u00e1 GET_UNRTF e tem um par\u00e2metro de entrada que \u00e9 um blob do tipo texto cujo formato \u00e9 RTF e retorna o conte\u00fado deste blob em formato blob textual tamb\u00e9m, por\u00e9m plaintext.<\/p>\n\n\n\n<p>O script abaixo foi testado em diversas situa\u00e7\u00f5es e \u00e9 plenamente funcional e at\u00e9 o momento livre de bugs. No entanto, os testes foram em cima de arquivos gerados pelo ms-wordpad que como sabemos \u00e9 bem mais limitado do que um RTF gerado pelo LibreOffice ou MSWord. Segue:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text\/plain&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;TrpContentRestriction&quot;:{&quot;restriction_type&quot;:&quot;exclude&quot;,&quot;selected_languages&quot;:[],&quot;panel_open&quot;:true},&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}\">create or alter function GET_UNRTF (\n    P_SOURCE_RTF blob sub_type text)\nreturns blob sub_type text\nAS\ndeclare variable TAGSTART integer;\ndeclare variable TAGFINISH integer;\ndeclare variable I integer;\ndeclare variable TAGFOUND blob sub_type text;\ndeclare variable RESULT_TEXT blob sub_type text;\ndeclare variable TAG_OPEN varchar(255) = '';\ndeclare variable TAG_CLOSE varchar(255) = '';\ndeclare variable CHAR_TEST varchar(255);\ndeclare variable CHAR_HEX varchar(255);\ndeclare variable CHAR_HEX_TO_STR varchar(255);\ndeclare variable HEXA_LEN integer = 2;\ndeclare variable C_QUOTE varchar(1);\ndeclare variable C_CRLF varchar(10);\ndeclare variable C_SPACE varchar(10);\ndeclare variable C_TAG_PAR varchar(10);\ndeclare variable C_SLASH varchar(10);\nbegin\n  -- essa procedure\/fun\u00e7\u00e3o retorna um texto (blob) sem a por\u00e7\u00e3o de tags rtf, ex:\n  -- ret=STR_UNRTF(string_rtf);   \/\/ resultado: texto sem as tags rtf\n  -- Util para tornar um texto RTF texto pesquis\u00e1vel.\n  result_text='';\n  p_source_rtf=trim(:p_source_rtf);\n  c_quote='''';\n  c_crlf= ascii_char(13)||ascii_char(10);\n  c_space=ascii_char(32);\n  c_tag_par='\\par';\n  c_slash='\\';\n  tag_open='{';\n  tag_close='}';\n\n  -- todo cabecalho de arquivo RTF possui &quot;{\\rtf&quot; ent\u00e3o \u00e9 possivel conferir se\n  -- o parametro de entrada esta mesmo no formato RTF, caaso n\u00e3o esteja ent\u00e3o\n  -- basta retornar o mesmo valor que o parametro de entrada. Isso permite\n  -- ganho de velocidade absurdo.\n  if (position('{\\rtf', :p_source_rtf)&lt;=0) then\n  begin\n    result_text=p_source_rtf;\n    return :result_text;\n    exit;\n  end\n\n  -- RTF \u00e9 bagun\u00e7ado com quebra de linhas, existe tanto o CRLF como tamb\u00e9m a tag \\par \n  -- E o pior \u00e9 que tem \\par seguido de CRLF que se convertido individualmente\n  --   produziria duas linhas vazias ao inves de uma\n  -- Ent\u00e3o \u00e9 preciso substitu\u00ed-los por CRLF antes de remover todas as tags, e deve\n  --   ser com todo cuidado.\n  if (position(:c_tag_par, :p_source_rtf)&gt;0) then\n  begin\n    p_source_rtf=replace (:p_source_rtf, :c_tag_par||c_space||c_crlf, :c_crlf);\n    p_source_rtf=replace (:p_source_rtf, :c_tag_par||c_crlf, :c_crlf);\n    p_source_rtf=replace (:p_source_rtf, :c_tag_par||c_space, :c_crlf);\n    p_source_rtf=replace (:p_source_rtf, :c_tag_par, :c_crlf);\n  end\n  -- por\u00e9m o primeiro caractere se come\u00e7ar com {  ou o ultimo caracter terminar com }\n  -- dever\u00e1 ser removido\n  char_test=left(:p_source_rtf,char_length(:tag_open));\n  if (char_test=:tag_open) then\n  begin\n    p_source_rtf=substring(:p_source_rtf from 2 for char_length(:p_source_rtf));\n    p_source_rtf=trim(:p_source_rtf);\n  end\n  char_test=right(:p_source_rtf,char_length(:tag_close));\n  if (char_test=:tag_close) then\n  begin\n    p_source_rtf=substring(:p_source_rtf from 1 for char_length(:p_source_rtf)-char_length(:tag_close));\n    p_source_rtf=trim(:p_source_rtf);\n  end\n\n  -- Remove tudo que estiver em {tag}\n  tagstart = position (:tag_open, :p_source_rtf);\n  while (:tagstart &gt; 0) do\n  begin\n    tagfinish = position (:tag_close, :p_source_rtf, :tagstart);\n    if (:tagfinish&lt;:tagstart) then\n      tagfinish=char_length(:p_source_rtf);\n    tagfound = substring (:p_source_rtf from :tagstart for ((:tagfinish - :tagstart) + 1));\n    p_source_rtf = replace (:p_source_rtf, :tagfound, '');\n    tagstart = position (:tag_open, :p_source_rtf);\n  end\n\n  -- RTF tem \\escape para caracteres especiais, ex: fabrica\\'e7\\'e3o = fabrica\u00e7\u00e3o\n  -- \u00c8 preciso localizar todos os escapes e troc\u00e1-los pelas suas referencias Hexa-&gt;Ascii\n  tag_open=:c_slash||:c_quote;\n  tag_close=:c_space;\n  tagstart = position (:tag_open, :p_source_rtf);\n  char_hex='';\n  hexa_len=2+(char_length(:tag_open));\n  while (:tagstart &gt; 0) do\n  begin\n    char_hex=substring(:p_source_rtf from :tagstart for :hexa_len);\n    char_hex_to_str='0x'||substring(:char_hex from char_length(:tag_open)+1);\n    i=cast(:char_hex_to_str as int);\n    if (i&gt;0) then\n    begin\n      char_hex_to_str=ascii_char(i);\n    end\n    else\n    begin\n      char_hex_to_str='{'||:char_hex_to_str||'}';\n    end\n    p_source_rtf = replace (:p_source_rtf, :char_hex, :char_hex_to_str);\n    tagstart = position (:tag_open, :p_source_rtf);\n  end\n  p_source_rtf=trim(:p_source_rtf);\n\n  -- RTF tem tags assim:\n  -- \\viewkind4\\uc1\\pard\\sa200\\sl276\\slmult1\\lang1046\\fs20 blabla bla bla\n  -- \u00c9 preciso localizar essas tags e trocar por vazios\n\n  tag_open=:c_slash;\n  tag_close=:c_space;\n  tagstart = position (:tag_open, :p_source_rtf);\n  while (:tagstart &gt; 0) do\n  begin\n    tagfinish = position (:tag_close, :p_source_rtf, :tagstart);\n    if (:tagfinish&lt;:tagstart) then\n      tagfinish=char_length(:p_source_rtf);\n    tagfound = substring (:p_source_rtf from :tagstart for ((:tagfinish - :tagstart) + 1));\n    p_source_rtf = replace (:p_source_rtf, :tagfound, '');\n    tagstart = position (:tag_open, :p_source_rtf);\n  end\n\n  -- finaliza\n  result_text = trim(:p_source_rtf);\n\n  return :result_text;\nEND<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Modo de usar<\/h2>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;sql&quot;,&quot;mime&quot;:&quot;text\/x-sql&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;TrpContentRestriction&quot;:{&quot;restriction_type&quot;:&quot;exclude&quot;,&quot;selected_languages&quot;:[],&quot;panel_open&quot;:true},&quot;language&quot;:&quot;SQL&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;sql&quot;}\">texto_puro=GET_UNRTF(string_rtf); <\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Vale esclarecer que opera\u00e7\u00f5es com blobs n\u00e3o s\u00e3o perform\u00e1ticas, extrair um plain-text de um blob RTF leva tempo e se voc\u00ea precisa fazer isso num grupo de registros em loop para procurar um CPF, CNPJ ou algo do tipo talvez esteja fazendo isso do jeito errado. Situa\u00e7\u00f5es que requerem m\u00e1xima performance, talvez seja melhor pagar um pre\u00e7o maior e adicionar mais um campo para armazenar a mesma vers\u00e3o do conte\u00fado RTF por\u00e9m plain-text, \u00e9 certo que isso quase dobrar\u00e1 o armazenamento, por\u00e9m em contrapartida ter\u00e1 uma vers\u00e3o RTF analis\u00e1vel j\u00e1 pronta e obter\u00e1 a melhor performance poss\u00edvel para suas buscas ou analises. Claro que ao usar essa solu\u00e7\u00e3o, analise se extrair\u00e1 o plain-text no lado cliente ou usar essa SQL Function no lado servidor, escolha a op\u00e7\u00e3o que no seu contexto se encaixar melhor.<\/p>","protected":false},"excerpt":{"rendered":"<p>Qualquer que seja a linguagem de programa\u00e7\u00e3o, para ela ser considerada boa suficiente precisa ter os comandos necess\u00e1rios de estrutura de loop, condicionantes, vari\u00e1veis e alguns outros elementos. O mesmo vale para linguagens procedurais SQL(psql daqui em diante). Talvez um excelente exemplo para provar o valor da linguagem psql do banco de dados FirebirdSQL seja [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"parent":1109,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"templates\/template-full-width.php","meta":{"footnotes":""},"class_list":["post-1431","page","type-page","status-publish","hentry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1431","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/comments?post=1431"}],"version-history":[{"count":23,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1431\/revisions"}],"predecessor-version":[{"id":2125,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1431\/revisions\/2125"}],"up":[{"embeddable":true,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1109"}],"wp:attachment":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/media?parent=1431"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}