[Powered by Google Translate] [Passo a passo - Conjunto de Problemas 4] [Zamyla Chan - Harvard University] [Esta é CS50. - CS50.TV] Tudo bem. Olá, todos, e bem vindo ao Passo a passo 4. Hoje a nossa pset é Forensics. Forense é um pset muito divertido que envolve lidar com arquivos de bitmap para descobrir quem cometeu um crime. Então vamos redimensionar alguns arquivos de bitmap, então estamos indo para lidar com uma parte muito divertido chamado Recuperar, em que estamos, basicamente, entregou um cartão de memória em que alguém acidentalmente apagado todos os seus arquivos, e estamos pediu para recuperar esses arquivos. Mas, primeiro, antes de entrar no pset, eu realmente só quero felicitar todos. Estamos prestes a meio deste curso. 0 Quiz está atrás de nós, e nós estamos em pset4, assim, essencialmente, estamos a meio caminho. Percorremos um longo caminho, se você olhar para trás para sua Série de Exercícios, pset0 e pset1, para congratular-se com isso, e nós vamos entrar em algumas coisas realmente divertido. Portanto, a nossa caixa de ferramentas para este pset, de novo, em vez de correr o yum update-y sudo, somos capazes de executar apenas update50 se você estiver na versão 17,3 e acima do aparelho. Então não se esqueça de executar update50 - é muito mais fácil, alguns personagens menos - ter certeza de que você está na versão mais recente do aparelho. Especialmente é importante update50 quando começar a usar CS50 Check. Então, certifique-se que você faça isso. Para todas as seções para este pset, vamos estar lidando com entradas e saídas de arquivo, arquivo I / O. Nós vamos estar passando por uma série de programas que lidam com matrizes apontando para arquivos e coisas assim, por isso queremos ter certeza de que estamos realmente familiar e confortável lidar com a forma de entrada e saída em arquivos. No código de distribuição para este pset é um arquivo chamado copy.c, e é isso que vamos encontrar, vai ser muito útil para nós porque nós vamos acabar realmente copiar o arquivo copy.c e apenas alterando ligeiramente para ser capaz de atingir as 2 primeiras partes do conjunto de problemas. E então, como eu disse antes, estamos lidando com bitmaps, bem como JPEGs. Então, realmente compreender a estrutura de como esses arquivos são organizados, como podemos realmente traduzir os 0s e 1s em estruturas e as coisas que nós podemos realmente entender e interpretar e editar, que será realmente importante, para entrar em JPEG e arquivos de bitmap e compreender a estrutura desses. Pset4, como de costume, começa com uma seção de perguntas. Aqueles irá lidar com o arquivo de I / O e levá-lo acostumado a isso. Em seguida, parte 1 é Whodunit, em que você recebe um arquivo de bitmap que parece muito com pontos vermelhos todo. E então basicamente o que nós vamos fazer é pegar este arquivo e editá-lo apenas ligeiramente em uma versão que podemos ler. Essencialmente, uma vez que terminar, nós vamos ter o mesmo arquivo, exceto nós vamos ser capazes de ver a mensagem escondida escondida por todos os pontos vermelhos. Então Resize é um programa que, dado um arquivo e então dado o nome do arquivo que ele gera e, então, dado um número, bem como, vai realmente redimensionar esse bitmap por esse valor inteiro. Então, por fim, temos a pset recuperar. Nós recebemos um cartão de memória e tem que recuperar todas as fotos que foram apagados acidentalmente, mas, como veremos, não realmente excluídos e removidos do arquivo; Nós meio que perdida quando eles estavam no arquivo, mas vamos recuperar isso. Grande. Então vai em arquivo I / O, especificamente, estes são toda uma lista de funções que você estará usando. Você já viu um pouco o básico de fopen, fread e fwrite, mas vamos olhar mais para algum arquivo I / O funciona como fputc, em que você acabou de escrever um personagem de cada vez, para fseek, onde tipo de mover o indicador de posição do arquivo para a frente e para trás, e, em seguida, alguns outros. Mas nós falaremos sobre isto um pouco mais tarde, durante o pset. Então, primeiro, só para entrar no arquivo I / O, antes de ir para o pset, para abrir um arquivo, por exemplo, o que você tem que fazer é realmente definir um ponteiro para o arquivo. Portanto, temos um ponteiro FILE *. Neste caso, eu estou chamando-o um ponteiro porque esse vai ser o meu infile. E assim eu vou usar a função fopen e depois o nome do arquivo e, em seguida, o modo em que eu vou estar lidando com o arquivo. Portanto, não há "r", neste caso, para a leitura, "w" para a escrita, e então "a" para acrescentar. Por exemplo, quando você está lidando com um infile e tudo que você quer fazer é ler os bits e bytes armazenados lá, então você provavelmente vai querer usar "r", como seu modo. Quando você quer realmente escrever, tipo de fazer um novo arquivo, então o que nós vamos fazer é que vamos abrir o novo arquivo e usar o "w" modo de escrever. Então quando você está lendo nos arquivos, a estrutura é a seguinte. Primeiro você incluir o ponteiro para a estrutura que irá conter os bytes que você está lendo. Assim que vai ser o local final de bytes que você está lendo. Você está indo então para indicar o tamanho, gosto basicamente quantos bytes seu programa tem que ler para o arquivo, o tamanho basicamente um elemento é, e então você vai para especificar quantos elementos que você deseja ler. E então, finalmente, você tem que saber onde você está lendo, de modo que vai ser o ponteiro no. Eu codificados por cores estes porque fread também é muito semelhante à fwrite, exceto que você quer ter certeza de que você use a ordem certa, se certificar de que você está realmente gravação ou leitura do arquivo certo. Então, em seguida, como antes, se tivermos a dimensão do elemento, assim como o número de elementos, então podemos brincar um pouco aqui. Digamos que eu tenha uma estrutura cão e então eu quero ler dois cães de uma vez. O que eu poderia fazer é dizer o tamanho de um elemento que vai ser do tamanho de um cão e eu vou realmente ler dois deles. Como alternativa, o que eu poderia fazer é dizer que eu estou indo só para ler um elemento e que um elemento vai ser o tamanho de dois cães. Então, isso é análogo como você pode tipo de brincar com tamanho e número dependendo do que é mais intuitivo para você. Tudo bem. Então, agora temos de arquivos escrita. Quando você quiser escrever um arquivo, o primeiro argumento é realmente onde você está lendo. Então é isso, basicamente, os dados que você vai escrever para o arquivo, que é o ponteiro para fora na extremidade. Então, quando você está lidando com o pset, certifique-se que você não se confundir. Talvez ter o lado definições a lado. Você pode puxar as definições no manual, digitando o homem e depois fwrite, por exemplo, no terminal, ou você pode se referir a este slide e certifique-se que você está usando o caminho certo. Então, novamente, para fwrite, quando você tem um arquivo que você quer escrever para, que vai ser o último argumento, e que vai ser um ponteiro para o arquivo. Então é assim que lidar com a escrita, talvez vários bytes de cada vez, mas dizer que você quer apenas escrever em apenas um personagem único. Como veremos mais adiante neste exemplo, nos bitmaps que vamos ter de usar isso. É quando podemos usar fputc, essencialmente, apenas colocando um personagem de cada vez, chr, para o ponteiro de arquivo, e isso é o ponteiro nossa lá. Então sempre que buscar ou escrever em um arquivo, o arquivo é manter o controle de onde estamos. Então, é uma espécie de cursor, o indicador de posição de arquivo. E assim, sempre que escrever ou ler outra vez em um arquivo, o arquivo realmente se lembra de onde é, e por isso continua a partir de onde o cursor está. Isto pode ser benéfico quando você quiser, por exemplo, ler em uma certa quantia para fazer algo e em seguida, ler no seguinte quantidade, mas às vezes pode querer voltar ou começar realmente a partir de um valor de referência determinado. Então a função fseek, o que ele faz é que nos permite mover o cursor em um determinado arquivo um determinado número de bytes. E então o que temos que fazer é especificar onde o valor de referência é. Assim, ou ele se move para a frente ou para trás, de onde o cursor está atualmente, ou podemos especificar que ele deve mover-se em apenas a partir do início do arquivo ou a partir do fim do ficheiro. E assim você pode passar valores positivos ou negativos para montante, e que tipo de mover o cursor para a frente ou para trás. Antes de entrar em Série de Exercícios outros, dúvidas em arquivo I / O? Okay. Como entramos em mais exemplos, sinta-se livre para me parar para perguntas. Assim, em Whodunit, você está entregue um arquivo bitmap semelhante a esta vermelho no slide, e parece que isso - um monte de pontos vermelhos - e você não sabe realmente o que está escrito. Se você é vesgo, você pode ser capaz de ver uma ligeira cor azulada dentro da média. Essencialmente, que é onde o texto está armazenado. Houve um assassinato que aconteceu, e nós precisamos descobrir quem fez isso. A fim de fazer isso, nós precisamos tipo de converter essa imagem em um formato legível. Se vocês já encontrou este, às vezes, não haveria kits pequenos onde você teria uma lupa com um filme vermelho. Alguém? Sim. Então você seria algo handed como este, você teria uma lupa com o filme vermelho sobre ele, você iria colocá-lo sobre a imagem, e você seria capaz de ver a mensagem escondida nela. Nós não temos uma lupa com película vermelha, então ao invés vamos espécie de criar a nossa própria neste pset. E assim, o usuário vai whodunit entrada, então a pista. Bmp, então essa é a infile, essa é a mensagem ponto vermelho, e então eles estão dizendo verdict.bmp vai ser nosso arquivo de saída. Por isso, vai criar uma nova imagem bitmap semelhante à pista um exceto em um formato legível, onde podemos ver a mensagem oculta. Já que vamos estar lidando com edição e manipulação de bitmaps de algum tipo, vamos tipo de mergulhar no estrutura desses arquivos bitmap. Nós fomos sobre estes um pouco na aula, mas vamos olhar para eles um pouco mais. Bitmaps são essencialmente apenas um arranjo de bytes onde tenha especificado quais bytes significa o quê. Então aqui é como uma espécie de mapa da imagem de bitmap dizendo que ele começa com alguns arquivos de cabeçalho, começa com algumas informações lá. Você vê que, sobre o número de bytes o tamanho 14 é indicado da imagem bitmap, e ela continua. Mas então o que estamos realmente interessados ​​em aqui está começando em torno de byte número 54. Temos esses triplica RGB. O que vai fazer é conter os pixels reais, os valores de cor. Tudo o acima exposto, que o cabeçalho é algumas informações correspondente ao tamanho da imagem, a largura da imagem, e a altura. Quando nós vamos para o preenchimento mais tarde, vamos ver por que o tamanho da imagem pode ser diferente do que a largura ou a altura. Então para representar esses - essas imagens bitmap são sequências de bytes - o que podemos fazer é dizer tudo bem, eu vou me lembrar que no índice 14, é aí que o tamanho é, por exemplo, mas em vez disso o que vamos fazer para tornar isso mais fácil é compactá-lo em uma estrutura. E assim temos duas estruturas feitas para nós, um BITMAPFILEHEADER e um BITMAPINFOHEADER, e por isso sempre que lemos no para o arquivo, por padrão ele vai estar indo em ordem, e assim, de modo que também vai preencher em variáveis ​​como biWidth e biSize. E, em seguida, finalmente, cada pixel é representado por três bytes. O primeiro é a quantidade de azul no pixel, o segundo é a quantidade de verde, e, finalmente, a quantidade de vermelho, onde 0 é essencialmente não verde, azul ou nenhum ou nenhum vermelho e em seguida, ss é o valor máximo. Estes são valores hexadecimais. Então, em seguida, se temos ff0000, em seguida, que corresponde à quantidade máxima de azul e, em seguida, sem verde e sem vermelho, para, então, que nos daria um pixel azul. Então, se temos todos os FF através da placa, então isso significa que temos um pixel branco. Esta é uma espécie de oposto ao normalmente quando dizemos RGB. Ele está realmente acontecendo BGR. Então, se nós realmente olhar para um exemplo de uma imagem bitmap - deixa-me tirar uma aqui. É um pouco pequena. Estou de ampliar, e podemos ver que está pixelizada. Parece que os blocos de cores. Você tem blocos brancos e depois blocos vermelhos. Se você jogar no Microsoft Paint, por exemplo, você poderia fazer algo assim por, basicamente, apenas pintando algumas praças em uma ordem específica. Então, o que se traduz no bitmap é a seguinte. Aqui temos primeiro pixels brancos, que todos os seis são f, e então temos pixels vermelhos, indicado por 0000ff. E assim a seqüência de bytes que temos indica como a imagem bitmap vai olhar. Então, o que eu tenho feito aqui é apenas escrito todos esses bytes e colorido então no vermelho de modo que você pode tipo de ver, se você olhar de soslaio um pouco, como esse tipo de indica um rosto sorridente. A maneira que o trabalho de imagens bitmap é, eu imagino que, basicamente, como uma grade. E assim por padrão, todas as linhas da grade tem que ser um múltiplo de 4 bytes. Se olharmos para uma imagem bitmap, você está preenchendo todos os valores. Por exemplo, você pode ter um vermelho aqui, um verde aqui, um azul aqui, mas você tem que ter certeza de que a imagem é preenchido com um múltiplo de quatro bytes. Então, se eu quero a minha imagem para ser a três quarteirões de largura, então eu teria que colocar um valor vazio no último a torná-lo um múltiplo de quatro. Então eu gostaria de acrescentar em algo que estamos chamando de preenchimento. Eu só vou para indicar que ali com um x. Agora dizer que queremos uma imagem que é de 7 pixels de comprimento, por exemplo. Nós temos 1, 2, 3, 4, 5, 6, 7, e tudo isso é preenchido com a cor. A maneira que as imagens bitmap trabalhar é que precisamos de uma oitava. Agora temos 1, 2, 3, 4, 5, 6, 7. Precisamos de 8 espaços para a imagem bitmap para ler corretamente. Então o que temos que fazer é adicionar em um pouco de enchimento para certificar-se de que todas as larguras são uniformes e que todas as larguras são um múltiplo de 4. E assim eu indicado anteriormente, acolchoar como um x ou uma linha sinuosa, mas as imagens reais de bitmap do estofo é indicado por um 0 hexadecimal. Então, que seria um personagem único, 0. O que pode vir a calhar é o comando XXD. O que ele faz é, na verdade, mostra, como semelhante ao que eu fazia antes com o smiley quando eu realmente imprimiu o que cada cor seria para o pixel e então um código de cores que, quando você executar xxd com os seguintes comandos, então ele vai realmente imprimir o que as cores são para aqueles pixels. O que você tem a fazer é por aqui que eu indicar, como o S-54 diz que eu vou começar no byte 54 porque, antes disso, lembre-se, se olharmos para o mapa dos bitmaps, que é toda a informação do cabeçalho e coisas assim. Mas o que realmente importa é os pixels reais que indicam a cor. Assim, adicionando em que a bandeira,-s 54, então somos capazes de ver os valores de cor. E não se preocupe com as bandeiras complicados e coisas assim. Na especificação do conjunto de problemas, você terá instruções sobre como usar xxd para exibir os pixels. Então, se você vê aqui, meio que se parece com uma caixa verde, essa coisa pequena. Eu tenho um código de cores a 00ff00 basicamente como dizer não azul, muito verde, e não vermelho. De modo que corresponde a verde. Como você pode ver aqui, nós vemos um retângulo verde. Este retângulo verde é de apenas 3 pixels de largura, então o que temos que fazer para ter certeza de que a imagem é um múltiplo de 4 larga é adicionar em estofamento extra. E então é assim que você ver esses 0s aqui. Isso vai realmente ser o resultado de seu pset Resize, essencialmente tomar o pequeno bitmap e depois ampliá-lo por 4. E então o que vemos é que, na verdade, esta imagem é de 12 pixels de largura, mas 12 é um múltiplo de 4, e assim nós realmente não vejo nenhum 0s no final, porque não precisa adicionar nenhum porque está totalmente preenchido. Ele não tem espaço mais. Okay. Qualquer dúvida sobre o preenchimento? Okay. Cool. Como mencionei antes, os bitmaps são apenas uma seqüência de bytes. E então o que temos é, em vez de precisar saber exatamente o número de bytes que corresponde a um elemento específico, que realmente criaram uma estrutura para representar isso. Então, o que nós temos é uma estrutura RGBTRIPLE. Sempre que você tem uma instância de um triplo RGB, porque este é um tipo de definir struct, então você pode acessar a variável rgbtBlue, similarmente as variáveis ​​verde e vermelho, o que indicará a quantidade de azul, verde e vermelho respectivamente, que você tem. Então, se temos a variável azul a 0, o conjunto verde para ff, que é o valor máximo que pode ter, e então a variável vermelho definido como 0, então o que a cor que neste particular RGB triplo representa? >> [Aluno] Verde. Green. Exatamente. Vai ser útil saber que sempre que você tiver uma instância de um triplo RGB, você pode realmente acessar a quantidade de cor - azul, verde e vermelho - separadamente. Agora que já falamos sobre a estrutura do que, vamos dar uma olhada no arquivo BMP. Estes são estruturas feitas para você. Aqui temos uma estrutura BITMAPFILEHEADER. De interesse é o tamanho. Mais tarde, temos o cabeçalho da informação, que tem mais algumas coisas que são interessantes para nós, a saber, o tamanho, a largura e a altura. Como vamos entrar em mais tarde, quando você lê para o arquivo, ele automaticamente lê porque nós definir a fim de ser o mesmo. Assim, o biSize conterá os bytes direitas que correspondem ao tamanho real da imagem. E então, aqui, por último, como nós falamos, nós temos a estrutura RGBTRIPLE typedef. Temos um rgbtBlue, Verde e Vermelho associados. Grande. Okay. Agora que entendemos bitmaps um pouco, entender que temos um cabeçalho de arquivo e um cabeçalho informações associadas a ela e depois disso, temos a parte interessante das cores, e as cores são representados por estruturas RGBTRIPLE, e os que, por sua vez, tem três valores associados ao azul, o verde eo vermelho. Então, agora, nós podemos tipo de pensar em recuperar um pouco. Desculpe. Pense Whodunit. Quando temos o nosso arquivo de pista, então o que nós queremos fazer é ler para ele pixel por pixel e depois de alguma forma alterar os pixels para que possamos produzir em um formato legível. E assim a saída, nós vamos escrever pixel por pixel no arquivo verdict.bmp. Esse é um tipo de muito para fazer. Nós percebemos isso. Então, o que temos feito é que nós, na verdade, desde que com copy.c. O que faz é apenas copy.c faz uma cópia exata de um arquivo de bitmap dado e em seguida, gera-lo. Então, isso já abre o arquivo para você, lê em pixel por pixel, e depois escreve-in em um arquivo de saída. Vamos dar uma olhada nisso. Este é assegurar uma utilização adequada, recebendo os nomes aqui. O que isto significa é que define o arquivo de entrada para ser o que passamos em no infile aqui, que é o nosso argumento de linha de comando segundo. Verifica para ter certeza de que podemos abrir o arquivo. Verifica se podemos fazer uma outfile novo aqui. Então, o que isso faz aqui, é basicamente só começa a ler para o arquivo de bitmap desde o início. O início, como se sabe, contém o BITMAPFILEHEADER, e assim as sequências de bits irá diretamente preencher o BITMAPFILEHEADER. Então o que temos aqui está dizendo que bf BITMAPFILEHEADER - essa é a nossa nova variável do tipo BITMAPFILEHEADER - vamos colocar dentro bf o que lemos no ponteiro, que é o nosso infile. Quanto é que lemos? Lemos em quantos bytes que precisamos para conter a BITMAPFILEHEADER todo. Da mesma forma, isso é o que nós fazemos para o cabeçalho info. Então, nós estamos continuando ao longo de nossa arquivo no infile, e estamos lendo os bits e bytes, e estamos ligando-os diretamente no nestes casos das variáveis ​​que estamos fazendo. Aqui estamos apenas ter certeza de que o bitmap é um bitmap. Agora temos um outfile, certo? Assim como está quando a criamos, é essencialmente vazio. Então nós temos que, basicamente, criar um novo bitmap a partir do zero. O que fazemos é que temos que ter certeza de que nós copiamos no cabeçalho do arquivo eo cabeçalho informações como o infile tem. O que nós fazemos é escrever - e lembre-se que bf é a variável de BITMAPFILEHEADER tipo, então o que fazemos é apenas usar esse conteúdo escrever para o arquivo de saída. Aqui, lembro que falou sobre o preenchimento, como é importante ter certeza de que a quantidade de pixels que temos é um múltiplo de 4. Esta é uma fórmula muito útil para calcular a quantidade de preenchimento que você tem dada a largura do seu arquivo. Eu quero que vocês lembrar que em copy.c temos uma fórmula para calcular o estofamento. Ok? Então, lembre-se que todos. Grande. Então o que copy.c faz a seguir é que itera sobre todos os scanlines. Ele vai percorrer as linhas e depois armazena todos os triplos que ele lê e depois grava-lo no arquivo de saída. Então aqui estamos lendo apenas um RGB triplo de cada vez e depois colocar que Triple mesmos para o arquivo de saída. A parte complicada é que o preenchimento não é uma tripla RGB, e por isso não pode apenas ler que quantidade de preenchimento de triplos RGB. O que temos que fazer é realmente apenas mover nosso indicador de posição do arquivo, mover nosso cursor, que tipo de pular sobre todo o estofamento de modo que estamos na linha seguinte. E então o que isso faz é cópia mostra como você pode querer adicionar o estofamento. Então, nós calculamos a quantidade de preenchimento que precisamos, o que significa que precisamos número preenchimento de 0s. O que isso faz é um loop que coloca estofo número de 0s em nosso arquivo de saída. E então, finalmente, fechar os dois arquivos. Você fecha o infile, bem como o arquivo de saída. Então é assim que copy.c obras, e que vai ser bastante útil. Em vez de apenas na verdade diretamente copiar e colar ou apenas olhando para ele e digitando o que quiser, você pode apenas querer executar este comando no terminal, cp copy.c whodunit.c, que irá criar um novo arquivo, whodunit.c, que contém exatamente o mesmo conteúdo como cópia faz. Então o que podemos fazer é usar isso como uma estrutura sobre a qual construir e editar para o nosso arquivo whodunit. Estes são os nossos para-dos a fazer para Whodunit, mas o que faz copy.c é realmente toma conta da maioria deles para nós. Então, tudo o que precisamos fazer agora é mudar os pixels conforme necessário para realmente tornar o arquivo legível. Lembre-se que para um determinado pixel triplo, assim, para uma determinada variável de RGBTRIPLE tipo, você pode acessar os azuis, os valores ecológicos e vermelho. Isso vai vir a calhar, porque se você pode acessá-los, isso significa que você também pode vê-los, e isso significa que você também pode mudá-los. Então, quando nós voltamos para o nosso exemplo vermelho lupa, basicamente, que estava agindo como uma espécie de filtro para nós. Então, o que nós queremos fazer é que nós queremos para filtrar todos os triplos que estão vindo dentro Existem várias maneiras diferentes de fazer isso. Basicamente, você pode ter qualquer tipo de filtro que você deseja. Talvez você queira mudar todos os pixels vermelhos ou talvez você quer mudar um pixel de cor diferente para uma cor diferente. Isso é com você. Lembre-se que você pode verificar qual a cor do pixel é e então você também pode mudá-lo como você está passando. Okay. Então, isso é Whodunit. Uma vez que você executar Whodunit, você vai saber quem é o culpado do crime era. Agora estamos indo para ir para redimensionar. Nós vamos ainda estar lidando com bitmaps. O que nós vamos fazer é que vamos ter um bitmap de entrada e depois vamos passar em um número e em seguida, obter um bitmap outfile onde isso é basicamente a nossa infile escalado por n. Diga meu arquivo era apenas um pixel grande. Então, se o meu n foi de 3, o dimensionamento por 3, então gostaria de repetir que o pixel n número de vezes, Portanto, 3 vezes, e, em seguida, também reduzir a escala 3 vezes também. Então você vê que eu estou escalando-o na vertical como na horizontal. E então aqui está um exemplo. Se você tem n = 2, você vê que o primeiro pixel azul lá repetido duas vezes horizontalmente, bem como duas vezes verticalmente. E, então, que continua, e então você tem uma escala direta de sua imagem original por dois. Então, se fôssemos para detalhar o pseudocódigo para isso, queremos abrir o arquivo. E então, sabendo que se voltar aqui, vemos que a largura para o outfile vai ser diferente do que a largura para o infile. O que significa isso? Isso significa que o nosso informações de cabeçalho vai mudar. E então o que nós queremos fazer é atualizar as informações do cabeçalho, sabendo que quando lemos nos arquivos se você estiver operando no quadro copy.c, já temos uma variável que indica o que o tamanho é, e coisas assim. Portanto, uma vez que você tem, o que você pode querer fazer é mudar as variáveis ​​particulares. Lembre-se, se você tem uma estrutura, como você acessar as variáveis ​​dentro dessa. Você usa o operador ponto, certo? Então usando isso, você sabe que você vai precisar para mudar a informação do cabeçalho. Então, aqui é só uma lista dos elementos reais que vão estar mudando em seu arquivo. O tamanho do ficheiro irá estar a mudar, da imagem, bem como a largura e a altura. Então, em seguida, voltar ao mapa dos bitmaps, olha se é o cabeçalho do arquivo ou o cabeçalho que contém informações que a informação e, em seguida, faça as alterações necessárias. Mais uma vez, dizer cp copy.c resize.c. Isso significa que resize.c agora contém tudo o que está contido dentro de cópia cópia porque nos fornece uma maneira de ler para cada pixel por pixel scanline. Só que agora, em vez de apenas alterando os valores como fizemos no Whodunit, o que nós queremos fazer é que nós queremos escrever em múltiplos pixels desde que o nosso n é maior que 1. Então, o que nós queremos fazer é que nós queremos esticar horizontalmente por n, bem como esticá-lo na vertical, n. Como podemos fazer isso? Diga o seu n é 2 e você tem esse infile dado. O cursor vai começar no primeiro, eo que você quer fazer, se n é 2, você quer imprimir em duas delas. Então você imprimir em duas delas. Em seguida, o cursor vai mudar para o próximo pixel, que é o vermelho, e vai imprimir duas daqueles vermelhos, anexando-lo para o que é feito antes. Em seguida, o cursor irá se mover para o próximo pixel e desenhar em duas delas. Se você olhar para trás para o quadro copy.c, o que isso faz aqui é ele cria uma nova instância de um triplo RGB, uma nova variável chamada triplo. E aqui, quando se lê para ele, ele lê a partir de 1 infile RGBTRIPLE e armazena-lo dentro dessa variável triplo. Então você realmente tem uma variável que representa o pixel particular. Então, quando você escreve, o que você pode querer fazer é encerrar a declaração fwrite para um ciclo que escreve em sua outfile tantas vezes quanto necessário. Isso é bastante simples. Basicamente só repetir o processo de escrita n número de vezes para escalá-lo na horizontal. Mas, então, temos que lembrar que nosso espaço vai mudar. Anteriormente, dizer que tinha algo de comprimento 3. Então nós só adicionar em quanto estofamento? Só mais uma para torná-lo um múltiplo de 4. Mas dizer que estamos escalando essa imagem em particular ao n = 2. Então quantos pixels azuis teríamos no final? Teríamos 6. 1, 2, 3, 4, 5, 6. Tudo bem. 6 não é um múltiplo de quatro. Qual é o múltiplo mais próximo de 4? Isso vai ser 8. Então, nós estamos realmente vai ter dois caracteres de preenchimento lá. Alguém se lembra se temos uma fórmula para calcular o preenchimento e onde que poderia ser? [Resposta do aluno inaudível] >> Sim copy.c., Direito. Há uma fórmula em copy.c para calcular a quantidade de preenchimento que você tem dada uma largura particular da imagem bitmap. Então isso vai ser útil quando você precisa adicionar uma certa quantidade de estofamento para realmente descobrir o quanto estofamento você precisa adicionar. Mas uma nota, no entanto, é que você quer ter certeza de que você está usando o tamanho certo. Só tome cuidado porque você está basicamente vai ser lidar com duas imagens bitmap. Você quer ter certeza de que você está usando o caminho certo. Quando você está calculando o preenchimento para o outfile, que pretende utilizar a largura da outfile e não a largura da anterior. Grande. Esse tipo de cuida de esticar uma imagem bitmap inteiro horizontalmente. Mas o que nós queremos fazer é realmente esticar-lo na vertical também. Este vai ser um pouco mais complicado, porque quando terminar de copiar uma linha e escrita nessa linha, o nosso cursor vai ser no final. Então, se ler de novo, então ele só vai ler para a próxima linha. Então, o que nós queremos fazer é uma espécie de encontrar alguma maneira de copiar as linhas novamente ou apenas uma espécie de tomar essa linha e depois reescrevê-lo novamente. Como eu meio que aludiu, existem várias maneiras diferentes de fazer isso. O que você poderia fazer é como você está passando e leitura através da scanline especial e alterá-lo, se necessário, em seguida, tipo de loja todos os pixels em uma matriz. Depois, mais tarde você sabe que você vai precisar para imprimir essa matriz novamente, e assim você pode apenas usar essa matriz para fazer isso. Outra maneira de fazer isso é que você pode copiar uma linha para baixo, entender que você precisa copiar isso de novo, então realmente mover o cursor, e que vai estar usando o fseek método. Você pode mover o cursor todo o caminho de volta e repita o processo de cópia novamente. Então, se o nosso número de escala é n, então quantas vezes teríamos de voltar e reescrever uma linha? >> [Aluno] n - 1. >> Sim, perfeito. n - 1. Nós fizemos isso uma vez, e por isso, então vamos querer repetir o processo voltar atrás n - 1 quantidade de vezes. Okay. Portanto, você tem sua função de redimensionamento. Agora podemos chegar a uma parte muito divertido, minha pset favorito, que é Recuperar. Em vez de bitmaps, desta vez estamos lidando com JPEG. Nós não estamos realmente dado um arquivo apenas de JPEGs, nós estamos dando basicamente um formato de cartão de memória-prima. E assim, este contém um pouco de valores e informações de lixo, no início, e então ele começa e tem um monte de arquivos JPEG. No entanto, estamos entregou um cartão onde nós excluimos as fotos; essencialmente, esquecemos onde as fotos estão localizados dentro do cartão. Então a nossa tarefa em Recuperação é para passar por este formato de cartão e encontrar as fotos novamente. Felizmente, a estrutura de arquivos JPEG eo arquivo de cartão é um pouco útil. Ele definitivamente poderia ter sido um pouco mais complicado se não fosse nesse formato particular. Cada arquivo JPEG realmente começa com duas sequências possíveis, listados acima. Basicamente, sempre que você tem um novo arquivo JPEG, que começa com a sequência de qualquer FFD8 FFE0 ou ​​o outro, FFD8 ffe1. Outra coisa útil é saber que JPEGs são armazenados de forma contígua. Assim, sempre que um arquivo JPEG termina, o outro começa. Portanto, não há qualquer tipo de em-entre os valores lá. Depois de bater o início de um JPEG, se você já foi a leitura de um JPEG, você sabe que você bateu o final do anterior eo início do próximo. Para tipo de visualizar isso, eu fiz um esquema. Outra coisa sobre JPEGs é que pode lê-los em seqüências de 512 bytes de cada vez, de forma semelhante com o início do cartão. Nós não precisamos de estar a verificar cada byte, porque isso seria um saco. Então, em vez disso, o que podemos fazer é realmente acabou de ler em 512 bytes de cada vez e então, em vez de verificar entre os naquelas pequenas fatias pequenas, podemos verificar apenas o início dos 512 bytes. Essencialmente, neste quadro, o que você vê é no início do cartão, você tem valores que não são realmente relevantes para os JPEGs propriamente ditas. Mas então o que eu tenho é uma estrela para indicar uma das duas seqüências de partida para um JPEG. Assim, sempre que você vê uma estrela, você sabe que você tem um arquivo JPEG. E então todos os arquivos JPEG vai ser um múltiplo de 512 bytes mas não necessariamente o mesmo múltiplo. A maneira que você sabe que você bateu outro JPEG é se você bater outra estrela, uma outra sequência de partida de bytes. Então, o que você tem aqui é que você tem o vermelho arquivo JPEG continuar até que você bata uma estrela, o que é indicado por uma nova cor. Você continua e depois de bater outra estrela, você bateu outro JPEG, você continuar todo o caminho até o fim. Você está na última foto aqui, a uma rosa. Você vai para o fim até chegar ao final do arquivo de caracteres. Isso vai ser realmente útil. A poucos takeaways principais aqui: O arquivo de cartão não começar com um JPEG, mas uma vez que começa um JPEG, todas as imagens JPEG são armazenados lado a lado uns com os outros. Alguns pseudocódigo para a recuperar. Primeiro, vamos abrir o nosso arquivo de cartão, e que vai estar usando o nosso arquivo de E / S funções. Vamos repetir o processo a seguir até que tenhamos atingido o final do arquivo. Nós vamos ler 512 bytes de cada vez. E o que eu disse aqui é que estamos indo para armazená-lo em um buffer, Então, basicamente segurar a esses 512 bytes até que saibamos exatamente o que fazer com eles. Então, o que nós queremos fazer é que nós queremos verificar se nós batemos uma estrela ou não. Se nós batemos uma estrela, se nós batemos uma das sequências iniciais, então sabemos que nós batemos um novo arquivo JPEG. O que nós queremos fazer é que vamos querer criar um novo arquivo no nosso diretório pset4 para continuar a fazer esse arquivo. Mas também, se já fez um JPEG antes, então queremos acabar com esse arquivo e empurrá-lo para a pasta pset4, onde teremos que arquivo armazenado, porque se nós não especificar que já terminou o arquivo JPEG, então nós basicamente tem uma quantidade indeterminada. Os JPEGs nunca vai acabar. Então, nós queremos ter certeza de que quando estamos lendo em um arquivo JPEG e escrito que, queremos fechar especificamente que, a fim de abrir o próximo. Vamos querer verificar várias coisas. Queremos verificar se estamos no início de um novo JPEG com o nosso buffer e também se já ter encontrado um JPEG antes porque isso vai alterar o seu processo ligeiramente. Então depois de passar por todo o caminho e você acertar o fim do arquivo, então o que você vai querer fazer é que você vai querer fechar todos os arquivos que estão abertos. Isso provavelmente vai ser o último arquivo JPEG que você tem, bem como o arquivo do cartão que você está lidando. O último obstáculo que temos de enfrentar é como realmente fazer um arquivo JPEG e como realmente empurrá-lo para a pasta. O pset requer que cada JPEG que você encontrar estar no seguinte formato, onde você tem o número. jpg. O número, mesmo que seja 0, chamamos isso 000.jpg. Sempre que você encontrar um JPEG em seu programa, você vai querer nomeá-lo na ordem em que ele é encontrado. O que isso significa? Precisamos tipo de manter o controle de quantos temos encontrado e que o número de cada JPEG deveria ser. Aqui vamos tirar proveito da função sprintf. Semelhante ao printf, que apenas um tipo de impressões um valor fora no terminal, sprintf imprime o arquivo para dentro da pasta. E então o que este faria se eu tivesse sprintf título, e depois a corda lá, seria imprimir 2.jpg. Supondo que eu fechei meus arquivos corretamente, que contém o arquivo que eu tinha escrito para fora. Mas uma coisa é que o código que eu tenho aqui não bastante satisfazer o que requer o pset. O pset requer que o arquivo JPEG segundo deve ser nomeado 002 em vez de apenas dois. Então, quando você imprimir o nome, então talvez você pode querer alterar o espaço reservado ligeiramente. Alguém se lembra de como permitir espaços extras quando imprimir algo? Sim. >> [Aluno] Você coloca um 3 por cento entre o sinal ea 2. >> Sim, perfeito. Você vai colocar um 3 neste caso, porque queremos espaço para 3. 3d% provavelmente dar-lhe 002.jpg vez de 2. O primeiro argumento para a função sprintf é na verdade uma matriz de char, que já sabia como strings. Aqueles vontade, espécie de mais como um armazenamento temporário, apenas armazenar a string resultante. Você realmente não vai ser lidar com isso, mas você precisa incluí-lo. Sabendo que cada nome do arquivo tem o número, que leva até três personagens, e depois. jpg, quanto tempo deve ser essa matriz? Jogue fora um número. Quantos caracteres no título, no nome? Portanto, há três hashtags, período, jpg. >> [Aluno] 7. >> 7. Não é bem assim. Nós vamos querer 8 porque queremos permitir que o terminador nulo também. Finalmente, só para tirar o processo que você vai fazer para recuperar, você tem alguma informação começo. Você continua até encontrar o início de um arquivo JPEG, e que pode ser qualquer uma das duas sequências de partida. Você continua lendo. Cada barra aqui representa 512 bytes. Você continua a leitura, manter a leitura até encontrar uma outra seqüência de partida. Uma vez que você tem isso, você termina o atual JPEG - neste caso, é o vermelho, assim que você quer acabar com isso. Você quer sprintf o nome do que em sua pasta pset4, então você quer abrir um novo JPEG e manter a leitura até encontrar o próximo. Continue a ler, manter a leitura, e, finalmente, eventualmente, você vai chegar ao final do arquivo, e assim que você vai querer fechar o JPEG passado que você estava trabalhando, sprintf que em sua pasta pset4, e depois olhar para todas as fotos que você já conquistou. Essas fotos são realmente imagens de CS50 pessoal, e por isso este é o lugar onde a parte divertida bônus do pset vem em é que você está competindo em suas seções para encontrar os TFs nas fotos e tirar fotos com eles para provar que você fez o pset e assim você pode ver que os membros da equipe estão nas fotos. Então você tirar fotos com o pessoal. Às vezes, você tem que persegui-los. Provavelmente alguns deles vão tentar fugir de você. Tirar fotos com eles. Isso está em curso. Não é devido quando o pset é devido. O prazo será anunciado na especificação. Em seguida, juntamente com sua seção, qualquer seção leva mais fotos com os membros da maioria dos funcionários vai ganhar um prêmio muito legal. Esse é um tipo de incentivo para que o seu pset4 terminar o mais rápido possível porque então você pode começar a trabalhar caçando todos os diferentes CS50 funcionários. Isso não é obrigatório, porém, assim que uma vez que você obter as imagens, então você está acabado com pset4. E eu estou acabado com Passo a passo 4, portanto, obrigado a todos por terem vindo. Boa sorte com Forensics. [Aplausos] [CS50.TV]