Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Функция "переворачиваня" текстового файла, не работает с большими файлами 
:(
    Опции темы
Code Magister
  Дата 5.6.2005, 02:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



Код

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

void main()
{
    FILE *f;
    FILE *f2;
    char str[100], *way;
    unsigned long int c=0;  //счетчик строк

    clrscr();
    cout<<"FileReverser v1.0 beta\n\n";
    cout<<"Input the name of file: ";
    cin>>way;
    f=fopen(way,"r");
    if (!f) { printf("File does not exist...",f); getch(); abort(); }
    f2=fopen("reverse.txt","w");
    if (!f2) { printf("Can't create output file...",f); getch(); abort(); }

    while(!feof(f))       //тут мы считаем сколько всего строк в файле
    {
        fscanf(f,"%s",str); //это нужно только для перемещения курсора
        c++;                         
    }
    while(c>0)           //тут переходим  на последнюю строку, преносим ее в 2 файл, затем на предпоследюю и т. д.
    {
        rewind(f);
        for(int i=1; i<c; i++) fscanf(f,"%s",str);   
        fscanf(f,"%s",str);
        fprintf(f2,"%s\n",str);
        c--;
    }
    cout<<"done!\nPress any key...";
    getch();
    fclose(f);
    fclose(f2);
}

Вот она эта функция, написаная мной.
С небольшими файлами работает безотказно, НО если в файле (насколько я заметил) больше, чем 32786 строки, то прога сначала дуплится, а потом начинает выводить эту самую 32786 строку ХЗ сколько раз.
В чем тут дело?
Добавлено @ 02:12
И еще, если файл меньше, то она хоть и правильно, но как-то очень медленно работает.
Я пытался откомпилировать это в билдере, так там вобще ошибку на стадии выполнения выдает.

Это сообщение отредактировал(а) _Slipknot - 5.6.2005, 02:19
--------------------
PM MAIL WWW ICQ   Вверх
Mayk
Дата 5.6.2005, 07:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Можно сделать так: перемотать файл в конец, потом считывать с конца в буфер n-ное кол-во байт, строки из буфера в обратном порядке пихать в выходной файл. Только тут надо учитывать то, что на начало буфера выпадает середина строки. Поэтому приходится отматывать вперед, чтобы при следующем заходе прочитать всю строку.
И еще один момент - в DOS'е строка оканчивается не так как в Linux. Это может повлиять на результат.

800 килобайтный Фауст(20427 строк) перевернулся за ~0.1 секунду
3 метровый Эмбер(60895 строк) перевернулся за ~0.66 секунды.

Код

#include <stdio.h>
int main(int argc, char** argv)
{
        FILE* fin, *fout;
        long pos;char buf[1024];int len;
        fin = fopen( argv[1], "r" );
        fout = fopen( argv[2], "w" );
        fseek(fin, 0, SEEK_END);
        pos = ftell(fin);
        while( pos > 0 ){
                char* ptr;
                len = ( pos < sizeof(buf) ) ? pos : sizeof(buf);
                fseek(fin, -len, SEEK_CUR);
                printf("%x\n", pos);
                fread(buf, len, 1, fin);
                buf[len]=0;
                while( ptr = strrchr(buf, '\n') ){
                        *ptr++=0;
                        fprintf(fout,"%s\n",ptr);
                }
                if( pos < sizeof(buf) ){
                        fputs(buf, fout);
                        break;
                }
                fseek(fin, strlen( buf )-len, SEEK_CUR);
                pos += strlen( buf ) - len;
        }
        fclose(fin);
        fclose(fout);
}


Цитата(_Slipknot @ 5.6.2005, 02:09)
И еще, если файл меньше, то она хоть и правильно, но как-то очень медленно работает.

Ну так, посмотри как ты много читаешь: допустим в файле 5 строк.
Ты прочитаешь 5 строк, чтобы узнать их кол-во,
затем прочитаешь 4 строки, чтобы прочитать и вывести пятую,
потом три строки, чтобы вывести четвертую.
В общем кол-во чтений строк - сумма арифмитической прогрессии от 1 до n, где n - кол-во строк. И это будет что-то типа
S=n*((1+n)/2)=(n^2+n)/2;
если я не попутал. В примере S = (25+5) / 2 = 15; да еще +5 = 20 чтений строк.
А теперь пусть n=1000=10^3. S=(10^6+10^3) / 2 = 10 * (10^5 + 10^2) / 2 = 5 * 10^ 5 + 5 * 10^2 = 50500 чтений. Это много.





--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Code Magister
Дата 5.6.2005, 19:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



спасибо конечно за прогу, но я хочу разобратъся со своей.
Цитата
А теперь пусть n=1000=10^3. S=(10^6+10^3) / 2 = 10 * (10^5 + 10^2) / 2 = 5 * 10^ 5 + 5 * 10^2 = 50500 чтений. Это много.

можно попроще обьяснить? и как это исправить?
--------------------
PM MAIL WWW ICQ   Вверх
Mayk
Дата 5.6.2005, 21:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(_Slipknot @ 5.6.2005, 19:33)
и как это исправить?

Я привел пример как это делать быстрее.

Цитата(_Slipknot @ 5.6.2005, 19:33)

можно попроще обьяснить


[в прошлом посте недоквотил "в чем тут дело?"]

По-проще это будет "функция fscanf вызывается СЛИШКОМ много раз, этого можно избежать". Или "алгоритм не годится для решения задачи".

Те вычисления показывают что значит слово "СЛИШКОМ" и объясняют почему
алгоритм не годится:
Большую часть времени программа из первого листинга бегает по файлу туда-сюда, хотя этого можно избежать. Медленная работа вызвана выбором далеко не лучшего алгоритма.
Кол-во fscanfов(кстати fgets() подошел бы больше) есть квадратичная функция
(это показано в вычислениях).
Программа из второго листинга бегает гораздо реже(и там кол-во вызовов fseek() есть линейная функция, которая кстати от кол-ва строк зависит лишь косвенно).


Графики функций ax^2 и kx представляешь?








--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
__Sergey__
Дата 6.6.2005, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 67
Регистрация: 21.1.2005

Репутация: нет
Всего: 4



Пример от Рихтера

Присоединённый файл ( Кол-во скачиваний: 7 )
Присоединённый файл  17_FileRev.rar 5,32 Kb
PM MAIL   Вверх
Code Magister
Дата 6.6.2005, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



Mayk а что от того, что она много бегает по файлу,она зависает чтоли?
--------------------
PM MAIL WWW ICQ   Вверх
Mayk
Дата 6.6.2005, 20:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(_Slipknot @ 6.6.2005, 18:15)
Mayk а что от того, что она много бегает по файлу,она зависает чтоли?

Она от этого медленно работает. А зависает от... (копаюсь)... Хм, по-моему она не зависает. Просто медленно работает, можно делать printf("%d",c); чтоб не скучно было.

Теперь к дупам:
Вот в 30 строчке есть
Код

for(int i=1; i<c; i++) fscanf(f,"%s",str);

Ну если особо не вдаваться в подробности - вот здесь int заменить на unsigned long int.

Кстати, только сейчас заметил, прога переворачивает слова, а не строки, а в комментариях указаны строки... И еще - cin >> way; куда указывает way?

Это сообщение отредактировал(а) Mayk - 6.6.2005, 20:45


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Code Magister
Дата 6.6.2005, 23:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



Цитата(Mayk @ 6.6.2005, 20:42)
Она от этого медленно работает. А зависает от... (копаюсь)... Хм, по-моему она не зависает.

Зависает, зацикливается на 32768 строчке. Если б проблема была только в быстродействии - для меня это не была б проблема.
Цитата(Mayk @ 6.6.2005, 20:42)
Ну если особо не вдаваться в подробности - вот здесь int заменить на unsigned long int.

Точно, с long, а i int - ща исправим.
Цитата(Mayk @ 6.6.2005, 20:42)
Кстати, только сейчас заметил, прога переворачивает слова, а не строки, а в комментариях указаны строки...

Прога переворачивает строки. 1-я строчка станет последней, а последняя первой. Сами строчки при этом не изменятся
Цитата(Mayk @ 6.6.2005, 20:42)
И еще - cin >> way; куда указывает way?

way - это путь файла, который мы будем переворачивать.
Добавлено @ 23:38
Mayk да, проблема была именно в типе i. Кажется теперь работает, хоть и медленно.
Спасибо за помощь!
--------------------
PM MAIL WWW ICQ   Вверх
Code Magister
Дата 7.6.2005, 12:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



А насчет твоей проги. Так она переворачивает только первые 128 строк.
Как я понял она считывает файл в оперативу.
Я увеличил размер
Цитата(Mayk @ 5.6.2005, 07:32)
char buf[1024]
и она прочитала больше строк. Тогда я увеличил его до char buf[65535]. И она уже продолжительное время работает... Или я не так понял принцип ее работы?
--------------------
PM MAIL WWW ICQ   Вверх
ManiaK
Дата 7.6.2005, 14:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 5
Всего: 29



Цитата(_Slipknot @ 6.6.2005, 23:31)
Зависает, зацикливается на 32768 строчке.

Помножте число на двое и вычтете единицу. Получите 0xFFFF. Это значит, что в какой-то функции происходит переполнение двухбайтовой знаковой переменной.
PM MAIL WWW   Вверх
Mayk
Дата 7.6.2005, 15:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Судя по всему не понял. Приницип я уже пояснял.
Теперь проиллюстрирую работу:
входной файл:
--- a.txt ---
AAAAAA`\n`
BBBBBB`\n`
CCCCCC`\n`
-- eof ---
Размер буфера = 10.
Теперь посмотрим что прочтется из файла.
--- a.txt --
AAAAAA`\n`
BBBB[BB`\n`
CCCCCC`\n`
]
--- eof ---
в квадратных скобках выделено жирным то, что содержится в буфере.
Теперь прочтенные строки пихаем в обратном порядку:
1) \n в конце. Все что справа пихаем в выходной файл и \n. Так как правее нет ничего, то в выходной файл идет только \n (багофича). Заменяем '\n' на 0
2) '\n' после BB. Все что правее пихаем в выходной файл + '\n'. Туда добавляется "CCCCCC\n"
'\n' меняем на 0.
EOL'ов больше нет.

Считываем следующий блок.
Так как в буфере есть два байта то теперь мы считываем не 10 байт, а только 8(таким образом мы прочитаем эту строчку полностью)
--- a.txt --
AAA[AAA`\n`
BBBBBB
]`\n`
CCCCCC`\n`
--- eof ---

В выходной файл идет все что правее '\n'. то есть BBBBBB. Плюс перевод строки.
(кстати, теперь в выходном файле три строки: пустая, CCCCCC, BBBBBB)

В буфере остается 3 байта.
Мы не можем полностью заполнить весь буфер, поэтому считываем так:

--- a.txt --
[AAAAAA]`\n`
BBBBBB`\n`
CCCCCC`\n`
--- eof ---

Выкидываем эту строку в выходной файл. Теперь файл перевернут










--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Code Magister
Дата 7.6.2005, 15:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



Mayk тогда хз че она у меня не работает.
А моя уже 2-й час переворачивает 6-ти метровый файлик. Тише едешь - дальше будешь smile
--------------------
PM MAIL WWW ICQ   Вверх
ManiaK
Дата 7.6.2005, 15:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 5
Всего: 29



Цитата(_Slipknot @ 5.6.2005, 02:09)
for(int i=1; i<c; i++) fscanf(f,"%s",str);


Вот это место. Похоже в вашем компиляторе int - двухбайтовый. Тогда, разумеется, цикл будет вечным. Замените на unsigned long int или, лучше, просто unsigned long (насколько знаю, разницы никакой).
PM MAIL WWW   Вверх
Mayk
Дата 7.6.2005, 16:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



_Slipknot
Какой компилятор используешь?


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Code Magister
Дата 7.6.2005, 16:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



ManiaK почитай посты выше. Я уже нашел ошибку и исправил ее.
А говорим мы сейчас о программе, которую написал Mayk
Добавлено @ 16:04
Mayk BC++ 3.1
--------------------
PM MAIL WWW ICQ   Вверх
Code Magister
Дата 7.6.2005, 16:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Magister of Code
*


Профиль
Группа: Участник
Сообщений: 155
Регистрация: 9.7.2004
Где: Kiev

Репутация: 1
Всего: 1



Я откомпилил твою прогу на билдере(6-м) - совсем по-другому работает(почти правильно).
--------------------
PM MAIL WWW ICQ   Вверх
Mayk
Дата 7.6.2005, 16:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Итак, ChangeLog для BC3.1
long pos; заменяем на unsigned long pos;
int len заменяем на long len
fread(buf, len, 1, fin); заменяем на len=fread(buf,1,len,fin);
fseek(fin, -len, SEEK_CUR); на fseek(fin, pos-len, SEEK_SET);
printf("%x",pos); на printf("%lx",pos);

Это сообщение отредактировал(а) Mayk - 7.6.2005, 16:58


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Mayk
Дата 7.6.2005, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Так... Теперь функция работает но весьма странно.
Получается интересная бага:
fseek(fin,pos-len,SEEK_SET);
устанавливает стрим на нужную позицию(то есть если pos=121, len=80, то ftell(fin) дает 41, errno = 0)
Зато
len=fread(buf,1,len,fin)
Возвращает 76 smile
Под VS2003 все работает нормально... Под gcc тоже. Так как у нас есть 2 компилятора под которым это работает и 1 под которым не работает то мы можем сделать вывод о том, что где-то в bc есть бага.


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Mayk
Дата 7.6.2005, 23:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



УРА! Все, разобрался в чем дело. Дело оказывается в том, что в DOS строки заканчиваются двумя байтами. Отсюда описанная выше проблема с fseek/fread - fread читает все 80 байт, но возвращает 76 потому что 4 байта - это четыре байта '\r', которых в текстовом файле с точки зрения програмы быть не должно. Для того, чтобы эта священная война CR/LF vs CR оставила нас в покое мы делаем следующее:

1) Входной файл надо открывать КАК БИНАРНЫЙ(fopen("","rb");), а не как текстовой. Таким образом fread не будет пудрить мозги неправильным результатом.
2) Выходной файл по-прежнему открываем как текстовой, чтобы все CR/LFы были в порядке
3) Когда мы находим '\n' мы зануляем его и следующий за ним символ, если он является '\r' и выводим строку в выходной файл со следующего за ним символа. Также мы зануляем предыдущий символ, если он является '\r' (здесь надо не забыть про то, что "\r\n" могут быть разорваны буфером)


Теперь это работает даже в Borland C 3.1
Правда не совсем понятно почему под визуалом текстовой файл окончивающийся вроде дуплетом был удачно перевернут, ну да ладно.


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
ManiaK
Дата 9.6.2005, 10:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 5
Всего: 29



_Slipknot Что-то я ослеп что ли... smile
PM MAIL WWW   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0954 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.