Extraindo e validando nomes de uma string no padrão Camel Case

Bom Dia Pessoal,

No mundo da programação é bem comum escrevermos nomes na notação Camel Case já que quase todas as linguagens de programação evitam trabalhar com espaços nas definições de nome. Para quem não entendeu nada da frase anterior, a prática Camel Case escreve vários nomes (abreviados ou não) em uma única string. Normalmente os nomes participantes iniciam-se com letras maiúsculas e as demais letras são minúsculas. Tomemos por exemplo as palavras número, cartão e crédito. Essas palavras podem ser combinadas para formar uma variável ou uma coluna em um banco de dados. Possivelmente o nome sugerido será NumeroCartaoCredito. Podemos visualizar que as três palavras (número, cartão e crédito) estão juntas em uma única string (NumeroCartaoCredito). Visualmente é possível perceber que essa string é composta por essas três palavras e ao visualizá-la já se tem idéia do que ela significa.

Uma das boas práticas de administração de dados é a utilização de um padrão de nomenclatura. Normalmente a presença desse padrão representa uma linguagem comum que auxilia os administradores de dados, desenvolvedores, analistas e outros profissionais que irão criar e manter esses modelos de dados. De forma análoga, o mesmo princípio é utilizado para declaração de variáveis nos códigos a serem utilizados pelos softwares desenvolvidos dentro da organização. Para evitar colunas e variáveis com nomes muito grandes é comum abreviar esses nomes. Em um padrão hipotético, as palavras número, cartão e crédito poderiam ser substituídas pelos acrônimos Num, Cart e Cred e a string NumeroCartaoCredito consequentemente seria NumCartCred.

Se existe um padrão de nomenclatura, naturalmente que suas definições serão armazenadas em algum local, pois, será necessário conferir o padrão na hora de criar e manter as colunas e variáveis. O script a seguir cria uma tabela com alguns acrônimos e suas respectivas palavras chave.

— Cria uma tabela de Definições
CREATE TABLE Definicoes (
    DefID INT NOT NULL IDENTITY(1,1),
    DefAcronimo VARCHAR(100) NOT NULL,
    DefPalavra VARCHAR(200) NOT NULL)

— Insere alguns registros
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Num’,‘Número’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Cart’,‘Cartão’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Cred’,‘Crédito’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Deb’,‘Débito’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Cli’,‘Cliente’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Val’,‘Valor’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Par’,‘Parcela’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Venc’,‘Vencimento’)
INSERT INTO Definicoes (DefAcronimo, DefPalavra) VALUES (‘Dat’,‘Data’)

Agora que temos alguns acrônimos definidos, seria fácil montar alguns nomes e identificar seus respectivos significados:

Nome Significado
NumCartCred Número Cartão Crédito
ValDeb Valor Débito
ValParCli Valor Parcela Cliente
DatVencParc Data Vencimento Parcela
NumParCli Número Parcela Cliente

A idéia geral já está posta, mas como fazer para que a partir de um string se extraia o significado ? Se os nomes fossem delimitados por algum caractér como ; ou ainda | seria bem mais fácil. Bastaria utilizar algumas técnicas que já apresentei em artigos anteriores relacionados a Arrays (Parte I, II, III). Entretanto esse não parece ser o caso. A única regra é que as palavras se iniciam com uma letra maiúscula. Se tivéssemos que escolher um delimitador seria uma letra maiúscula, mas isso não representa um único caractér.

As letras maiúsculas no SQL Server iniciam-se na posição ASCII 65 representando a letra A e finalizam na posição ASCII 90 representando a letra Z. Todas as letras maiúsculas concetram-se nesse intervalo. O script abaixo demonstra esse intervalo

SELECT ASCII(‘A’) As Inicio, ASCII(‘Z’) As Fim
SELECT CHAR(65) As [65], CHAR(68) As [68], CHAR(84) As [84], CHAR(90) As [90]

Início Fim
65 90

65 68 84 90
A D T Z

No artigo Criando uma tabela com uma seqüência de números – Parte II demonstrei como gerar uma tabela de números utilizando as CTEs. Com essa mesma técnica é possível percorrer uma string é possível identificar exatamente quais são as posições que obedecem ao RANGE ASCII 65 – 90 e portanto serão maiúsculas. Algumas pessoas como o Paulo e o Thiago já fizeram algo semelhante e possivelmente seus respectivos blogs tem boas referências sobre CTEs (recomendo uma leitura posterior).

DECLARE @Nome VARCHAR(100)
SET @Nome = ‘NumCartCred’

;WITH Nums As (
    SELECT 1 As Num
    UNION ALL
    SELECT Num + 1 FROM Nums
    WHERE Num + 1 <= LEN(@Nome))

SELECT
    SUBSTRING(@Nome,Num,1) As Caracter,
    CASE WHEN ASCII(SUBSTRING(@Nome,Num,1))
        BETWEEN 65 AND 90 THEN ‘Sim’ ELSE ‘Não’ END As Maiuscula,
    Num As Posicao

FROM Nums

Caractér Maiúscula ? Posição
N Sim 01
u Não 02
m Não 03
C Sim 04
a Não 05
r Não 06
t Não 07
C Sim 08
r Não 09
e Não 10
d Não 11

Com essa tabela, podemos ver claramente quais as posições que representam letra maiúsculas (1, 4 e 8) e letras minúsculas. O próximo passo seria ligar as posições das letras maiúsculas para poder montar as palavras. O script a seguir é uma aplicação da técnica utilizada no artigo

DECLARE @Nome VARCHAR(100)
SET @Nome = ‘NumCartCred’

;WITH Nums As (
    SELECT 1 As Num
    UNION ALL
    SELECT Num + 1 FROM Nums
    WHERE Num + 1 <= LEN(@Nome)),

Analise As (

SELECT
    SUBSTRING(@Nome,Num,1) As Caracter,
    Num As Posicao
FROM Nums
WHERE ASCII(SUBSTRING(@Nome,Num,1)) BETWEEN 65 AND 90)

SELECT Caracter, Posicao As Inicio,
    ISNULL((SELECT MIN(Posicao) FROM Analise As TInt
    WHERE TInt.Posicao > TOut.Posicao),LEN(@Nome)) As Fim
FROM Analise As TOut

Caractér Início Fim
C 01 04
N 04 08
N 08 11

Agora que os intervalos de início e fim de cada palavra são conhecidos, fica muito fácil montar as palavras (um pequeno ajuste na posição fim da última palavra):

DECLARE @Nome VARCHAR(100)
SET @Nome = ‘NumCartCred’

;WITH Nums As (
    SELECT 1 As Num
    UNION ALL
    SELECT Num + 1 FROM Nums
    WHERE Num + 1 <= LEN(@Nome)),

Analise As (

SELECT
    Num As Posicao
FROM Nums
WHERE ASCII(SUBSTRING(@Nome,Num,1)) BETWEEN 65 AND 90),

Palavras As (

SELECT Posicao As Inicio,
    ISNULL((SELECT MIN(Posicao) FROM Analise As TInt
    WHERE TInt.Posicao > TOut.Posicao),LEN(@Nome) + 1) As Fim
FROM Analise As TOut)

SELECT SUBSTRING(@Nome, Inicio, Fim – Inicio) As Palavra, Inicio, Fim
FROM Palavras

Palavra Início Fim
Num 1 4
Cart 4 8
Cred 8 12

O próximo passo é encapsular esse processo em uma function para reuso:

— Criação da função
CREATE FUNCTION dbo.FnExtraiPalavras (@Nome VARCHAR(100))
RETURNS TABLE
RETURN
(

WITH Nums As (
    SELECT 1 As Num
    UNION ALL
    SELECT Num + 1 FROM Nums
    WHERE Num + 1 <= LEN(@Nome)),

Analise As (

SELECT
    Num As Posicao
FROM Nums
WHERE ASCII(SUBSTRING(@Nome,Num,1)) BETWEEN 65 AND 90),

Palavras As (

SELECT Posicao As Inicio,
    ISNULL((SELECT MIN(Posicao) FROM Analise As TInt
    WHERE TInt.Posicao > TOut.Posicao),LEN(@Nome) + 1) As Fim
FROM Analise As TOut)

SELECT SUBSTRING(@Nome, Inicio, Fim – Inicio) As Palavra, Inicio, Fim
FROM Palavras)

— Uso da função
SELECT Palavra FROM dbo.FnExtraiPalavras(‘DatVencParc’)

Palavra
Dat
Venc
Parc

Um Join com a tabela de definições pode inclusive dar o significado de cada palavra encontrada em um nome em particular:

— Mostra o significado das palavras em um nome específico
DECLARE @Nome VARCHAR(100)
SET @Nome = ‘ValParCli’

SELECT DefAcronimo As Acronimo, DefPalavra As Palavra
FROM Definicoes As Def
INNER JOIN dbo.FnExtraiPalavras(@Nome) As Pal ON Def.DefAcronimo = Pal.Palavra

Acrônimo Palavra
Val Valor
Par Parcela
Cli Cliente

Embora a função seja capaz de extrair as palavras de um string, o próximo passo é verificar a validade dessas palavras, ou seja, é necessário que cada palavra extraída possua uma definição previamente cadastrada. A seguir mostro como fazer isso com outra função:

— Cria uma função de validação
CREATE FUNCTION dbo.ValidaNome (@Nome VARCHAR(100))
RETURNS INT
AS
BEGIN

    — Declara uma variável de retorno
    DECLARE @Retorno INT
    SET @Retorno = 1

   — Se houver alguma palavra não cadastrada
    IF EXISTS (SELECT * FROM dbo.FnExtraiPalavras(@Nome) As Pal
        WHERE NOT EXISTS (SELECT * FROM Definicoes As Def
            WHERE Pal.Palavra = Def.DefAcronimo))
    SET @Retorno = 0
    RETURN (@Retorno)
END

— Testa a função
SELECT
    dbo.ValidaNome(‘NumParCli’) As NumParCli,
    dbo.ValidaNome(‘NumDebPar’) As NumDebPar,
    dbo.ValidaNome(‘NumIdenCli’) As NumIdenCli

NumParCli NumDebPar NumIdenCli
1 1 0

Para os nomes "NumParCli" e "NumDebPar" a função retornou 1 o que mostra que ambos são válidos já que todas as palavras constituintes estão previamente cadastradas nas definições. O nome "NumIdenCli" ao ser submetido para análise retornou 0 uma vez que uma ou mais palavras não estão cadastradas nas definições (no caso a palavra Iden).

[ ]s,

Gustavo

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s