views:

85

answers:

5

I have text being passed to a function. That text can contain anything, from a single character all the way to a full book.

I need to extract the first like and use it as a "title" so I can name a file where I am saving that text as a backup. I am using the following:

function GetTitle(var Text:string):string;
var
  title: string;
  position: integer;
begin
    title := '';
    position := AnsiPos(#10, Text);

    if position = 0 then
    begin
      position := AnsiPos('.', Text);
      if (position = 0) then
         title := Text
      else
         title := copy(Text, 1, position);
    end
    else
    begin
        title := copy(Text, 1, position);
    end;

    result := title;
end;

I'm checking #10 and not #13 because the text can be either passed from a Windows app or a Mac OS X app. In cases where there is no #10 I'm checking for the 1st . (dot) and if there is none then I am passing the whole thing as a title.
This approach is causing some problems with filenames containing #13 in the name or names being too long. I can add a check for > 256 on title but that's just another thing.

Anyway, is there any way to properly read the first line (including #13#10 or #10 or #13 or . or whatever)?

I know this is simple, but I can't seem to find a way to deal with this without having nested ifs...

code is appreciated. thank you

+3  A: 

hmmm, this one works:

function TForm1.SimpleNoteGetTitle(Text:string):string;
var
  position: integer;
begin
    position := AnsiPos(#13#10, Text);
    if (position = 0) or (position > 50) then position := AnsiPos(#10, Text);
    if (position = 0) or (position > 50) then position := AnsiPos(#13, Text);
    if (position = 0) or (position > 50) then position := AnsiPos('.', Text);
    if (position = 0) or (position > 50) then position := AnsiPos(',', Text);
    if (position = 0) or (position > 50) then position := AnsiPos(';', Text);
    if (position = 0) or (position > 50) then position := AnsiPos('?', Text);
    if (position = 0) or (position > 50) then position := AnsiPos('!', Text);
    if (position = 0) or (position > 50) then position := AnsiPos(' ', Text);

    if position = 0 then
       result := Text
    else
       result := copy(Text, 1, position - 1);
end;

UPDATED version of the function

Uri
+1. Id'get AnsiPos for all of #10, #13 and '.' and then use the smallest of those three. Looking for space is probably not such a good idea.
Cosmin Prund
i am checking for space as well because in the absence of any indicator i'll extract the text up to the 1st space. it's working nicely
Uri
I'd just add for an empty string check at the end, sth. like `if Result = '' then Result := 'Untitled';`.
Sertac Akyuz
there is no such thing. I mean, it won't be allowed even before reaching this code, but thanks for pointing it out
Uri
+2  A: 

This may not be the best way, but it does eliminate the nested if's...

function GetTitle(var Text:string):string; 
var 
  position1: integer; 
  position2: integer; 
  position3: integer; 
  position: integer; 
begin 

    Text := copy(Text,0,255);

    position1 := AnsiPos(#10, Text); 
    position2 := AnsiPos(#13, Text); 
    position3 := AnsiPos('.', Text); 

    if position1 = 0 then position1 := 255;
    if position2 = 0 then position2 := 255;
    if position3 = 0 then position3 := 255;

    position := Min(Min(position1,position2),position3);

    result := copy(Text, 1, position); 

end; 
Fosco
First of all, you should use `Text: string`, not `var Text: string`. You are actually destroying the `Text`! Secondly, you do not need the `title` variable. Simply use `result` all the way.
Andreas Rejbrand
@andreas: thanks for the tips
Uri
+3  A: 

What you're doing sounds like the most you can do given the arbitrary nature of your input, but it's not going to be enough because there is no guarantee that the first sentence in your arbitrary text will be short enough to be a title. You need to add a step to truncate the text if it is longer than some arbitrary length limit, and add ellipsis (three dots ...) to indicate that there is more text.

dthorpe
+1  A: 

I'd tackle the problem with a loop, probably a bit faster than multiple calls to Pos.

function GetTitle(const Text:string):string; 
var 
  I: integer;
begin 
  I := 0;
  while I < Length(Text) do
  begin
    if (Text[I+1] in [#10,#13,'.']) then
      Break;
    Inc(I);
  end;
  Result := Copy(Text,1,I);
end; 
Rob McDonell
let me try it. thanks!
Uri
A: 

Why not extract up to (say) 10 words from the beginning of the string while ignoring illegal file characters such as CR, LF, : . etc if they exist.

and make that the title?

more complex but certain to produce a legal filename

just a thought :)

Despatcher