A utilização e manipulação de ficheiros de texto (*. txt ou outra extensão) é muito comum embora existam bastantes alternativas disponíveis para guardar informação. No entanto, muitas vezes não é uma questão de alternativa (usar ou não usar) mas sim uma necessidade.
O .NET Framework 2.0 introduziu novos métodos que possibilitam um fácil e rápido acesso. Os métodos mais utilizados e mais eficazes para ler e escrever em ficheiros de texto são os namespaces IO (através da classe File, principalmente ReadAllLines, WriteAllText e AppendAllText e o StreamReader, StreamWriter) e o My (através da classe My.Computer.FileSystem, principalmente ReadAllText e WriteAllText).
Obviamente que existem diferenças entre eles, mas as principais diferenças são que o StreamReader e StreamWriter lê/escreve linha a linha e os outros métodos lêem/escrevem tudo de uma só vez, ou seja, utilizam mais recursos da aplicação. Por exemplo o StreamReader lê linha a linha enquanto o método ReadAllLines lê toda a informação para uma variável (array).
É claro que isto apenas se consegue notar em grande número de informação, mas para verem as principais diferenças e desempenhos vejam a seguinte tabela com os tempos de acesso em milisegundos.
Tempo Médio de Leitura
| Método/Nº Linhas | 10.000 | 100.000 | 1.000.000 |
| IO.File.ReadAllLines | 4,0 | 71,2 | 957,1 |
| IO.StreamReader | 2,6 | 33,9 | 313,1 |
| My.Computer.FileSystem.ReadAllText | 6,7 | 60,5 | 991,2 |
Tempo Médio de Leitura
| Método/Nº Linhas | 10.000 | 100.000 | 1.000.000 |
| IO.File.WriteAllText | 15,1 | 123,8 | 1.426,4 |
| IO.StreamWriter | 13,8 | 115,5 | 1.922,8 |
| My.Computer.FileSystem.WriteAllText | 15,3 | 116,8 | 1.389,1 |
Nota: Tempo em milisegundos com a média de 10 leitura e texto por linha de 13 caracteres
Para mostrar um pouco a utilização dos diferentes métodos serão mostrados dois exemplos da leitura e escrita. Como o IO.File e o My.Computer.FileSystem são bastante semelhantes, onde a principal diferença é que a classe IO.File tem um método que lê todas as linhas sem necessitar de utilizar uma função de Split() para as colocar num array, irá ser mostrado apenas este. O My.Computer.FileSystem tem ainda um parametro adicionar que permite adicionar, ou não, texto a um arquivo já existente (append).
Exemplo de leitura de um ficheiro através do classe IO.File (ReadAllLines)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Guarda num Array linha a linha
Dim lines() As String = IO.File.ReadAllLines(fileName)
' Ciclo que irá mostrar linha a linha
For Each line As String In lines
Debug.WriteLine(line.ToString)
total += 1
Next
' Mostra o total de linha lidas
Debug.WriteLine("Total de linhas:" & total.ToString)
Exemplo de escrita de um ficheiro através do classe IO.File (WriteAllText)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Cria um novo StringBuilder
Dim sb As New System.Text.StringBuilder
' Ciclo que irá inserir construir o ficheiro a gravar
For x As Integer = 0 To 9999
' Cria uma linha com o formato "Linha nº 0001"
sb.AppendLine("Linha nº " & x.ToString.PadLeft(4, "0"))
total += 1
Next
' Grava o ficheiro no disco
IO.File.WriteAllText(fileName, sb.ToString)
' Mostra o resultado
Debug.WriteLine(String.Format("Ficheiro {0} com {1} registos", fileName, total))
Exemplo de leitura de um ficheiro através do classe IO (StreamReader)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Ciclo que irá mostrar linha a linha
Using reader As New IO.StreamReader(fileName)
' Ciclo até ao fim do ficheiro
While Not reader.EndOfStream()
' Mostra o resultado linha a linha
Dim input As String = reader.ReadLine()
Debug.WriteLine(input)
total += 1
End While
End Using
' Mostra o total de linha lidas
Debug.WriteLine("Total de linhas:" & total.ToString)
Exemplo de escrita de um ficheiro através do classe IO (StreamWriter)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Cria um nova instância de um StreamWriter
Using writer As New IO.StreamWriter(fileName)
' Inicia o ciclo que irá gravar no ficheiro
For x As Integer = 0 To 9999
writer.WriteLine("Linha nº " & x.ToString.PadLeft(4, "0"))
total += 1
Next
End Using
' Mostra o resultado
Debug.WriteLine(String.Format("Ficheiro {0} com {1} registos", fileName, total))
Estes são pequenos exemplos da estrutura tipo, embora existam ainda em cada classe, uma serie de métodos que podem ser utilizados. Pelos exemplos mostrados e pelo desempenho de cada um, deverá ser utilizado aquele que mais se ajustar a cada situação. Apenas em ficheiros grandes deverá haver um mais cuidado na escolha do método a utilizar.
Todos utilizam por defeito uma codificação de UTF-8 mas por vezes é necessário ajustar para certos ficheiros. A opção System.Text.Encoding.Default é muitas vezes a melhor solução para problemas de codificação.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
DateTimePicker – Sem Selecção (nulo)
O DateTimePicker é um excelente controlo
para seleccionar e mostrar datas ou horas. Não necessita de validação, é fácil de
seleccionar ou incrementar, permite uma introdução directa e é visualmente bastante
agradável. Mas, e como não há regra sem excepção, tem um grande defeito: não tem
uma possibilidade directa simples de indicar que não existe escolha, ou seja, colocar
uma selecção a nulo.
Quando estamos a inserir ou a mostrar
dados, o controlo DateTimePicker indica sempre uma data/hora. Isto obriga a uma
adaptação por parte do programador, criando um conjunto de soluções alternativas,
para indicar que não existe nenhuma selecção. Há quem coloque uma data (tipo 01-01-1900),
há quem coloque uma checkbox para habilitar/desabilitar
o controlo e há quem simplesmente não o utilize.
A melhor solução, e é obviamente a
minha opinião, é mostrar sem nada (em branco), indicando que não tem selecção. Para
fazer isto é necessário o seguinte:
DATA
Para mostrar a data em branco é necessário
definir o controlo DateTimePicker com o formato
Custom (personalizado) e indicar um espaço em branco (“ “)
Me.DateTimePicker1.Format = DateTimePickerFormat.Custom
Me.DateTimePicker1.CustomFormat = " "
Depois, e quando o utilizador seleccionar
uma data, repomos o formato normal
Private Const EmptySpace
As String = " "
'
Detecta que foi alterado um valor(data) no DateTimePicker
Private Sub DateTimePicker1_ValueChanged(ByVal sender As
Object, ByVal
e As System.EventArgs) _
Handles DateTimePicker1.ValueChanged
' Verifica se o texto
é um espaço em branco
If DateTimePicker1.Text
= EmptySpace Then
' Define
o formato como Shot (formato DD-MM-YYYY) e dá um
' enter
para fechar (CloseUp) da janela que está aberta
Me.DateTimePicker1.Format = DateTimePickerFormat.Short
SendKeys.Send("{ENTER}")
End If
End
Sub
HORA
Como no caso anterior é necessário
definir o controlo DateTimePicker com o formato
Custom (personalizado) e indicar um espaço em branco (“ “). O problema é
que neste caso o utilizador inicia a alteração da hora/ através dos botões UpDown
uma vez que não consegue introduzir manualmente (não permite o focus uma vez que
não tem valores). Para detectarmos que o utilizador clicou com o rato num botão
de incremento/decremento é necessário fazer o override ao WndProc, uma vez que os
eventos MouseDown ou MouseUp não detectam os botões.
O melhor é criar um controlo personalizado
em que se define este processo. No exemplo o controlo tem o nome de myDateTimePicker.
Me.MyDateTimePicker1.ShowUpDown
= True
Me.MyDateTimePicker1.Format
= DateTimePickerFormat.Custom
Me.MyDateTimePicker1.CustomFormat = " ""
O código para o controlo personalizado,
fazendo o override ao WndProc é o seguinte:
Public Class
myDateTimePicker
Inherits DateTimePicker
Const EmptySpace As
String = " "
'
Constante que verifica se foi utilizado o botão esquerdo do rato
Private Const WM_LBUTTONDOWN = &H201
' Overrides ao WndProc
Protected Overrides
Sub WndProc(ByRef m
As System.Windows.Forms.Message)
' Caso seja o botão esquerdo e o texto esteja com um espaço em branco
If m.WParam = WM_LBUTTONDOWN Then
If Me.Text
= EmptySpace Then
Me.Format = DateTimePickerFormat.Time
End If
End If
MyBase.WndProc(m)
End
Sub
End
Class
Para terminar,
pode-se ainda apagar a formatação quando o utilizador pressionar a tecla DEL, nos
eventos KeyUp, KeyDown ou KeyPress.
Private Sub MyDateTimePicker1_KeyDown(ByVal sender As
Object, ByVal
e As System.Windows.Forms.KeyEventArgs) _
Handles
MyDateTimePicker1.KeyDown
' Caso a tecla seja a
DELETE
If e.KeyCode = Keys.Delete Then
Me.MyDateTimePicker1.Format = DateTimePickerFormat.Custom
Me.
MyDateTimePicker1.CustomFormat = " "
End If
End
Sub
Este é um exemplo
de como melhorar a utilização deste controlo tornando-o quase “perfeito”.
StopWatch – Medindo
o Tempo de Execução
Existem formas diferentes de construir
código de modo a executar algo. Umas mais rápidas, umas mais simples ou mesmo mais
versáteis. As operações mais demoradas estão normalmente relacionadas com a gestão
de dados (bases de dados, ficheiros, etc.) mas as pequenas optimizações em conjunto,
podem melhorar a performance da aplicação. Nos casos de duvida, em que qual é o
método mais rápido para fazer algo, o melhor mesmo é testar.
O StopWatch permite medir de uma forma
bastante simples o tempo de execução, utilizando muito pouco código. O método para
testar o tempo de execução de diferentes métodos é sempre o mesmo, ou seja:
.
' Cria-se uma nova instância
Dim stopWatch
As New Stopwatch
' Inicia-se a contagem
stopWatch.Start()
' Faz-se um ciclo com várias repetições
para verificar o tempo que demorou
' a executar. Caso o código já seja
demorado, não é necessário ciclo
For x As Integer
= 0 To 10000
' código a testar
Next
' Para-se a contagem
stopWatch.Stop()
' Mostra-se o tempo que demorou a executar
em milisegundos
Debug.WriteLine(stopWatch.ElapsedMilliseconds.ToString)
Depois repetem-se
algumas vezes, coloca-se o código de comparação a testar e comparam-se os resultados
obtidos. Pode ser necessário ajustar o número de repetições no ciclo de maneira
a diferenciar melhor os resultados.
Podem-se ainda
melhorar a visualização, especialmente em tarefas mais demoradas, utilizando o TimeSpan.
Depois de parar o Stopwatch, através do método Stop(), pode-se fazer o seguinte:
' Coloca o tempo total na
variável do tipo TimeSpan.
Dim ts As
TimeSpan = stopWatch.Elapsed
' Mostra a informação dividida
em Horas, Minutos, Segundo e Milisegundos.
Dim totalTime As
String = _
String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
Debug.WriteLine(totalTime)
O exemplo mostrar como aplicar e executar
testes de velocidade de código, com o objectivo de melhorar a performance da aplicação.
PS: Como sempre, qualquer dúvida,
comentário ou correcção ao artigo é sempre bem
vinda!
A maioria das aplicações tem algumas
opções que são importantes guardar de modo a serem utilizadas durante e em próximas
execuções. Estas configurações ou settings
são bastante utilizadas e importantes, sendo os métodos mais usados no Visual Basic
6 a criação de ficheiros INI’s e a utilização do
Registry do Windows.
Tamanho e/ou localização de uma janela,
data do último acesso, impressora utilizada, nome do último
login, são apenas alguns exemplos do que é “normal” e necessário guardar.
Pode-se obviamente também guardar esta informação numa base de dados, mas se necessitarmos
de guardar o endereço da base de dados ou o nome do servidor? É necessário recorrer
a outros métodos.
Actualmente, e embora também se utilize
também no Visual Basic 6, os ficheiros XML estão a liderar os ficheiros de
settings fazendo parte de várias classes do Visual Basic.NET o que simplifica
bastante este processo. Existe ainda um processo de criação de
settings embebido na aplicação.
Existem grandes defensores dos ficheiros
INI’s e grandes defensores de guardar a informação no
Registry do Windows, no entanto, e é a minha opinião pessoal, é que ambos
os métodos são válidos e devem ser utilizados para diferentes fins.
Ambos têm vantagens e desvantagens,
por exemplo: os ficheiros INI’s são fáceis analisar, não necessitam de acesso de
administrador para os modificar/criar, mas também são fáceis de modificar não oferecendo
por isso nenhuma segurança. Por outro lado a utilização do
Registry do Windows necessita de acesso de administrador (o que em muitos
sistemas é impensável), é mais delicada a sua utilização pois podem-se apagar/modificar
inadvertidamente outras informações importantes no
Registry e é mais difícil de analisar a informação sendo necessário recorrer
a um editor. No entanto é bastante mais seguro.
Podem ser utilizados os dois sistemas,
de acordo com a interpretação de cada programador, por exemplo, guardar informações
mais importantes (como passwords) no
Registry e informações menos importantes/relevantes
em ficheiros INI.
Ficheiros INI’s
Os ficheiros INI’s não são mais do
que ficheiros de texto em que se guardam informações devidamente agrupadas o que
permitem uma boa organização e fácil acesso através de API’s. A estrutura de um
ficheiro é:
[Secção1]
Chave1=Texto1
Chave2=Texto2
[Secção2]
Chave1=Texto1
Chave2=Texto2
Este é um exemplo simples de utilização:
Option Explicit
' Declaração de API's
Private Declare Function WritePrivateProfileString Lib
"kernel32" Alias "WritePrivateProfileStringA" _
(ByVal
lpApplicationName As String,
ByVal lpKeyName As Any, _
ByVal lpString As Any,
ByVal lpFileName As String)
As Long
Private Declare Function GetPrivateProfileString Lib
"kernel32" Alias "GetPrivateProfileStringA" _
(ByVal
lpApplicationName As String,
ByVal lpKeyName As Any, _
ByVal lpDefault As String,
ByVal lpReturnedString As String, _
ByVal nSize As Long,
ByVal lpFileName As String)
As Long
' Escreve no ficheiro
INI através de API ignorando o erro
Public Function INIWrite(sSection As String,
sKeyName As String, sNewString
As String, sINIFileName As String) As Boolean
Call
WritePrivateProfileString(sSection, sKeyName, sNewString, sINIFileName)
INIWrite = (Err.Number = 0)
End Function
' Lê no ficheiro INI
através de API ignorando o erro
Public Function INIRead(sSection As String,
sKeyName As String, sINIFileName
As String) As String
Dim
sRet As String
sRet = String(255, Chr(0))
INIRead = Left(sRet, GetPrivateProfileString(sSection,
ByVal sKeyName, "", sRet, Len(sRet), sINIFileName))
End Function
' No evento Form Load
Private Sub Form_Load()
' Escreve uma informação no ficheiro INI. Caso exista modifica
a existente,
‘ caso não exista cria uma nova chave e o respectivo texto
Call INIWrite("GERAL", "utilizador", "jpaulino", App.Path
& "\mySettings.ini")
End Sub
' Botão para mostrar
o resultado
Private Sub btnRead_Click()
Dim
result As String
' Lê a chave no ficheiro INI para uma variável
e mostra a informação
result = INIRead("GERAL", "utilizador", App.Path &
"\mySettings.ini")
MsgBox result, vbInformation
End Sub
O resultado no evento Form Load será
a criação de um ficheiro ini (mySettings.ini) com a seguinte estrutura:
[GERAL]
utilizador=jpaulino
Windows Registry
A utilização do Windows Registry é
ainda mais fácil, não sendo necessário a utilização de API’s. A localização onde
é guardada a informação é fixa e é a seguinte:
HKEY_CURRENT_USER
\Software
\VB and VBA Program Settings
\Applicação
\ Secção
Chave = Texto
Este é um exemplo simples de utilização:
Option Explicit
' Escreve a informação
no Registry
Private Sub bntWrite_Click()
SaveSetting "Teste", "GERAL",
"Página", "http://vbtuga.blogspot.com/"
End Sub
' Botão para mostrar
o resultado
Private Sub btnRead_Click()
Dim
result As String
result = GetSetting("Teste",
"GERAL", "Página", "")
MsgBox result, vbInformation
End Sub
Como já referido anteriormente, existem
actualmente outros métodos para guardar informação relevante à aplicação, mas estas
duas formas são ainda utilizadas. Muitos programadores de Visual Basic 6 que migraram
para Visual Basic.Net ainda utilizam estes métodos (embora não aconselhável).
Este artigo, embora bastante simples,
pretende apenas mostrar algumas alternativas fáceis e eficazes de guardar informações
de configuração da aplicação e como as utilizar de uma forma geral.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
O Microsoft Excel tem inúmeras fórmulas para diversos utilizações, muitas delas que nem sabemos da sua existência nem sabemos para que serve. Tem fórmulas para quase tudo! Mas no entanto existem operações que por serem muito específicas ou simplesmente para simplificar (de modo a não utilizar muitas fórmulas em conjunto) dava jeito serem personalizadas, ou seja, à nossa medida.
A verdade é que não é prático utilizar algo do género:
{=IF(FORMULA(<criteria>)=0;IF(MATCH(<range>;<criteria>); ....
A fórmula além de complicada de construir é também complicada de interpretar e alterar. Nestes casos o ideal é criarmos a nossa própria fórmula permitindo, além de simplificar, executar mais funcionalidades para além das já existentes e disponíveis.

Para demonstrar a criação de uma fórmula personalizada, será mostrado um exemplo de como recolher a primeira e ultima palavra de um nome completo, construindo um nome abreviado.
Nota: Esta funcionalidade poderá também ser construída através de fórmulas, mas o objectivo é mostrar a melhoria que estas fórmulas predefinidas podem trazer às folhas de cálculo.
Para adicionarmos esta função a uma folha de Excel devemos fazer o seguinte:
1 – Menu Tools – Macros – Visual Basic Editor ou ALT+F11
2 – Adicionar um novo módulo clicando com o botão direito ou através do menu Insert (Figura 1)
(Figura 1)
3 – Colocar a seguinte código no módulo criado:
' ---------------------------------------------------------------------------
' Separa uma frase e retorna o primeiro e último nome
' ---------------------------------------------------------------------------
Public Function FirstLast(ByVal str As String) As String
Dim result() As String
Dim total As Integer
' Separa para um array o valor da célula de acordo com o
' separador " " ou seja espaço
' Por exemplo na frase "isto é um teste" o resultado ficará desta forma:
'
' result(0) = isto
' result(1) = é
' result(2) = um
' result(3) = teste
'
result = Split(str, " ")
' Verifica qual o tamanho do array (o array começa em zero)
total = UBound(result)
' Caso seja zero
If total = 0 Then
If str = "" Then
' Caso esteja em branco não retorna nada
FirstLast = ""
Else
' Caso exista texto retorna esse texto
FirstLast = StrConv(str, vbProperCase)
End If
Else
' Retorna o primeiro nome e o último, com um espaço no
‘ meio utiliza ainda a função StrConv() que converte
‘ o texto (neste caso) para um formato the Proper Case
FirstLast = StrConv(result(0), vbProperCase) & Space(1) & _
StrConv(result(total), vbProperCase)
End If
End Function
4 – Fechar o Editor de VBA e na folha de cálculo utilizar a função criada
O exemplo deste pequeno artigo é mostrar que as fórmulas personalizadas, utilizando VBA, permitem melhorar e potenciar as folhas de cálculo. É mostrado um exemplo simples que pretende explicar o funcionamento geral mas também mostrar algumas instruções de código (como o caso de arrays, StrConv, Split, etc).
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!