Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Как прочитать текстовый файл целиком


Автор: Bugmaker 31.5.2009, 14:52
Добрый день.
Задача следующая:
Написать программу, которая считывает текст из файла и выводит предложения, которые не содержат запятых.

Разобрался как работать с ReadLn, но загвоздка в том, что предложение может начиниться на одной строке, а заканчиватьяс на другой.

Как прочитать весь файл целиком в массив, или, возможно есть другйо способ разделить файл на предложения...

Код

var
  Form1: TForm1;
   var f : TextFile; // îáúÿâëåíà ôàéëîâàÿ ïåðåìåííàÿ f òèïà TextFile
   myChar  : Char;
   i: integer;
   charArray  : Array of Char;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin

memo1.lines.loadfromfile('C:/2.txt');
AssignFile( f , 'C:/2.txt');
Reset(f);

While not(eof(f)) do // ïîêà ôàéë íå êîí÷èòñÿ âûïîëíÿòü öèêë
begin



Read ( f, myChar); // ïîñèìâîëüíîå ÷òåíèå
i:=i+1;
charArray[i]:= myChar;

end;


end;



end.



Пытаюсь сделать так, но получаю ошибку. Насколько я понимаю ошибука обращения к памяти  smile 

Автор: kami 31.5.2009, 19:03
Код

function FileOfferCounter:integer;
var
  f:TFileStream;
  s, offer:string;
  StartPos, EndPos:integer; // начальная и конечная позиции предложения
  Counter:integer;
begin
  // в этом коде есть ряд недостатков.
  // например, не проверяется, есть ли такой файл вообще.
  // и так же - не является ли он пустым.

  f:=TFileStream.Create('c:\setup.log', fmOpenRead);
  SetLength(s, f.Size);
  f.Read(s[1], Length(s));

  EndPos:=0;
  Result:=0;
  While EndPos<Length(s) do
    begin
      StartPos:=EndPos+1;
      EndPos:=PosEx('.', s, StartPos);
      if EndPos=0 then
        break;
      offer:=Copy(s, StartPos, EndPos-StartPos+1);
      //Здесь offer - это предложение, от первого его символа до последнего, включая точку.
      // с ним (предложением) можно сделать все, что угодно - например, подсчитать количество слов, запятых и т.д.    
      // просто наличие/отсутствие запятых определяется функцией pos.
      inc(Result); // ну а я в своем примере просто подсчитаю количество предложений.
    end;
  f.Free;
end;

Автор: Bugmaker 31.5.2009, 19:16
Не определены Result и PosEx....
Они должны быть integer?

Извиняюсь.. вижу что объявлены.. компилятор выдает
"Undeclared identifier: 'Result'"
"Undeclared identifier: 'PosEx"


Автор: Данкинг 31.5.2009, 19:29
Цитата(Bugmaker @  31.5.2009,  20:16 Найти цитируемый пост)
"Undeclared identifier: 'Result'"

Ну так объяви его.

Автор: Bugmaker 31.5.2009, 19:32
Пишу на делфи билдере 7

вот весь код
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  f:TFileStream;
  s, offer:string;
  StartPos, EndPos:integer; // íà÷àëüíàÿ è êîíå÷íàÿ ïîçèöèè ïðåäëîæåíèÿ
  Counter:integer;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin

  f:=TFileStream.Create('c:\2.txt', fmOpenRead);
  SetLength(s, f.Size);
  f.Read(s[1], Length(s));

  EndPos:=0;
  Result:=0;
  While EndPos<Length(s) do
    begin
      StartPos:=EndPos+1;
      EndPos:=PosEx('.', s, StartPos);
      if EndPos=0 then
        break;
      offer:=Copy(s, StartPos, EndPos-StartPos+1);
      //Çäåñü offer - ýòî ïðåäëîæåíèå, îò ïåðâîãî åãî ñèìâîëà äî ïîñëåäíåãî, âêëþ÷àÿ òî÷êó.
      // ñ íèì (ïðåäëîæåíèåì) ìîæíî ñäåëàòü âñå, ÷òî óãîäíî - íàïðèìåð, ïîäñ÷èòàòü êîëè÷åñòâî ñëîâ, çàïÿòûõ è ò.ä.    
      // ïðîñòî íàëè÷èå/îòñóòñòâèå çàïÿòûõ îïðåäåëÿåòñÿ ôóíêöèåé pos.
      inc(Result); // íó à ÿ â ñâîåì ïðèìåðå ïðîñòî ïîäñ÷èòàþ êîëè÷åñòâî ïðåäëîæåíèé.
    end;
  f.Free;
end;

end.



Сорри комментарии будут крокозябами %) НО они есть в посте kami.

Насколько я вижу вот тут:
var
  Form1: TForm1;
  f:TFileStream;
  s, offer:string;
  StartPos, EndPos:integer; // íà÷àëüíàÿ è êîíå÷íàÿ ïîçèöèè ïðåäëîæåíèÿ
  Counter:integer;
implementation

Они уже объявлены, где еще это нужно сделать?

Автор: Данкинг 31.5.2009, 20:45
Цитата(Bugmaker @  31.5.2009,  20:32 Найти цитируемый пост)
Сорри комментарии будут крокозябами

Русскую раскладку включи перед копированием текста.

Цитата(Bugmaker @  31.5.2009,  20:32 Найти цитируемый пост)
Они уже объявлены,

Извиняюсь, ступил: result - это же к функции относится. А PosEx где объявлена? Это, видимо, функция какого-то модуля.

Автор: Bugmaker 31.5.2009, 21:15
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
   var f : TextFile;
   myChar  : Char;
   i,i1,s: integer;
   myString : array of string;
   z: Array of integer;


   charArray  : Array of Char;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  i:=0;
  i1:=0;
memo1.lines.loadfromfile('C:/2.txt');
AssignFile( f , 'C:/2.txt');
Reset(f);

 SetLength(charArray,100000);
 SetLength(z,100000);

    While not(eof(f)) do
    begin

    Read ( f, myChar);
    i:=i+1;
    charArray[i]:= myChar;
    Memo2.Lines.Add(myChar);
    i1:=i;
    end;

    for i:=0 to i1 do
    begin

    if charArray[i]='.' then
        begin
        ShowMessage('to4ka');
        s:=s+1;
        z[s]:=i;
        end;

    end;

end;


end.


Вобщем вот такой код  разбивающий весь файл на char'ы и запоминающий номер точек.. подскажите как теперь из этих char'ов собрать обратно нужные предложения)

Автор: kami 31.5.2009, 21:26
Цитата(Bugmaker @  31.5.2009,  19:16 Найти цитируемый пост)
"Undeclared identifier: 'Result'"

Я привел код функции.
И Result - это результат этой функции.
Но! это код для подсчета количества предложений. Всего-то нужно добавить в него одну строку (условный оператор), чтобы получить выполнение задачи
Цитата(Bugmaker @  31.5.2009,  14:52 Найти цитируемый пост)
выводит предложения, которые не содержат запятых.




Цитата(Bugmaker @  31.5.2009,  19:16 Найти цитируемый пост)
"Undeclared identifier: 'PosEx"

Кнопку F1 уже отменили?
uses StrUtils;

Автор: Bugmaker 31.5.2009, 21:46
kami огромнейшее спасибо, разобрался)

Автор: kami 31.5.2009, 21:47
Цитата(kami @  31.5.2009,  21:26 Найти цитируемый пост)
 Всего-то нужно добавить в него одну строку (условный оператор)

Извините - 2 строки:
условный оператор и вывод предложения,  не содержащего запятых.

Автор: Bugmaker 3.6.2009, 21:57
Возникла еще пара вопросов... первый в том, что предложение может оканчиваться вопросительным и восклицательным знаком а не точкой...
Второй - запятая может быть в дробном числе.. как учесть эти моменты? 
Чешу репу, ковыряюсь но надежды на меня мало... Помогите пожалуйста)

У меня почему то такое ощущение что все придется переделывать ... не используя PosEx а перебирая каждый символ (

На данный момент код такой:

Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, StrUtils;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
    f:TFileStream;
  s, offer:string;
  StartPos, EndPos:integer; // íà÷àëüíàÿ è êîíå÷íàÿ ïîçèöèè ïðåäëîæåíèÿ
  Counter, Result:integer;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
        f:=TFileStream.Create('c:\2.txt', fmOpenRead);
  SetLength(s, f.Size);
  f.Read(s[1], Length(s));

  EndPos:=0;
  Result:=0;
  While EndPos<Length(s) do
    begin
      StartPos:=EndPos+1;
      EndPos:=PosEx('.', s, StartPos);
      if EndPos=0 then
        break;
      offer:=Copy(s, StartPos, EndPos-StartPos+1);
        if Pos(',',offer)=0 then Memo1.Lines.Add(offer);

      inc(Result);
    end;
  f.Free;

end;

end.




Думаю стоит перейти к такой вот форме..
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, StrUtils;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  var f : TextFile;
  myChar : char;



implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
AssignFile( f , 'C:/2.txt');
Reset(f);

  while not eof(f) do
     begin
        Read ( f, myChar);
    if myChar='.' then          // тут действия если встречается точка
        begin


          ShowMessage('ss');
        end;
    if myChar='!' then          // тут действия если встречается восклицательный знак
        begin
          ShowMessage('ss');
        end;
    if myChar='?' then          // тут действия если встречается вопросительный знак
        begin
          ShowMessage('ss');
        end;
        Memo1.Lines.Add(myChar);
     end;

CloseFile(f);


end;

end.




Автор: kami 3.6.2009, 22:22
Продолжаем.
Я не зря сказал, что когда найден offer, с ним можно сделать все, что угодно.

С учетом вышесказанного (предложение может заканчиваться вопросительным и восклицательным знаком) дополним код:

Код

While EndPos<Length(s) do
    begin
      StartPos:=EndPos+1;
      EndPos:=PosEx('.', s, StartPos);
      if EndPos=0 then
        break;
      offer:=Copy(s, StartPos, EndPos-StartPos+1);
        if Pos(',',offer)=0 then Memo1.Lines.Add(offer);

      inc(Result);
    end;


Это исходное положение.
Изменяем поиск EndPos.

Код

function FindEndOffer(StartPos:integer; Str:string):integer;
var
   TmpResult:integer;
begin
  TmpResult:=PosEx('.', str, StartPos);
  Result:=TmpResult;
  // изначальное положение. Найдена (а может и нет) точка
  if Result=0 then
    Result:=Length(str); // точка не найдена. Для дальнейшей нормальной работы функции
  // определяем, что "позиция точки" - в конце всего текста.

  TmpResult:=PosEx('?', str, StartPos);
  if (TmpResult<>0) and (TmpResult<Result) then // если найден вопросительный знак
    // и он расположен раньше точки, значит предложение заканчивается им.
    Result:=TmpResult;

  TmpResult:=PosEx('!', str, StartPos);
  if (TmpResult<>0) and (TmpResult<Result) then // если найден восклицательный знак
    // и он расположен раньше вопросительного, значит предложение заканчивается им.
    Result:=TmpResult;
end;


Теперь - заменяем строку исходного текста 
Код

EndPos:=PosEx('.', s, StartPos);

на
Код

EndPos:=FindEndOffer(StartPos, s);


Сейчас напишу и про расширенный поиск запятых.

Добавлено через 2 минуты и 6 секунд
Кстати - из-за новой введенной функции текст от последней точки(вопросительного/восклицательного знака) до конца текста (даже если последний символ в тексте не является точкой/знаком) будет считаться предложением.

Автор: kami 3.6.2009, 22:40
Дополняем код после 
Цитата(kami @  3.6.2009,  22:22 Найти цитируемый пост)
offer:=Copy(s, StartPos, EndPos-StartPos+1);

Учитываем, что запятая может быть в дроби.
Определяем, что дробь - это последовательность символов, включающая запятую, слева и справа от которой расположены цифры.

Код

function IsValidComma(str:string; cPos:integer):boolean;
begin
  // в этой функции предполагается, что в позиции pos находится именно запятая и ничто иное.
  Result:=True;
  if cPos>1 then
    Result:=Result and not (str[cPos-1] in ['0'..'9']); // предыдущий символ -не цифра
  if cPos<Length(str) then
    Result:=Result and not (str[cPos+1] in ['0'..'9'); // следующий символ - не цифра
end;

function IsValidOffer(offer:string):boolean;
var
  tmpPos:integer;
begin
  Result:=True;
  TmpPos:=0;
  Repeat
    TmpPos:=PosEx(',',offer, TmpPos+1); // проходим по всем запятым в предложении
    if TmpPos<>0 then  // найдена очередная запятая
      if not isValidComma(offer, TmpPos) then // если запятая "неправильная"
        begin
          Result:=False; // то и предложение неправильное
          break;
        end;
  until TmpPos=0; 
end;


Теперь после offer:=Copy....... пишем
Код

if IsValidOffer(offer) then
  Memo1.Lines.Add(offer);


Писал прямо в браузере, мог с логикой немного напутать (в смысле, not не там, где нужно), но это уже оставляю на твоей совести - должен же быть какой-то творческий процесс smile Проходя отладчиком по этим функциям и просматривая значения переменных можно легко найти, где нарушена логика.

Автор: Bugmaker 3.6.2009, 22:55
Result:=Result and not (str[cPos+1] in ['0'..'9'); // следующий символ - не цифра  --- тут небольшая опечатка но это мелочи..


Вот эту часть не понял
begin
  // в этой функции предполагается, что в позиции pos находится именно запятая и ничто иное.
  Result:=True;
  if cPos>1 then
    Result:=Result and not (str[cPos-1] in ['0'..'9']); // предыдущий символ -не цифра
  if cPos<Length(str) then
    Result:=Result and not (str[cPos+1] in ['0'..'9'); // следующий символ - не цифра
end;

Пока что програамма наоборот не выводит предложения с дробными числами... Зато выводит с обычными запятыми чего делать не должна(

Помоему дело в функции IsValidComma ... 
Не вижу условия при котором она False... 

Автор: kami 3.6.2009, 23:07
Цитата(Bugmaker @  3.6.2009,  22:55 Найти цитируемый пост)
Result:=Result and not (str[cPos-1] in ['0'..'9']);

Рассмотрим отдельно.
1. str[cPos-1] in ['0'..'9']
Если символ, предшествующий запятой находится в рамках от "0" до "9" (то есть является цифрой), то результат выполнения этой части кода будет True. И False - в противном случае.
Рассматриваем полностью эту строку.
2. Берем предыдущий результат and  результат 1).


Цитата(Bugmaker @  3.6.2009,  22:55 Найти цитируемый пост)
Пока что програамма наоборот не выводит предложения с дробными числами

Ну и сделай в последней строке function IsValidComma:
Result:=not Result.
Получишь инвертированный результат работы кода внутри функции и будет выводить другие предложения smile

Автор: Bugmaker 3.6.2009, 23:11
Спасибо с моментом Result:=Result and not (str[cPos-1] in ['0'..'9']); догнал прямо перед тем как ты пояснил....


"Ну и сделай в последней строке function IsValidComma:
Result:=not Result."

Если ты об этом:
Код

function IsValidComma(str:string; cPos:integer):boolean;
begin
  // в этой функции предполагается, что в позиции pos находится именно запятая и ничто иное.
  Result:=True;
  if cPos>1 then
    Result:=Result and not (str[cPos-1] in ['0'..'9']); // предыдущий символ -не цифра
  if cPos<Length(str) then
    Result:=not Result and not (str[cPos+1] in ['0'..'9']); // следующий символ - не цифра
end;



То при таком раскладе все равно не показывает предложений которые с дробными цифрами.. Или я опять не так понял?

Автор: kami 3.6.2009, 23:12
Цитата(Bugmaker @  3.6.2009,  23:11 Найти цитируемый пост)
Если ты об этом:

нет.
Я об этом:

Код

function IsValidComma(str:string; cPos:integer):boolean;
begin
  // в этой функции предполагается, что в позиции pos находится именно запятая и ничто иное.
  Result:=True;
  if cPos>1 then
    Result:=Result and not (str[cPos-1] in ['0'..'9']); // предыдущий символ -не цифра
  if cPos<Length(str) then
    Result:=Result and not (str[cPos+1] in ['0'..'9'); // следующий символ - не цифра
  Result:=not Result; // инвертируем результат.
end;

Автор: Bugmaker 3.6.2009, 23:14
Да... я опять не так понял) спасибо помоему теперь все работает как надо...

Автор: kami 3.6.2009, 23:18
Цитата(Bugmaker @  3.6.2009,  23:14 Найти цитируемый пост)
помоему теперь все работает как надо

Ну теперь у тебя есть полный набор функций для работы по поставленной задаче.
При необходимости остается только расширять набор условий, используемых в них.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)