Wycinanie kawałka stringa

0

Witam
Jestem na etapie "początkujący" w delphi więc proszę o wyrozumiałość, postanowiłem trochę popracować nad stringami a konkretnie nad wycinaniem kawałka jakiegoś stringa ale niestety bardzo opornie mi to idzie więc postanowiłem zasięgnąć rady programistów. W memo1 mam tekst o treści "Skopiuj TO oraz !TO TegoNieKopiuj ItegoTezNie" próbuje napisać "aplikację" która skopiuje z memo1 "Skopiuj TO oraz !TO" a reszty nie skopiuje niestety kopiuje całośc czyli "Skopiuj TO oraz !TO TegoNieKopiuj ItegoTezNie" oto kod.

procedure TForm1.Button1Click(Sender: TObject);
begin
Tekst:=Memo1.Text;
if pos('TO',Tekst) > 0 then begin
Memo2.Lines.Add('Skopiowano ' +copy(Tekst,pos('!',Tekst),length(Tekst)));
end;
end;
1

Poczytaj o tych funkcjach i zobacz dokładnie jakie parametry przyjmują.
Copy
Delete
Pos

0

Chodzi o to że "!TO" może być dowolnym tekstem i nie da się przewidzieć jakim ale zawsze będzie zaczynał się od "!" i chodzi mi o to aby nie kopiowało nic co jest poza owym tekstem.

1

Tak to przecież jak pisałem można by się bawić w kilkukrotne użycie Copy, Pos, ew. PosEx (ale do PosEx trzeba dołączyć StrUtils a specjalnie po to się nie opłaca) więc lepiej samemu nasmarować coś w stylu:

var
  sIn, sOut: string;
  i: Integer;
  Ch: Char;
  Stop: Boolean;
begin
  Stop:= False;
  sOut:= '';
  sIn:= 'Skopiuj TO oraz !TO TegoNieKopiuj ItegoTezNie';
  if Length(sIn) > 0 then
  begin
    for i:= 1 to Length(sIn) do
    begin
      Ch:= sIn[i];
      if Stop and (Ch = ' ') then break;
      sOut:= sOut + Ch;
      if Ch = '!' then
        Stop:= True;
    end;
  end;
  ShowMessage(sOut);
end;
0

No to w takim razie: http://regexpstudio.com/TRegExpr/TRegExpr.html - jest dołączona dokumentacja i przkłady. A wyrażenia regularne są opisane dokładnie na mnóstwie stron, łatwych do odnalezionia w Google.

@kAzek mnie ubiegł, ale lepiej według mnie zastosować RegExpy, bo jak coś się zmieni w założoniach, to łatwiej to dostosować w kodzie.

0

Widzę że trochę namieszałem a nie to było moim zamiarem, chodziło mi dokładnie o ten post Formatowanie tekstu jednak pomyślałem że skoro na tamten nikt nie odpowiedział to i tym razem zostanie źle odebrany więc zmieniłem nieco kod dla niepoznaki.

0

Już się pogubiłem. Chcesz aby z "ChanServ JOIN #channel ChanServ test" zostało tylko "#channel" ? Bo jak chcesz aby zostało "ChanServ JOIN #channel" to przecież tak masz tylko musisz mienić "!" na "#".

0

W Tstrings memo1 mam wpisane :IrcOp KICK #test ChanServ test i chciałem aby po odebraniu tej informacji było Chciałem aby było :ChanServ JOIN #test ale niestety kopiuje całego stringa a więc :ChanServ JOIN #test ChanServ test myślę że namieszane jest z tym "length" itd a wystarczyłby pewnie jakiś separator.

1
kAzek napisał(a):

... Czyli jak by było sIn:= 'Skopiuj!TO oraz !SLOWO_KLUCZ TegoNieKopiuj
ItegoTezNie'; zachowa się źle bo skopiuje tylko "Skopiuj !TO" a powinno "Skopiuj !TO oraz !SLOWO_KLUCZ"

Jak autor chce tak jak mówisz to wystarczą drobne przeróbki w twoim kodzie.

var
  sIn,sOut:String;
  i,Stop:Integer;
  Ch:Char;
begin
  sIn:'Skopiuj TO oraz !TO TegoNieKopiuj ItegoTezNie';
  Stop:=0;
  sOut:='';
  for i:=1 to Length(sIn) do
  begin
    Ch:=sIn[i];
    case Stop of
      0: if Ch=' ' then Stop:=1;
      1: if Ch='!' then Stop:=2 else Stop:=0;
      2: if Ch=' ' then Break;
    end;
    sOut:=sOut+Ch;
  end;
  ShowMessage(sOut);
end;
0

@niezalogowany2013 - podaj przykładowe wartości i rezultaty Twojej funkcji (czyli to, co powinna zwracać) wstawiając je jak należy w znaczniki <code>; Mając konkretne dane będzie można podać konkretne rozwiązanie;

Osobiście polecam wskaźniki na znaki (typu PAnsiChar) do wyciągania odpowiednich kawałków łańcuchów (wygoda duża + większa szybkość niż w przypadku funkcji Pos, PosEx, Copy czy Delete);

Sposób ten jest dobry jeśli format danych do obróbki jest w miarę stały, ale i w innych przypadkach też się nadaje; Zaprezentuj ładnie dane, jakie potrzebujesz obrobić, a postaram się o dobry przykład bazujący na wskaźnikach.

0

@Furious Programming zacznę od początku, chodzi o to że pisze bardzo prostego bota który działa jako service na serwerze IRC który mam uruchomione u siebie, zdaje sobie sprawę z tego ze do obsługi protokołu IRC są przeznaczone do tego komponenty jednak ja postanowiłem użyć socketów z racji tego że wtedy jest możliwość napisania wszystkiego od podstaw i nie ma prawie żadnych ograniczeń (w porównaniu z gotowymi komponentami) Oczywiście ustanowiłem połączenie, bot się loguje, odbiera komendy od serwera które wyświetla w memo itp ale chciałbym go nieco rozbudować bo co to za bot który prawie nic nie robi? Najpierw chciałbym aby bot po wyrzuceniu z kanału wszedł na niego ponownie, niezależnie od tego jaki to kanał i w tym celu napisałem taki kawałek kodu.

If pos('KICK',Dane) > 0 then begin
Clientsocket1.Socket.SendText(':ChanServ JOIN #' + copy(Dane,pos('#',Dane) + 1,length(Dane) - pos('#',Dane)) +CRLF);

Niestety kod nie jest idealny i gdy bot odbierze np komendę ":tester KICK #test ChanServ test" to zamiast odpowiedzieć komendą "ChanServ JOIN #test" odpowiada "ChanServ JOIN #test test" czyli kopiuje zbyt wiele ze stringa i rezultat jest taki jak napisałem. Przydałaby się funkcja która wyeliminowałaby ten problem i dodatkowo "zabezpieczała" bota przed wystąpieniem zdefiniowanych słów w stringu na który ma odpowiedzieć, przykład: "AnyUser PRIVMSG #test tester KICK #test ChanServ" gdy bot otrzyma takiego stringa to również będzie próbował wejść na kanał (sprawdziłem snifferem) Podsumowując funkcja miałaby kopiować kawałek stringa a w przypadku gdy w stringu wystąpiły określone słowa nie wykonałaby się. Rozpisałem się a nie wiem czy ktoś w ogóle będzie miał ochotę to czytać także na tym na razie zakończę.

1
function Parse(var Str:String;const Delimeter:String):String;
var P:Integer;
begin
  P:=Pos(Delimeter,Str);
  if P>0 then
  begin
    Result:=Copy(Str,1,P-1);
    Delete(Str,1,P+Length(Delimeter)-1);
  end
  else
  begin
    Result:=Str;
    SetLength(Str,0);
  end;
end;

If pos('KICK',Dane) > 0 then
begin
  Parse(Dane,'#');
  Clientsocket1.Socket.SendText(':ChanServ JOIN #'+Parse(Dane,' ')+CRLF);
end;
2

Ale Ty jesteś uparty, skoro już i tak klepiesz to pod VCL, to dlaczego jak diabeł święconej wody unikasz wyrażeń regularnych? Co innego jakbyś pisał w WinAPI z użyciem Simple TCP na przykład, wtedy rozumiem wszystko robimy sami. Ale jeśli Tobie się nie chce poczytać jakie parametry przyjmuje funkcja Copy, to będziesz błądził po omacku. Nic dziwnego, że kopiuje za wiele danych skoro, bo tak jej każesz.

I w ogóle Twoje podejście do tematu jest złe, najpierw kombiujemy samodzielnie wspierając sie Google. Do bólu. I w ostateczności piszemy na forum. Nieważne, jaki stopień zaawansowania i jaki dział oraz język. Skorzystaj z TBrain w swojej głowie. Smarowanie na forum to ostateczność.

0

@_13th_Dragon Po użyciu Twojej funkcji bot wysyła komendę ":ChanServ JOIN #:tester" ale dzięki za poświęcony czas, może kiedyś będzie okazja abym się odwdzięczył. @olesio przyjąłem do wiadomości i masz jak w banku że nie napisze już ani w dziale delphi ani newbie, jeżeli już to tylko i wyłącznie w dziale praca.

0

Ok, jeśli tak uważasz. Dla mnie to jeśli chcesz bardzo to pisz. Tylko mi chodzi o jedno, dostajesz tutaj konkretne rady. Ba, nawet gotowce kodów, a odnoszę niestety wrażenie, że mimo wszystko nie chcesz z nich skorzystać i nie rozumiem dlaczego. Ponieważ ogarniasz sockety i takie zagadnienia jak protokół serwera IRC, a masz problem z wyeodrębnieniem kawałka stringa z innego w nieskomplikowanym wariancie nazwijmy to danych wejściowych.

1

Pytający tak się odgrażał, że napisał specjalnie jeszcze na unit1. Tak w końcu wiadomo co chce zrobić. Podałem mu tam taki kod, który według mnie robi to co on chce. Pewnie da się prościej, ale ja tak to wykombinowałem. I wspomniałem tam, ze o wiele łatwiej było by RegExprem, ale pytający ma to cnyba w... głębokim poważaniu. Podaje kod tutaj, może komuś się przyda

function GetKickData(DataToParse : string; var AKickedBy, AChannel, AReason : string) : boolean;
const
  Kick_Marker = 'KICK #';
var
  X : integer;
begin
  AChannel := '';
  AReason := '';
  X := Pos(':', DataToParse);
  Result := X = 1;
  if Result then
  begin
    X := Pos(' ', DataToParse);
    Result := X > 0;
    if Result then
    begin
      AKickedBy := Copy(DataToParse, 2, X - 1);
      X := Pos('!', AKickedBy);
      AKickedBy := Copy(AKickedBy, 1, X - 1);
      X := Pos(Kick_Marker, AnsiUpperCase(DataToParse));
      Result := X > 0;
      if Result then
      begin
        AChannel := Copy(DataToParse, X + Length(Kick_Marker) - 1, MaxInt);
        AReason := AChannel;
        X := Pos(' ', AChannel);
        AChannel := Copy(AChannel, 1, X - 1);
        X := Pos(':', AReason);
        AReason := Copy(AReason, X + 1, MaxInt);
      end;
    end
    else
    begin
      AKickedBy := '';
    end;
  end;
end;

procedure TForm1.Button1Click(Sender : TObject);
const
  Source_Data = ':[email protected] KICK #kanalik oles` :testowy powood kopa';
var
  KickedBy, Chn, Rsn : string;
begin
  if GetKickData(Source_Data, KickedBy, Chn, Rsn) then
  begin
    Memo1.Clear;
    Memo1.Lines.Add('Wykopano z kanału: ' + Chn);
    Memo1.Lines.Add('Wykopany przez: ' + KickedBy);
    Memo1.Lines.Add('I powód wykopania: ' + Rsn);
    Clientsocket1.Socket.SendText((':ChanServ JOIN ' + Chn);
  end;
end;
0

Ponieważ ogarniasz sockety i takie zagadnienia jak protokół serwera IRC, a masz problem z wyeodrębnieniem kawałka stringa z innego w nieskomplikowanym wariancie nazwijmy to danych wejściowych.
@olesio Jeżeli chodzi o sockety to coś tam wprawdzie daje radę zrobić ale na poziomie "początkujący" co do protokołu IRC chcąc nie chcąc musiałem swego czasu nauczyć się jak najwięcej w tym temacie ponieważ byłem kimś w rodzaju betatestera na jednym z polskich czatów którego serwery działają pod dyktando protokołu RFC2812 stad tez moje "zamiłowanie" do IRC-a
pytający ma to cnyba w... głębokim poważaniu.
Tutaj też sprawa wygląda nieco inaczej ponieważ walczyłem z bolącym zębem ale oczywiście sprawdziłem funkcje która napisałeś i wszystko działa tak jak powinno za co dziękuje i to samo dotyczy innych osób które poświeciły mi swój czas. Wyrażenia regularne to dla mnie przysłowiowa "czarna magia" i nie miałem z nimi nigdy do czynienia jednak może posłucham rady olesio i zgłębię temat skoro ma to mi zaoszczędzić problemów w przyszłości i pozwolić na uniknięcie sytuacji że ktoś nazwie mnie degeneratem.

0

Spoko, cieszę się, że mogłem pomóc. A bolący ząb to koszmar. Też mnie niedawno nawalał. Także trzeba będzie cudem znaleźć wolny dzień i iść do innego dentysty niż ten do tej pory, od którego pląba ułmała się i z prawej strony musiałem mieć usuwane aż trzy, bo dwa się "zaniedbały" niestety. A długo nic nie bolało to ignorowałem.

Anyway. Na przyszłość radzę poświęcić chwilę na zgłębienie tematu wyrażeń regularnych. To się Tobie prędzej czy później przyda w jakimś kodzie najpewniej. Sam kiedyś lamiłem i googlowałem za funkcjami wycinającymi jak najszybciej z pomiędzy wielu tagów HTMLa potrzebne mi dane. Wtedy też jedna osoba, która już się nie udziela na u1, naprowadziła mnie na ten moduł. Pogooglowałem i ogarnąłem temat. Sorry też może za ostre porównanie, ale nie odpisywałeś, a mimo sugestii do skorzystania z RegExpra, nie ustosunkowałeś się. Także odniosłem wtedy mylne wrażenie, że mimo udzielanych rad masz je za nic i uparcie chcesz zrobić coś po swojemu, tylko niebardzo wiadomo co.

I na koniec jeszcze jedna rada. Zawsze jeżeli pytasz o coś, a nie każdy może zgadnąć co chcesz osiągnąc. To od razu mogłeś napisać tak jak na unit1. A nawet prościej, przykład danych wejściowych i co chcesz uzyskać w rezultacie. Wtedy ktoś już przedemną podał by Tobie podpowiedział skutecznie. A tak trzeba odpalić WireShark i mIRCa, po czym sprawdzić wyniki. Nie jest to wprawdzie aż taki wielki problem. Jednak nie każdemu się chce to zrobić albo wertować RFC IRCowe.

1 użytkowników online, w tym zalogowanych: 0, gości: 1