Ir ao conteúdo

C Existe comando equivalente à fseek mas que pare o cursor sobre uma linha?


Ir à solução Resolvido por codigo rapido,

Posts recomendados

Postado

tenho a seguinte linha para ler um txt:

 

 

fseek(file, (registro - 1) * LENGTH_LINE, SEEK_SET);


fseek é para ler conjuntos de bytes de tamanho LENGTH_LINE. Onde o inicio do proximo se dá de acordo com (registro - 1) * LENGTH_LINE.
Existe algum comando que mova o cursor, equivalente ao fseek, que leia os dados entre cada '\n' ?

Postado

Olá!

 

Talvez procure a função  fscanf  combinada à 'scanset' - sem/com gravação

p. ex:

signed answer = -1;
for (unsigned nregistro = 0 ; nregistro < 3 ; ++ nregistro) {
		answer = fscanf(arquivo , " %*[^\n]");
		if (answer != 0) break;
}

 

" %*[^\n]" --- registra que todos os caracteres até \n (Nova Linha) são lidos e não gravados.

 

Para gravação, delete o asterisco e forneça um endereço de memória no argumento seguinte à 'strings' de formato. 

char buff[BUFSIZ];

signed answer = -1;
for (unsigned nregistro = 0 ; nregistro < 3 ; ++ nregistro) {
		answer = fscanf(arquivo , " %[^\n]" , &buff[0]);
		if (answer != 0) break;
}

 

(:)) --- é isso?

  • Curtir 1
Postado

para ler:
fscanf(arquivo , " %*[^\n]");

para gravar:
answer = fscanf(arquivo , " %[^\n]" , &buff[0]);

Não sei se isso funciona com fseek...
O fseek posiciona o cursor pra leitura e gravação num ponto no arquivo.
veja:
fseek(file, (registro - 1) * LENGTH_LINE, SEEK_SET);
No caso, todas as linhas são obrigatoriamente do mesmo tamanho LENGTH_LINE.
Supondo que o ponto em que o cursor para é = (registro - 1) * LENGTH_LINE
a posição em que o cursor pula é de 'LENGTH_LINE' em 'LENGTH_LINE'. Isso acelera muito as leituras e gravacões.

O comando para posicionar o cursor sobre o primeiro caractere logo após o 3º '\n' é qual?
O fseek também é capaz de posicionar o cursor apos, por exemplo, o 3º '\n'?
 

Então eu queria saber se de alguma forma o fseek ou outro comando faça o equivalente, mas com o 'passo' dependendo da posição de cada '\n'.
Exemplo:
fseek(file, registro_Numero_x_depois_de_barra_n, SEEK_SET);

 

  • Solução
Postado

Obrigado a todos pelas respostas!

Mas pelo que eu li, infelizmente isso que estou querendo não é possivel em C.
Não existe como o cursor, como um fseek, parar sobre um "\n" especifico.
Eu seria obrigado a ter mapeado todas as quebras de linha. A solução para mim é fazer como o convencional do fseek e tratar os dados de outro modo.
 

Postado
Em 29/08/2021 às 23:09, codigo rápido disse:

Mas pelo que eu li, infelizmente isso que estou querendo não é possivel em C.
Não existe como o cursor, como um fseek, parar sobre um "\n" especifico.
Eu seria obrigado a ter mapeado todas as quebras de linha

 

Agora isso é objetivo... Não sei o que andou lendo, além da minha resposta. Mas o que leu está errado.

 

Contrariando a aparente tendência de separar arquivos em arquivos binários e arquivos texto, lembro que tais coisas não existem.

 

Não existe arquivo binário, não existe arquivo texto. Existe arquivo.

 

E arquivo em C seria o que?

 

Deve ter notado que é FILE*, e o asterisco indica um ponteiro. Não só existe um cursor como ele pode ir e voltar. Mesmo no modo texto tem  ungetc() e ungetwc().

Postado
Citação

Agora isso é objetivo... Não sei o que andou lendo, além da minha resposta. Mas o que leu está errado.

você não sabe o que um fseek faz? moço, você mesmo quem passou um codigo com isso pra mim. E sem eu te pedir... e de novo...

eu fui especifico que eu procuro um comando equivalente ao fseek, mas que me permita parar o cursor apenas sobre o enézimo '\n' dado... Não tem como melhorar isso.

O uso de fseek para posicionar o leitor em um ponto do arquivo permite que esse método seja mais rápido do que eu ficar passando a toda hora por todas as linhas anteriores a uma x linha dada.

Então se eu falei de fseek é porque eu quero algo que funcione de forma equivalente. Algo que vá direto ao ponto. Em quê isso se assemelha a um fgets? Nada!

E isso tem até logica: O fseek, alem de parar num ponto especifico dado, ele não testa caracteres. Ele simplesmente os pula para o ponto dado. Qualquer outro comando, mesmo que seja para parar exatamente sobre o '\n' dado terá que testar caractere por caractere, até encontrar o desejado, mesmo que seja para mapea-lo em algum momento, como eu mesmo sugeri que poderia ser criado comando assim.
 

Citação

Contrariando a aparente tendência de separar arquivos em arquivos binários e arquivos texto, lembro que tais coisas não existem.

Não tem nada a ver com o que eu perguntei. E sua resposta só mostra uma coisa: As coisas foram definidas assim em C por um motivo... E isso faz diferença sim, principalmente no que se refere a utilidade. isso vai influenciar na escolha dos comandos que irá utilizar. Ou você vai querer utilizar um printf de qualquer parte de dentro de arquivo binário pro usuário? se você falar que sim porque não faz diferença entre arquivo texto de binario...
 

Citação

E arquivo em C seria o que?

Então podia ser o bin compilado ou o .txt ou .c fonte... se isso for relevante, como é para as coisas que eu perguntei, você falaria que era o binario ou o fonte... Mas veja... não foi o que eu perguntei. Mas mesmo assim vou te dar a dica, aprende: todo o arquivo texto é binário, mas nem todo arquivo binario é texto. Por isso não faz sentido falar de certos comandos de tratamento de texto para arquivos que são binarios. Então não venha com baboseiras genéricas porque eu não to fazendo outra linguagem. Eu to estudando C. Não é Java, não é C++, não é assembly... É C e ponto! Então aceite que para a linguagem faz direfença sim cada um desses detalhes que você inciste em discutir.

Postado
11 minutos atrás, codigo rápido disse:

Então se eu falei de fseek é porque eu quero algo que funcione de forma equivalente. Algo que vá direto ao ponto. Em quê isso se assemelha a um fgets? Nada! Quem é bem entendedor sabe que a resposta é pra atrapalhar.

E não tem 'mas' sobre esse assunto... A resposta é simples: Não existe!

 

Faça a seguinte conta:

 

cada fseek() muda o ponteiro do arquivo para um outro ponto

 

cada fgets() muda o ponteiro para o próximo newline, e é o que você diz que quer.

 

 

14 minutos atrás, codigo rápido disse:

eu fui especifico que eu procuro um comando equivalente ao fseek, mas que me permita parar o cursor apenas sobre o enézimo '\n' dado... Não tem como melhorar isso

 

Entendo: 10 linhas? 10 fgets()

 

 

 

 

Postado
10 horas atrás, codigo rápido disse:

Ou você vai querer utilizar um printf de qualquer parte de dentro de arquivo binário pro usuário?

 

Recomento ver man od no Linux. O nome vem de octal dump, e é um programa que faz isso: usa printf() dentro de partes de um arquivo. Como arquivo é uma coleção de bytes, um parâmetro indica se quer a saída em texto, hexadecimal ou octal. Eu até te mostrei um trecho da saída desse programa em outro post.

 

Foi escrito em C e é ferramenta básica desde os '70.

 

10 horas atrás, codigo rápido disse:

Mas veja... não foi o que eu perguntei. Mas mesmo assim vou te dar a dica, aprende: todo o arquivo texto é binário, mas nem todo arquivo binario é texto. Por isso não faz sentido falar de certos comandos de tratamento de texto para arquivos que são binarios

 

EXEMPLO em C

 

Recortei e colei de um exemplo que te mostrei antes, num tópico sobre navegação em vetores de estruturas

 

O que o programa faz

 

O exemplo aceita (exige) dois parâmetros: o nome de um arquivo e um número de linha.

 

A entrada do exemplo
 

    1                           "options": {
    2                                   "cwd": "${fileDirname}"
    3                           },
    4                           "problemMatcher": [
    5                                   "$gcc"
    6                           ],
    7                           "group": "build",
    8                           "detail": "compiler: /usr/bin/clang" (8)
    9                   }
   10           ]
   11   }


 

São 11 linhas recortadas de um arquivo qualquer. A linha 8 tem até um "(8)" no final.

O programa abre o arquivo e usa fgets() para encontrar a linha certa. Depois usa fseek() para posicionar o cursor de volta no início da linha. E então usa fgets() para tentar ler de novo a mesma linha, para testar. E usa strcmp() para comparar as duas linhas, que devem ser a mesma. E não é que são?

 

Um teste

 

CH> ./tseek-0830 file.txt 1


Testando com a linha 1 de "file.txt"
Esta deve ser a linha 1:
                        "options": {

    O "cursor" do arquivo esta em 17
    a linha 7 tem 16 bytes
    usando fseek() para voltar a ler essa linha
    SEEK ok
    Relendo a linha 1
                        "options": {

Linha lida:
                        "options": {

E as linhas sao iguais!

CH> ./tseek-0830 file.txt 8


Testando com a linha 8 de "file.txt"
Esta deve ser a linha 8:
                        "detail": "compiler: /usr/bin/clang" (8)

    O "cursor" do arquivo esta em 163
    a linha 7 tem 44 bytes
    usando fseek() para voltar a ler essa linha
    SEEK ok
    Relendo a linha 8
                        "detail": "compiler: /usr/bin/clang" (8)

Linha lida:
                        "detail": "compiler: /usr/bin/clang" (8)

E as linhas sao iguais!

 

E assim se pode ver que

  • o "cursor" existe, em qualquer arquivo.
  • As funções que tratam fluxos --- streams --- convivem com as funções que posicionam o FILE*
  • arquivo texto ou arquivo banco de dados ou arquivo de foto é a mesma coisa. Em Unix por exemplo TUDO é um arquivo, como a memória, a lista de processos e tal

As funções que tratam streams tem um tratamento diferente para sequencias de '\r' e '\n', um tratamento diferente para TAB e backspace mas um arquivo é apenas uma série de bytes.

 

O código em C

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int  testa_arquivo(const char*, const char*);
void uso();

int main(int argc, char** argv)
{
    if (argc < 3) uso();
    testa_arquivo(argv[1], argv[2]);
    return 0;
}

int testa_arquivo(const char* arquivo, const char* l)
{
    char     linha[80];
    char*    p       = linha;
    unsigned n_linha = atoi(l);
    if (n_linha == 0) return -3;
    if (n_linha > 42) return -3;

    printf("\n\nTestando com a linha %u de \"%s\"\n", n_linha, arquivo);
    FILE* F = fopen(arquivo, "r");
    if (F == NULL) return -1;

    for (unsigned i = 0; i < n_linha; i += 1)
    {
        p = fgets(p, sizeof(linha), F);
        if (p == NULL) return -1;
    }
    printf("Esta deve ser a linha %u:\n", n_linha);
    puts(p);
    long posicao = ftell(F);
    printf(
        "\
    O \"cursor\" do arquivo esta em %ld\n\
    a linha 7 tem %zu bytes\n\
    usando fseek() para voltar a ler essa linha\n",
        posicao, strlen(p));
#ifdef __GNUC__
    // no Unix pode usar um valor negativo
    int r = fseek(F, -(long)strlen(p), SEEK_CUR);
#else
    // mas no Windows em MSVC não 
    int r = fseek(F, posicao - (long)strlen(p) - 1, SEEK_SET);
#endif
    if (r != 0)
    {
        printf("seek falhou\n");
        return -5;
    }
    printf(
        "\
    SEEK ok\n\
    Relendo a linha %u\n",
        n_linha);
    char replay[80];
    p = replay;
    p = fgets(p, sizeof(replay), F);
    puts(p);

    printf("Linha lida:\n");
    puts(p);

    if (0 == strcmp(linha, replay))
        printf("E as linhas sao iguais!\n");
    else
        printf("Algo saiu errado! :(\n");

    fclose(F);
    return 0;
}

void uso()
{  // o classico
    printf("Use: \"programa arquivo linha\" [linha<43]\n");
    exit(0);
}
// fim

 

Na abertura do tópico

 

Em 28/08/2021 às 23:19, codigo rápido disse:
fseek(file, (registro - 1) * LENGTH_LINE, SEEK_SET);


fseek é para ler conjuntos de bytes de tamanho LENGTH_LINE. Onde o inicio do proximo se dá de acordo com (registro - 1) * LENGTH_LINE.


fseek() não "é para ler" nada. É uma função de posicionamento do ponteiro. Como eu expliquei umas vezes antes um arquivo é uma sequência de bytes. Veja a declaração:


 

        FILE* F = fopen(arquivo, "r");

 

F é um ponteiro. 

 

E o código sugere que registro seja de tamanho fixo, o que em geral não é verdade em um texto. Assim como nas strings em C, nos textos é preciso ler a linha pra ver onde ela termina. E não faz muita diferença em relação a contar os blocos em um fread().

 

 

 

 

 

 

image.png

  • Obrigado 1
Postado
Em 30/08/2021 às 13:59, codigo rápido disse:

Não percebe que se você já fez um laço for pra usar esses fgets não vai mais precisar de fseek?

Recomendo humildade ao aprender uma linguagem nova, e mesmo no uso cotidiano de uma linguagem e na análise de um texto ou código.

 

É claro que é preciso chamar fseek() para voltar o cursor --- que você diz que não existe em "arquivos texto" ---  de modo que fgets() quando chamada de novo leia  a mesma linha outra vez

 

A outra  alternativa seria reabrir o arquivo ou chamar rewind() e rodar o loop de novo.

 

A razão do código

 

O que eu mostrei é que o FILE* existe, o tal cursor, e o ponteiro desconhece essas coisas de texto ou binário. C foi escrita para escrever um sistema e não para ensinar a programar. E é básico nesse sistema --- Unix --- que tudo é um arquivo.

 

Imagine um navegador de texto que não pudesse ter o texto todo na memória e talvez entenda a razão de usar fgets() para navegar pelas linhas e fseek() para posicionar o ponteiro na parte do texto que está na tela. Por exemplo, eu já escrevi um editor de texto e um recuperador de arquivos e ambos usavam isso. E ficaram em uso por tempos.

 

Espero que os leitores tenham no geral compreendido.

Visitante
Este tópico está impedido de receber novas respostas.

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...