3 votes

Comment lire un bloc de données à partir d'un fichier (au lieu d'un fichier entier) en Swift

Disons que j'ai un fichier de 8 octets contenant uniquement des caractères ASCII : brownfox .

Au lieu de charger le fichier entier et de travailler dessus, je veux charger un bloc de 2 octets. [UInt8] et faire des opérations sur des blocs de 2 octets, donc les opérations sont les suivantes :

  1. charge br du fichier (et no l'ensemble du fichier)
  2. effectuer des actions sur les données, par exemple inverser pour rb
  3. enregistrer la sortie dans un autre fichier
  4. répéter pour : ow nf ox

Le raisonnement derrière cela : De cette façon, si je traite un fichier de 1 Go de texte, je n'ai pas besoin d'avoir 1 Go de RAM libre (ou 2 Go pour les fichiers d'entrée et de sortie).

Cette méthode de traitement des fichiers est importante à mes yeux pour le cryptage et l'envoi vers des solutions en nuage.

J'utilise cette extension :

extension Data {
    /**
     Consumes the specified input stream, creating a new Data object
     with its content.
     - Parameter reading: The input stream to read data from.
     - Note: Closes the specified stream.
     */
    init(reading input: InputStream) {
        self.init()
        input.open()

        let bufferSize = 1024
        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
        while input.hasBytesAvailable {
            let read = input.read(buffer, maxLength: bufferSize)
            self.append(buffer, count: read)
        }
        buffer.deallocate()

        input.close()
    }

    /**
     Consumes the specified input stream for up to `byteCount` bytes,
     creating a new Data object with its content.
     - Parameter reading: The input stream to read data from.
     - Parameter byteCount: The maximum number of bytes to read from `reading`.
     - Note: Does _not_ close the specified stream.
     */
    init(reading input: InputStream, for byteCount: Int) {
        self.init()
        input.open()

        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: byteCount)
        let read = input.read(buffer, maxLength: byteCount)
        self.append(buffer, count: read)
        buffer.deallocate()
    }
}

Pero init(reading input: InputStream, for byteCount: Int) va toujours du premier octet. Comment puis-je lire par exemple du 16ème au 20ème octet ?

Documentation sur InputStream.read(_:maxLength:)

À partir de l'index de lecture actuel, prendre jusqu'au nombre d'octets spécifié dans le second paramètre et les place dans le tampon fourni par le client (premier paramètre). tampon fourni par le client (premier paramètre). Le tampon doit être de type taille spécifiée par le second paramètre. Retourne le nombre réel d'octets d'octets placés dans le tampon ; s'il ne reste rien dans le flux, retourne 0. Réinitialise l'index dans le flux pour la prochaine opération de lecture.

Que puis-je faire pour no réinitialiser l'index, et obtenir l'opération suivante à l'endroit où la précédente s'est terminée ?

2voto

rmaddy Points 79279

Utilisez FileHandle . Vous pouvez ouvrir la poignée de fichier pour la lecture. Ensuite, utilisez seek(toFileOffset:) pour définir l'endroit à partir duquel vous souhaitez lire. Utilisez ensuite readData(ofLength:) pour obtenir Data . Veillez à fermer la poignée du fichier lorsque vous avez terminé.

1voto

vol Points 944

La solution de rmaddy a fonctionné !

Voici un extrait très sommaire pour tous ceux qui sont venus ici avec le même problème. Ce n'est pas une réponse exacte, mais cela montre tout ce qui doit être fait :)

    func loadInBlocks(path: String) -> [Data] {
        var blocks = [Data]()
        let correctPath = //path to file
        let fileHandle = FileHandle(forReadingAtPath: correctPath)

        let dataFromFirstByteTo4thByte = fileHandle!.readData(ofLength: 4)
        blocks.append(dataFromFirstByteTo4thByte)
        fileHandle?.seek(toFileOffset: 4)
        let dataFrom5thByteTo8thByte = fileHandle!.readData(ofLength: 4)
        blocks.append(dataFrom5thByteTo8thByte)
        fileHandle?.closeFile()

        return blocks
    }

et l'utilisation réelle :

    func loadBlock(number: Int, withBlockSize size: Int, path: String) throws -> Data {
        let correctPath = path.replacingOccurrences(of: "file://", with: "").replacingOccurrences(of: "%20", with: " ")

        guard let fileHandle = FileHandle(forReadingAtPath: correctPath) else { throw NSError() }

        let bytesOffset = UInt64((number-1) * size)
        fileHandle.seek(toFileOffset: bytesOffset)
        let data = fileHandle.readData(ofLength: size)
        fileHandle.closeFile()
        return data
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X