12

多くの投稿を見てきましたが、欲しいものは見つかりませんでした。
私は間違った出力を得ています:

ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ......  // may be this is EOF character

無限ループに入ります。

私のアルゴリズム:

  1. ファイルの終わりに移動します。
  2. ポインターの位置を 1 減らし、1 文字ずつ読み取ります。
  3. 10行が見つかった場合、またはファイルの先頭に達した場合は終了します。
  4. ここで、ファイル全体を EOF までスキャンし、//コードに実装されていないものを出力します。

コード:

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

using namespace std;
int main()
{
    FILE *f1=fopen("input.txt","r");
    FILE *f2=fopen("output.txt","w");
    int i,j,pos;
        int count=0;
        char ch;
        int begin=ftell(f1);
        // GO TO END OF FILE
        fseek(f1,0,SEEK_END);
        int end = ftell(f1);
        pos=ftell(f1);

        while(count<10)
        {
            pos=ftell(f1);
            // FILE IS LESS THAN 10 LINES
            if(pos<begin)
                break;
            ch=fgetc(f1);
            if(ch=='\n')
                count++;
            fputc(ch,f2);
            fseek(f1,pos-1,end);
        }
    return 0;
}

更新 1:

コードの変更: エラーが 1 つだけになりました - 入力に次のような行がある場合

3enil
2enil
1enil

it prints 10 lines only

line1
line2
line3ÿine1
line2
line3ÿine1
line2
line3ÿine1
line2
line3ÿine1
line2

PS:
1. notepad++ でウィンドウを操作する

  1. これは宿題ではありません

  2. また、これ以上メモリを使用したり、STL を使用したりせずに実行したいと考えています。

  3. 基本的な知識を向上させるために練習しているので、関数について投稿しないでください (tail -5 tc など)。

私のコードを改善するのを手伝ってください。

4

8 に答える 8

9

コード内のコメント

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

int main(void)
{
    FILE *in, *out;
    int count = 0;
    long int pos;
    char s[100];

    in = fopen("input.txt", "r");
    /* always check return of fopen */
    if (in == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }
    out = fopen("output.txt", "w");
    if (out == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }
    fseek(in, 0, SEEK_END);
    pos = ftell(in);
    /* Don't write each char on output.txt, just search for '\n' */
    while (pos) {
        fseek(in, --pos, SEEK_SET); /* seek from begin */
        if (fgetc(in) == '\n') {
            if (count++ == 10) break;
        }
    }
    /* Write line by line, is faster than fputc for each char */
    while (fgets(s, sizeof(s), in) != NULL) {
        fprintf(out, "%s", s);
    }
    fclose(in);
    fclose(out);
    return 0;
}
于 2013-07-26T09:49:39.140 に答える
7

コードには多くの問題があります。最も重要なことは、関数が成功したことを決してチェックしないことです。ftellまた、結果をに保存することintもあまり良い考えではありません。次に、テストがありpos < beginます。これは、エラーが発生した場合にのみ発生します。fgetcそして、結果をa に入れているという事実char(結果として情報が失われます)。そして、最初の読み取りがファイルの最後にあるため、失敗します (ストリームがエラー状態になると、そこにとどまります)。ftellそして、ファイルがテキストモードで開かれた場合、(Unix を除いて)によって返される値に対して確実に算術演算を行うことができないという事実。

ああ、「EOF文字」はありません。'ÿ'完全に有効な文字 (Latin-1 では 0xFF) です。fgetcの戻り値をに割り当てるとchar、ファイルの終わりをテストする可能性が失われます。

一度に 1 文字ずつ逆方向に読み取るのは非常に非効率的です。通常の解決策は、十分に大きなバッファを割り当ててから、その中の をカウントする'\n'ことです。

編集:

アイデアを与えるためのちょっとしたコード:

std::string
getLastLines( std::string const& filename, int lineCount )
{
    size_t const granularity = 100 * lineCount;
    std::ifstream source( filename.c_str(), std::ios_base::binary );
    source.seekg( 0, std::ios_base::end );
    size_t size = static_cast<size_t>( source.tellg() );
    std::vector<char> buffer;
    int newlineCount = 0;
    while ( source 
            && buffer.size() != size
            && newlineCount < lineCount ) {
        buffer.resize( std::min( buffer.size() + granularity, size ) );
        source.seekg( -static_cast<std::streamoff>( buffer.size() ),
                      std::ios_base::end );
        source.read( buffer.data(), buffer.size() );
        newlineCount = std::count( buffer.begin(), buffer.end(), '\n');
    }
    std::vector<char>::iterator start = buffer.begin();
    while ( newlineCount > lineCount ) {
        start = std::find( start, buffer.end(), '\n' ) + 1;
        -- newlineCount;
    }
    std::vector<char>::iterator end = remove( start, buffer.end(), '\r' );
    return std::string( start, end );
}

これは、エラー処理が少し弱いです。特に、ファイルを開くことができないことと、その他のエラーを区別する必要があるでしょう。(他のエラーは発生しないはずですが、わかりません。)

また、これは純粋な Windows であり、実際のファイルには純粋なテキストが含まれて'\r'おり、CRLF の一部ではないテキストは含まれていないと想定しています。(Unix の場合は、最終行の次の行をドロップするだけです。)

于 2013-07-26T09:51:07.573 に答える
1

私はあなたがfseek間違っていると信じています。man fseekGoogle で確認してください。

これを試して:

fseek(f1, -2, SEEK_CUR);
//1 to neutrialize change from fgect
//and 1 to move backward

また、最後の要素の先頭に位置を設定する必要があります。

fseek(f1, -1, SEEK_END).

end変数は必要ありません。

すべての関数 ( fgetcfseekおよびftell) の戻り値を確認する必要があります。それは良い習慣です。このコードが空のファイルまたは同様のファイルで機能するかどうかはわかりません。

于 2013-07-26T09:10:41.203 に答える
1
int end = ftell(f1);
pos=ftell(f1);

これは、ファイルの最後のポイントを示しているため、EOF. 読むとEOFエラーが発生し、ppointerは1スペース前に移動しようとしています...

したがって、現在の位置を 1 つ減らすことをお勧めします。または、while ループの先頭に fseek(f1, -2,SEEK_CUR) を配置して、fread を 1 ポイント補い、1 ポイント戻します...

于 2013-07-26T09:11:09.667 に答える
0

用途 :fseek(f1,-2,SEEK_CUR);戻る

私はこのコードを書きます、それは動作します、あなたは試すことができます:

#include "stdio.h"

int main()
{
        int count = 0;
        char * fileName = "count.c";
        char * outFileName = "out11.txt";
        FILE * fpIn;
        FILE * fpOut;
        if((fpIn = fopen(fileName,"r")) == NULL )
                printf(" file %s open error\n",fileName);
        if((fpOut = fopen(outFileName,"w")) == NULL )
                printf(" file %s open error\n",outFileName);
        fseek(fpIn,0,SEEK_END);
        while(count < 10)
        {
                fseek(fpIn,-2,SEEK_CUR);
                if(ftell(fpIn)<0L)
                        break;
                char now = fgetc(fpIn);
                printf("%c",now);
                fputc(now,fpOut);
                if(now == '\n')
                        ++count;
        }
        fclose(fpIn);
        fclose(fpOut);
}
于 2013-07-26T09:06:28.650 に答える