Guia de E/S no Groovy

Guia de E / S no Groovy

1. Introdução

Embora emGroovy possamos trabalhar com I / O assim como fazemos em Java,Groovy expands on Java’s I/O functionality with a number of helper methods.

Neste tutorial, veremos como ler e gravar arquivos, percorrer sistemas de arquivos e serializar dados e objetos por meio dos métodos de extensãoFile do Groovy.

Onde aplicável, estaremos vinculando nossos artigos Java relevantes para uma comparação fácil com o equivalente em Java.

2. Lendo arquivos

Groovy adiciona funcionalidade conveniente parareading files na forma de métodoseachLine, métodos para obterBufferedReaders eInputStreams e maneiras de obter todos os dados do arquivo com uma linha de código .

Java 7 e Java 8 têm suporte semelhante parareading files in Java.

2.1. Lendo comeachLine

Ao lidar com arquivos de texto, geralmente precisamos ler cada linha e processá-la. Groovy provides a convenient extension to java.io.File with the eachLine method:

def lines = []

new File('src/main/resources/ioInput.txt').eachLine { line ->
    lines.add(line)
}

O fechamento fornecido paraeachLine também tem um número de linha opcional útil. Vamos usar o número da linha para obter apenas linhas específicas de um arquivo:

def lineNoRange = 2..4
def lines = []

new File('src/main/resources/ioInput.txt').eachLine { line, lineNo ->
    if (lineNoRange.contains(lineNo)) {
        lines.add(line)
    }
}

By default, the line numbering starts at one. Podemos fornecer um valor a ser usado como o número da primeira linha, passando-o como o primeiro parâmetro para o métodoeachLine.

Vamos começar nossos números de linha em zero:

new File('src/main/resources/ioInput.txt').eachLine(0, { line, lineNo ->
    if (lineNoRange.contains(lineNo)) {
        lines.add(line)
    }
})

Se uma exceção for lançada emeachLine, Groovy makes sure the file resource gets closed. Muito parecido comtry-with-resources outry-finally em Java.

2.2. Lendo com o Reader

Também podemos obter facilmente umBufferedReader de um objetoFile do Groovy. Podemos usarwithReader para obter umBufferedReader para o objeto de arquivo e passá-lo para um encerramento:

def actualCount = 0
new File('src/main/resources/ioInput.txt').withReader { reader ->
    while(reader.readLine()) {
        actualCount++
    }
}

Como acontece comeachLine, o métodowithReader fechará automaticamente o recurso quando uma exceção for lançada.

Às vezes, podemos querer ter o objetoBufferedReader disponível. Por exemplo, podemos planejar chamar um método que use um como parâmetro. Podemos usar o métodonewReader para isso:

def outputPath = 'src/main/resources/ioOut.txt'
def reader = new File('src/main/resources/ioInput.txt').newReader()
new File(outputPath).append(reader)
reader.close()

Ao contrário dos outros métodos que vimos até agora,we’re responsible for closing the BufferedReader resource when we acquire a BufferedReader this way.

2.3. Lendo comInputStreams

Semelhante awithReader enewReader,Groovy also provides methods for easily working with InputStreams. Embora possamos ler texto comInputStreams e o Groovy até adicione funcionalidade para ele,InputStreams são mais comumente usados ​​para dados binários.

Vamos usarwithInputStream para passarInputStream para um fechamento e ler os bytes:

byte[] data = []
new File("src/main/resources/binaryExample.jpg").withInputStream { stream ->
    data = stream.getBytes()
}

Se precisarmos ter o objetoInputStream, podemos obter um usandonewInputStream:

def outputPath = 'src/main/resources/binaryOut.jpg'
def is = new File('src/main/resources/binaryExample.jpg').newInputStream()
new File(outputPath).append(is)
is.close()

Assim como acontece comBufferedReader, precisamos fechar nosso recursoInputStream quando usamosnewInputStream,, mas não quando usamoswithInputStream.

2.4. Lendo outras maneiras

Vamos terminar o assunto da leitura examinando alguns métodos que o Groovy possui para obter todos os dados do arquivo em uma instrução.

Se quisermos as linhas de nosso arquivo emList, podemos usarcollect com um iteradorit passado para o encerramento:

def actualList = new File('src/main/resources/ioInput.txt').collect {it}

Para colocar as linhas do nosso arquivo em uma matriz deStrings, podemos usaras String[]:

def actualArray = new File('src/main/resources/ioInput.txt') as String[]

Para arquivos curtos, podemos obter todo o conteúdo emString usandotext:

def actualString = new File('src/main/resources/ioInput.txt').text

E ao trabalhar com arquivos binários, há o métodobytes:

def contents = new File('src/main/resources/binaryExample.jpg').bytes

3. Escrevendo arquivos

Antes de começarmoswriting to files, vamos configurar o texto que iremos gerar:

def outputLines = [
    'Line one of output example',
    'Line two of output example',
    'Line three of output example'
]

3.1. Escrevendo com o Writer

Tal como acontece com a leitura de um arquivo,we can also easily get a BufferedWriter out of a File object.

Vamos usarwithWriter para obter umBufferedWritere passá-lo para um fechamento:

def outputFileName = 'src/main/resources/ioOutput.txt'
new File(outputFileName).withWriter { writer ->
    outputLines.each { line ->
        writer.writeLine line
    }
}

UsarwithReader fechará o recurso caso ocorra uma exceção.

O Groovy também possui um método para obter o objetoBufferedWriter. Vamos obter umBufferedWriter usandonewWriter:

def outputFileName = 'src/main/resources/ioOutput.txt'
def writer = new File(outputFileName).newWriter()
outputLines.forEach {line ->
    writer.writeLine line
}
writer.flush()
writer.close()

Somos responsáveis ​​por liberar e fechar nosso objetoBufferedWriter quando usamosnewWriter.

3.2. Escrevendo com fluxos de saída

Se estivermos gravando dados binários,we can get an OutputStream using either withOutputStream or newOutputStream.

Vamos escrever alguns bytes em um arquivo usandowithOutputStream:

byte[] outBytes = [44, 88, 22]
new File(outputFileName).withOutputStream { stream ->
    stream.write(outBytes)
}

Vamos pegar um objetoOutputStream comnewOutputStreame usá-lo para escrever alguns bytes:

byte[] outBytes = [44, 88, 22]
def os = new File(outputFileName).newOutputStream()
os.write(outBytes)
os.close()

Da mesma forma queInputStream,BufferedReader eBufferedWriter, somos responsáveis ​​por fechar oOutputStream nós mesmos quando usamosnewOutputStream.

3.3. Escrevendo com o operador <<

Como escrever texto em arquivos é muito comum, o operador<< fornece esse recurso diretamente.

Vamos usar o operador<< para escrever algumas linhas simples de texto:

def ln = System.getProperty('line.separator')
def outputFileName = 'src/main/resources/ioOutput.txt'
new File(outputFileName) << "Line one of output example${ln}" +
  "Line two of output example${ln}Line three of output example"

3.4. Gravando dados binários com bytes

Vimos anteriormente no artigo que podemos obter todos os bytes de um arquivo binário simplesmente acessando o campobytes.

Vamos escrever dados binários da mesma maneira:

def outputFileName = 'src/main/resources/ioBinaryOutput.bin'
def outputFile = new File(outputFileName)
byte[] outBytes = [44, 88, 22]
outputFile.bytes = outBytes

4. Atravessando árvores de arquivos

Groovy also provides us with easy ways to work with file trees. Nesta seção, vamos fazer isso comeachFile,eachDir e suas variantes e o métodotraverse.

4.1. Listando arquivos comeachFile

Vamos listar todos os arquivos e diretórios em um diretório usandoeachFile:

new File('src/main/resources').eachFile { file ->
    println file.name
}

Outro cenário comum ao trabalhar com arquivos é a necessidade de filtrar os arquivos com base no nome do arquivo. Vamos listar apenas os arquivos que começam com “io” e terminam em “.txt” usandoeachFileMatche uma expressão regular:

new File('src/main/resources').eachFileMatch(~/io.*\.txt/) { file ->
    println file.name
}

The eachFile and eachFileMatch methods only list the contents of the top-level directory. Groovy também nos permite restringir o que os métodoseachFile retornam passando umFileType para os métodos. As opções sãoANY,FILES eDIRECTORIES.

Vamos listar recursivamente todos os arquivos usandoeachFileRecursee fornecendoFileType deFILES:

new File('src/main').eachFileRecurse(FileType.FILES) { file ->
    println "$file.parent $file.name"
}

Os métodoseachFile lançam umIllegalArgumentException se fornecermos a eles um caminho para um arquivo em vez de um diretório.

Groovy also provides the eachDir methods for working with only directories. Podemos usareachDir e suas variantes para realizar a mesma coisa que usareachFile comFileType deDIRECTORIES.

Vamos listar recursivamente os diretórios comeachFileRecurse:

new File('src/main').eachFileRecurse(FileType.DIRECTORIES) { file ->
    println "$file.parent $file.name"
}

Agora, vamos fazer a mesma coisa comeachDirRecurse:

new File('src/main').eachDirRecurse { dir ->
    println "$dir.parent $dir.name"
}

4.2. Listando arquivos com Traverse

For more complicated directory traversal use cases, we can use the traverse method. Funciona de forma semelhante aeachFileRecurse, mas fornece a capacidade de retornar objetosFileVisitResult para controlar o processamento.

Vamos usartraverse em nosso diretóriosrc/main e pular o processamento da árvore no diretóriogroovy:

new File('src/main').traverse { file ->
   if (file.directory && file.name == 'groovy') {
        FileVisitResult.SKIP_SUBTREE
    } else {
        println "$file.parent - $file.name"
    }
}

5. Trabalhando com dados e objetos

5.1. Primitivas de serialização

Em Java, podemos usarDataInputStreameDataOutputStream aserialize primitive data fields. Groovy adiciona expansões úteis aqui também.

Vamos configurar alguns dados primitivos:

String message = 'This is a serialized string'
int length = message.length()
boolean valid = true

Agora, vamos serializar nossos dados em um arquivo usandowithDataOutputStream:

new File('src/main/resources/ioData.txt').withDataOutputStream { out ->
    out.writeUTF(message)
    out.writeInt(length)
    out.writeBoolean(valid)
}

E leia novamente usandowithDataInputStream:

String loadedMessage = ""
int loadedLength
boolean loadedValid

new File('src/main/resources/ioData.txt').withDataInputStream { is ->
    loadedMessage = is.readUTF()
    loadedLength = is.readInt()
    loadedValid = is.readBoolean()
}

Semelhante aos outros métodoswith*,withDataOutputStreamewithDataInputStream passam o fluxo para o fechamento e garantem que ele seja fechado corretamente.

5.2. Serializando objetos

Groovy also builds upon Java’s ObjectInputStream and ObjectOutputStream to allow us to easily serialize objects that implement Serializable.

Vamos primeiro definir uma classe que implementaSerializable:

class Task implements Serializable {
    String description
    Date startDate
    Date dueDate
    int status
}

Agora vamos criar uma instância deTask que podemos serializar em um arquivo:

Task task = new Task(description:'Take out the trash', startDate:new Date(), status:0)

Com nosso objetoTask em mãos, vamos serializá-lo em um arquivo usandowithObjectOutputStream:

new File('src/main/resources/ioSerializedObject.txt').withObjectOutputStream { out ->
    out.writeObject(task)
}

Finalmente, vamos ler nossoTask de volta ao usarwithObjectInputStream:

Task taskRead

new File('src/main/resources/ioSerializedObject.txt').withObjectInputStream { is ->
    taskRead = is.readObject()
}

Os métodos que usamos,withObjectOutputStreamewithObjectInputStream, passam o fluxo para um fechamento e tratam do fechamento dos recursos de forma adequada, assim como visto com os outros métodoswith*.

6. Conclusão

Neste artigo, exploramos a funcionalidade que o Groovy adiciona às classes de E / S de arquivos Java existentes. Usamos essa funcionalidade para ler e gravar arquivos, trabalhar com estruturas de diretório e serializar dados e objetos.

Tocamos apenas em alguns dos métodos auxiliares, por isso vale a pena pesquisarGroovy’s documentation para ver o que mais ele adiciona à funcionalidade de E / S do Java.

O código de exemplo está disponívelover on GitHub.