Removendo acentos de um texto, pode ser feito melhor? [RESOLVIDO]

1. Removendo acentos de um texto, pode ser feito melhor? [RESOLVIDO]

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 14/11/2021 - 19:03h

Boa Noite a Todos!

Eu montei o algoritmo abaixo pra remover acentos e passar tudo para minúsculas, que acredito que uso no meu dia a dia!
Gostaria de saber se meu algoritimo está bom, ou se é possível fazer melhor!
Então quem tiver alguma idéia, ou crítica, gostaria de saber!

Lembrando que uso acentos!
Esse algoritimo é para meu aprendizado pessoal, logo, não é um trabalho que tenha regras, e nem pra uso profissional que também seguirá padrões!
Apenas trata-se de pequenos algoritmos cujo qual eu quero controlar e dominar completamente, motivo que nestes casos, não quero usar funções prontas porque elas não me ensinam nada!

Usei wchar porque ele trabalha com acentos, diferente do char que me forçaria a verificar 2 caracteres por vez, porém, ainda assim, vou depois montar o mesmo algoritimo com o simples char porque acho que eu apreciaria mais, ficando mais simples e ocupando menos Bytes, visto que o wchar está gastando 4 Bytes contra 1 Byte do char.
#include <locale.h>
#include <wchar.h>

int main(void) {
setlocale(LC_ALL, "");
wchar_t Text[] = L"áéíóú àèìòù âêô ãõ ç ABCaÇÁ";

for(int Ch = 0; Text[Ch] != '\0'; ++Ch) {
if(Text[Ch] >= 'A' && Text[Ch] <= 'Z') Text[Ch] = Text[Ch] + 32;
else Text[Ch] = Text[Ch];

if(Text[Ch] == L'Á' || Text[Ch] == L'À' || Text[Ch] == L'Â' || Text[Ch] == L'Ã' || Text[Ch] == L'á' || Text[Ch] == L'à' || Text[Ch] == L'â' || Text[Ch] == L'ã') Text[Ch] = L'a';
if(Text[Ch] == L'É' || Text[Ch] == L'È' || Text[Ch] == L'Ê' || Text[Ch] == L'é' || Text[Ch] == L'è' || Text[Ch] == L'ê') Text[Ch] = L'e';
if(Text[Ch] == L'Í' || Text[Ch] == L'Ì' || Text[Ch] == L'í' || Text[Ch] == L'ì') Text[Ch] = L'i';
if(Text[Ch] == L'Ó' || Text[Ch] == L'Ò' || Text[Ch] == L'Ô' || Text[Ch] == L'Õ' || Text[Ch] == L'ó' || Text[Ch] == L'ò' || Text[Ch] == L'ô' || Text[Ch] == L'õ') Text[Ch] = L'o';
if(Text[Ch] == L'Ú' || Text[Ch] == L'Ù' || Text[Ch] == L'ú' || Text[Ch] == L'ù') Text[Ch] = L'u';
if(Text[Ch] == L'ç' || Text[Ch] == L'Ç') Text[Ch] = L'c';
}
wprintf(L"%ls\n", Text);
}



  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 16/11/2021 - 10:30h

ApprenticeX escreveu:

Boa Noite a Todos!

Eu montei o algoritmo abaixo pra remover acentos e passar tudo para minúsculas, que acredito que uso no meu dia a dia!
Gostaria de saber se meu algoritimo está bom, ou se é possível fazer melhor!
Então quem tiver alguma idéia, ou crítica, gostaria de saber!


Uma coisa que eu noto é que você fez os testes de todas as letras candidatas à remoção de acentos mesmo depois de possivelmente já saber qual a letra (por exemplo: supondo que você já trocou L'Á' por L'a', você continua entrando nos cinco ifs seguintes e, pela forma como eles foram construídos, compara a letra já trocada com cada uma das letras acentuadas equivalentes a e, i, o, u e c. Assim sendo, uma primeira correção seria trocar aquele conjuntos de if por uma tripa if/else if/else if//else.
if(/* testes da letra a*/)
text[ch]=L'a';
else if(/* testes da letra e */)
text[ch]=L'e';
else if(/* testes da letra i */)
text[ch]=L'i';
else if(/* testes da letra o */)
text[ch]=L'o';
else if(/* testes da letra u */)
text[ch]=L'u';
else if(/* testes da letra c */)
text[ch]=L'c';


Mas melhor ainda do que o encadeamento usando else seria trocar por switch, pois muitos compiladores implementam esses testes de forma muito mais eficiente, com uma tabela de desvios indexada pelo valor sendo examinado, em lugar de testar cada valor individualmente e em sequência.
switch(text[ch]){
case L'A':
case L'À':
case L'Á':
case L'Â':
case L'Ã':
case L'à':
case L'á':
case L'â':
case L'ã':
text[ch]=L'a';
break;
case L'C':
case L'Ç':
case L'ç':
text[ch]=L'c';
break;
/*
… etc.
*/
}


Lembrando que uso acentos!
Esse algoritimo é para meu aprendizado pessoal, logo, não é um trabalho que tenha regras, e nem pra uso profissional que também seguirá padrões!
Apenas trata-se de pequenos algoritmos cujo qual eu quero controlar e dominar completamente, motivo que nestes casos, não quero usar funções prontas porque elas não me ensinam nada!

Usei wchar porque ele trabalha com acentos, diferente do char que me forçaria a verificar 2 caracteres por vez, porém, ainda assim, vou depois montar o mesmo algoritimo com o simples char porque acho que eu apreciaria mais, ficando mais simples e ocupando menos Bytes, visto que o wchar está gastando 4 Bytes contra 1 Byte do char.


Cuidado que o nome do tipo é wchar_t, não “wchar”.

#include <locale.h>
#include <wchar.h>

int main(void) {
setlocale(LC_ALL, "");
wchar_t Text[] = L"áéíóú àèìòù âêô ãõ ç ABCaÇÁ";

for(int Ch = 0; Text[Ch] != '\0'; ++Ch) {
if(Text[Ch] >= 'A' && Text[Ch] <= 'Z') Text[Ch] = Text[Ch] + 32;
else Text[Ch] = Text[Ch];

if(Text[Ch] == L'Á' || Text[Ch] == L'À' || Text[Ch] == L'Â' || Text[Ch] == L'Ã' || Text[Ch] == L'á' || Text[Ch] == L'à' || Text[Ch] == L'â' || Text[Ch] == L'ã') Text[Ch] = L'a';
if(Text[Ch] == L'É' || Text[Ch] == L'È' || Text[Ch] == L'Ê' || Text[Ch] == L'é' || Text[Ch] == L'è' || Text[Ch] == L'ê') Text[Ch] = L'e';
if(Text[Ch] == L'Í' || Text[Ch] == L'Ì' || Text[Ch] == L'í' || Text[Ch] == L'ì') Text[Ch] = L'i';
if(Text[Ch] == L'Ó' || Text[Ch] == L'Ò' || Text[Ch] == L'Ô' || Text[Ch] == L'Õ' || Text[Ch] == L'ó' || Text[Ch] == L'ò' || Text[Ch] == L'ô' || Text[Ch] == L'õ') Text[Ch] = L'o';
if(Text[Ch] == L'Ú' || Text[Ch] == L'Ù' || Text[Ch] == L'ú' || Text[Ch] == L'ù') Text[Ch] = L'u';
if(Text[Ch] == L'ç' || Text[Ch] == L'Ç') Text[Ch] = L'c';
}
wprintf(L"%ls\n", Text);
}


A solução mais genérica seria usar algoritmos padronizados de alguma biblioteca de internacionalização (como a ICU, por exemplo) para decomposição de caracteres com marcas diacríticas em caráter-base mais caráter diacrítico combinante (por exemplo: tomando como entrada o caráter “ã” (U+00E3), a decomposição resultaria em “a” (U+0061) mais o til combinante (U+0303, que é diferente do til individual, em U+007E)). Aí você descartaria a marca diacrítica combinante, retendo apenas o caráter-base.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)

3. Re: Removendo acentos de um texto, pode ser feito melhor? [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 15/11/2021 - 20:48h

Pra ficar mais simples de ler e entender, você pode criar outra string (vetor de char) com as vogais maiusculas e então usar for para comparar:
Exemplo:

wchar_t maiusculas[] = "ÁÀÂÉÈÊÍÌÓÔÚ";
wchar_t minusculas[] = "áàâéèêóôú";
wchar texto[] = "Onde eu ía, lÁ eu estava, mas É assim que me conheço.";;
//iterando no texto de forma mais legal
for (wchar_t *wc = texto; *wc; wc++) {
//agora verifica se é vogal maiuscula acentuada
wchat_t *v = maiusculas;
whiloe (*v && *v != *wc) v++;
if (*v == *wc)
printf("Achamos uma vogal maiuscula acentuada\n");
//faça o mesmo para o vetor minusculas
//pode até colocar uma função pra evitar duplicar código.
}

Observe, código não testado. Mas a lógica é essa e serve pra treinar ponteiros.


4. Re: Removendo acentos de um texto, pode ser feito melhor?

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 15/11/2021 - 23:00h

SamL escreveu:

Pra ficar mais simples de ler e entender, você pode criar outra string (vetor de char) com as vogais maiusculas e então usar for para comparar:
Exemplo:

wchar_t maiusculas[] = "ÁÀÂÉÈÊÍÌÓÔÚ";
wchar_t minusculas[] = "áàâéèêóôú";
wchar texto[] = "Onde eu ía, lÁ eu estava, mas É assim que me conheço.";;
//iterando no texto de forma mais legal
for (wchar_t *wc = texto; *wc; wc++) {
//agora verifica se é vogal maiuscula acentuada
wchat_t *v = maiusculas;
whiloe (*v && *v != *wc) v++;
if (*v == *wc)
printf("Achamos uma vogal maiuscula acentuada\n");
//faça o mesmo para o vetor minusculas
//pode até colocar uma função pra evitar duplicar código.
}

Observe, código não testado. Mas a lógica é essa e serve pra treinar ponteiros.

Corrigi seu código, mas ficou algumas dúvidas!
#include <locale.h>
#include <wchar.h>

int main(void) {
setlocale(LC_ALL, "");
wchar_t maiusculas[] = L"ÁÀÂÉÈÊÍÌÓÔÚ";
wchar_t texto[] = L"Onde eu ía, lÁ eu estava, mas É assim que me conheço.";

// Não entendi porque a duplicidade de *wc aqui. O 1o eu entendi, mas o 2o não! E sem ele dá Segmentation fault
for(wchar_t *v, *wc = texto; *wc; wc++, v = maiusculas) {

// Porque *v && *v ??? Não Entendi, sei que sem os 2 BUGA a resposta!
while(*v && *v != *wc) v++;

if(*v == *wc)
wprintf(L"Achamos uma vogal maiuscula acentuada: %ls\n", wc); // Como imprimo apenas a LETRA?
}
}
/* RESULT
Achamos uma vogal maiuscula acentuada: Á eu estava, mas É assim que me conheço.
Achamos uma vogal maiuscula acentuada: É assim que me conheço.
*/




5. Re: Removendo acentos de um texto, pode ser feito melhor? [RESOLVIDO]

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 16/11/2021 - 18:19h

paulo1205 escreveu:
Uma coisa que eu noto é que você fez os testes de todas as letras candidatas à remoção de acentos mesmo depois de possivelmente já saber qual a letra (por exemplo: supondo que você já trocou L'Á' por L'a', você continua entrando nos cinco ifs seguintes e, pela forma como eles foram construídos, compara a letra já trocada com cada uma das letras acentuadas equivalentes a e, i, o, u e c. Assim sendo, uma primeira correção seria trocar aquele conjuntos de if por uma tripa if/else if/else if//else.
if(/* testes da letra a*/)
text[ch]=L'a';
else if(/* testes da letra e */)
text[ch]=L'e';
else if(/* testes da letra i */)
text[ch]=L'i';
else if(/* testes da letra o */)
text[ch]=L'o';
else if(/* testes da letra u */)
text[ch]=L'u';
else if(/* testes da letra c */)
text[ch]=L'c';

As vezes me esqueço de verificar se está havendo uma verificação duplicada!

Mas melhor ainda do que o encadeamento usando else seria trocar por switch, pois muitos compiladores implementam esses testes de forma muito mais eficiente, com uma tabela de desvios indexada pelo valor sendo examinado, em lugar de testar cada valor individualmente e em sequência.
switch(text[ch]){
case L'A':
case L'À':
case L'Á':
case L'Â':
case L'Ã':
case L'à':
case L'á':
case L'â':
case L'ã':
text[ch]=L'a';
break;
case L'C':
case L'Ç':
case L'ç':
text[ch]=L'c';
break;
/*
… etc.
*/
}

Também sempre me esqueço que neste caso os caracteres são decimais também e o case funciona sim melhor neste caso!







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts