Otwieranie pliku z ogólną obsługą błędów

Otwieranie pliku z ogólną obsługą błędów
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:minuta
  • Lokalizacja:Tuchów
  • Postów:12165
0

W większości przypadków, gdy potrzebowałem otwierać dowolny plik stosowałem taką, lub podobną konstrukcję:

Kopiuj
var
  fInput: File of Byte;
  wCount: Word;
begin
  AssignFile(fInput, 'C:\File.dat');

  try
    Reset(fInput);
    BlockRead(fInput, wCount, SizeOf(wCount));
  finally
    CloseFile(fInput);
  end;

  ReadLn;
end.

Co oczywiście wyklucza obsługę jakichkolwiek błędów podczas odczytu;


Załóżmy, że potrzebuję otworzyć plik, przy czym sprawdzić, czy faktycznie został otwarty, po czym jeśli operacja powiodła się rozpocząć wczytywanie danych; Jeżeli podczas odczytu danych wystąpi błąd to też go wychwycić; Po wszystkim plik zamknąć i gotowe; Błąd podczas odczytu może się zdarzyć jeśli plik będzie uszkodzony, np. zbyt krótki lub wczytywane dane będą nieprawidłowe, np. odczytuję datę, ale pobrany ciąg danych będzie uszkodzony czy przekłamany, czyli wartość dla daty będzie nieprawidłowa;

Na myśl przychodzi taka konstrukcja:

Kopiuj
var
  fInput: File of Byte;
  wCount: Word;
begin
  AssignFile(fInput, 'C:\File.dat');

  try
    try
      Reset(fInput);

      try
        BlockRead(fInput, wCount, SizeOf(wCount));
      except
        on Exception do
          WriteLn('Read Exception');
      end;
    finally
      CloseFile(fInput);
    end;
  except
    on Exception do
      Write('Reset Exception.');
  end;

  ReadLn;
end.

i wszystko byłoby w porządku (bo jeśli plik nie istnieje bądź jest blokowany przez inny proces wyjątek zostanie poprawnie przechwycony), jednak żeby następnie przechwycić błąd podczas odczytu danych trzeba było dodać kolejny blok try .. except pod Reset, dzięki czemu tworzy się drabinka, choć taka konstrukcja nawet by mnie satysfakcjonowała, bo spełnia założenia;


Można także wykorzystać obsługę błdów I/O i odpowiednio sprawdzić IOResult:

Kopiuj
var
  fInput: File of Byte;
  wCount: Word;
begin
  AssignFile(fInput, 'C:\File.dat');
  {$I-}
  Reset(fInput);
  {$I+}

  if IOResult = 0 then
    try
      try
        BlockRead(fInput, wCount, SizeOf(wCount));
      except
        on Exception do
          WriteLn('Read Exception');
      end;
    finally
      CloseFile(fInput);
    end
  else
    WriteLn('Reset Exception');

  ReadLn;
end.

i też poprawnie zostaje obsłużony błąd otwarcia, a jeśli plik zostanie otwarty poprawnie ale odczyt danych się nie powiedzie - plik zostanie zamknięty a błąd wychwycony i wyświetlony komunikat Read Exception; Taka kontrukcja też spełnia założenia, więc także można by ją wykorzystywać;

Sprawdzam różne konstrukcje i zamykanie pliku w tej sytuacji musi być wykonane jedynie wtedy, gdy IOResult = 0, bo inaczej dostaję nieznany wyjątek programowy; To nie jest dziwne, bo zamkniętego pliku nie można zamknąć;


Moje pytanie brzmi: w jaki sposób poprawnie obsługiwać błędy związane z dostępem do pliku oraz odczytywaniem danych?

Nie potrzebuję rozróżniać nie wiadomo ile błędów, jedynie błąd otwarcia pliku i błąd odczytu danych z pliku; Z racji tej, że na razie nie mam jakiegoś super skutecznego sposobu chciałbym się dowiedzieć w jaki sposób Wy implementujecie obsługę błędów związanych z wczytywaniem danych z pliku; Proszę o przedstawienie Waszych metod, z których korzystacie na co dzień;


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
1

Moje pytanie brzmi: w jaki sposób poprawnie obsługiwać błędy związane z dostępem do pliku oraz odczytywaniem danych?

Wszystkie sposoby które przedstawiłeś są poprawne. Osobiście najczęściej używam wysłużonego {$I-} żeby uniknąć wyjątku albo zostawiam {$I+} i po prostu pozwalam się w takim wypadku programowi wyłożyć na głównym systemie obsługi błędów (jeżeli plik musi dać się otworzyć żeby program poprawnie działał).

Proszę o przedstawienie Waszych metod, z których korzystacie na co dzień;

Kopiuj
procedure TMyPe.Read(s: ansistring);
var
  f:file of boolean;
  i,j,n:integer;
  oldfmode:byte;
const
  MZMagic:word=23117;
  PEMagic:dword=17744;
begin
  oldfmode:=filemode;
  filemode:=0;
  assign(f,s);
  {$I-}reset(f);
  if IOResult<>0 then raise exception.Create('Cannot read file');
  BlockRead(f,doshead,sizeof(doshead),i);
  if i<>sizeof(doshead) then raise exception.Create('Cannot read header!');
  if MZMagic<>doshead.e_magic then raise exception.Create('This file isn''t magic!');
  n:=filepos(f);
  setlength(subdata,doshead.e_lfanew-n);
  BlockRead(f,subdata[0],length(subdata),i);
  //seek(f,doshead.e_lfanew);
  if filepos(f)<>doshead.e_lfanew then raise exception.Create('Subdata read fail');
  BlockRead(f,nthead,sizeof(nthead),i);
  if i<>sizeof(nthead) then raise exception.Create('Cannot read nt header');
  if nthead.Signature<>PEMagic then raise exception.Create('This isn''t valid file for me.');

Jeżeli nie chciałbym aby program używał wyjątków to zrobił bym sobie result z booleanem albo intem i używał exit(-1); i podobnych. Rozwiązanie może i nieidealne ale preferuję programowanie liniowe nad wiele rozgałęzień i długich bloków if. Jeżeli się zastanawiasz czemu np. tutaj nie używam w ogóle finally aby zwolnić pliki etc. to wynika to z tego że błędy przy odczycie plików zazwyczaj traktuję jako non-recoverable error a ta biblioteka jest używana głównie w aplikacjach konsolowych gdzie wychodzenie z exceptionem nie jest niczym wyjątkowym więc nigdy nawet nie przechwytuję tych wyjątków, najwyżej dodam piękny czerwony komunikat.
Generalnie to w moim kodzie rzadko kiedy można się doszukać bloków finally albo except ponieważ wyjątki traktuję jako poważne problemy które raczej nie powinny występować w przypadku poprawnej operacji programu.

_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
1

Wg mnie, poprawnie obsługujesz.
Możesz też całość dać w {$I-} {$I+} (lub tylko BlockRead()/BlockWrite) oraz badać IOResult po BlockRead()/BlockWrite;

Albo zostaw w spokoju mechanizm pascala z epoki kamienia łupanego i przejdź na porządny TFileStream obsługa błędów staje się jednolita.


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:minuta
  • Lokalizacja:Tuchów
  • Postów:12165
0
-321oho napisał(a)

Wszystkie sposoby które przedstawiłeś są poprawne.

Poprawne tak, jednak pierwsze nie posiada obsługi błędów, stąd wszystkimi zajmie się system, a to w ogóle nie wchodzi w grę; Chcę obsługiwać wszystkie błędy jakie mogą powstać w programie i sygnalizować je własnym oknem dialogowym (z odpowiednią informacją);

-321oho napisał(a)

Osobiście najczęściej używam wysłużonego {$I-} żeby uniknąć wyjątku albo zostawiam {$I+} i po prostu pozwalam się w takim wypadku programowi wyłożyć na głównym systemie obsługi błędów (jeżeli plik musi dać się otworzyć żeby program poprawnie działał).

Wymienione dyrektywy to dobra sprawa, bo podobnie do wyjątków pozwalają na rozróżnienie typu błędu, ale że jest ich mnóstwo (sygerując się np. listą) to wolałbym traktować je ogólnie; Bo użytkownikowi nie są potrzebne takie informacje, po co mu wiedzieć czy uchwyt jest właściwy czy nie, czy suma kontrolna się zgadza itd.; Najważniejszą grupę błędów mam wyznaczoną:

  1. plik nie istnieje,
  2. brak dostępu do pliku (plik jest blokowany przez inny proces),
  3. nieprawidłowa budowa pliku,
  4. nieprawidłowy nagłówek,
  5. nieoczekiwany koniec pliku (czyli pętla wczytująca odpowiednie bloki danych zbyt wcześnie natrafia na koniec pliku)
    i to w sumie tyle - uznałem, że tylko takie informacje są potrzebne użytkownikowi do ocenienia przyczyny błędu; Niestety (a może nawet "stety") użytkownicy mogą być totalnymi matołami, więc trzeba było wybrać te najważniejsze, które nawet totalny laik potrafiłby zrozumieć i odpowiednio zareagować;
-321oho napisał(a)

Jeżeli nie chciałbym aby program używał wyjątków to zrobił bym sobie result z booleanem albo intem i używał exit(-1); i podobnych. Rozwiązanie może i nieidealne ale preferuję programowanie liniowe nad wiele rozgałęzień i długich bloków if.

Ja nie mam nic przeciwko wyjątkom i mogę je stosować, jednak wychodzenie Exit'em z parametrem nie wchodzi w grę, bo ustalenie kodu wykonania funkcji zaimplementowane jest wewnątrz niej; A co długich if'ów to mnie nie przeszkadzają, nie mam z nimi w ogóle problemów; Czasem jednak lepiej jest przerywać działanie procedury przez Exit i liniowo sprawdzać różne rzeczy, ale to wszystko zależy od wykonywanych operacji;

_13th_Dragon napisał(a)

Albo zostaw w spokoju mechanizm pascala z epoki kamienia łupanego i przejdź na porządny TFileStream obsługa błędów staje się jednolita.

Taka konstrukcja mi nie przeszkadza, bo nie najgorzej się z niej korzysta, ale chyba już czas zacząć korzystać ze strumieni; Naszkicowałem sobie kod realizujący wczytanie pliku do strumienia i odczytujący jedną liczbę, jednak aby obsłużyć błędy musiałem podobnie jak wcześniej zapisać trzy bloki:

Kopiuj
var
  fsInput: TFileStream;
  wCount: Word;
begin
  try
    fsInput := TFileStream.Create('C:\File.dat', fmOpenRead or fmShareDenyNone);

    try
      try
        fsInput.ReadBuffer(wCount, SizeOf(wCount));
      except
        on Exception do
          WriteLn('Read Exception');
      end;
    finally
      fsInput.Free();
    end;
  except
    on Exception do
      WriteLn('Open Exception');
  end;

  ReadLn;
end.

W takim przypadku gdy plik nie istnieje lub jest pusty wyświelony zostanie komunikat Open Exception, a jeśli błąd spowoduje kopiowanie danych ze strumienia do zmiennych zostanie wyświetlony komunikat Read Exception; Dobrze myślę, czy wypada to zabezpieczyć inaczej?


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 2x, ostatnio: flowCRANE
_13th_Dragon
  • Rejestracja:ponad 19 lat
  • Ostatnio:2 miesiące
1

Poco tak komplikować:

Kopiuj
var
  fsInput: TFileStream;
  wCount: Word;
begin
  try
    fsInput := TFileStream.Create('C:\File.dat', fmOpenRead or fmShareDenyNone);
    fsInput.ReadBuffer(wCount, SizeOf(wCount));
    fsInput.Free();
  except
    on E:EFileCreateError do WriteLn('Nie udało się stworzyć pliku');
    //on E: ....
    on E:Exception do WriteLn('Open Exception '+E.ClassName);
  end;
 
  ReadLn;
end.

Pozostałe rodzaje wyjątków popatrz w źródle lub w dokumentacji lub zwyczajnie metodą eksperymentu, pokaże się nazwa kłasy.


Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
1
Kopiuj
  try
    fsInput := TFileStream.Create('C:\File.dat', fmOpenRead or fmShareDenyNone);
 
    try
      try
        fsInput.ReadBuffer(wCount, SizeOf(wCount));
      except
        on Exception do
          WriteLn('Read Exception');
      end;
    finally
      fsInput.Free();
    end;
  except
    on Exception do
      WriteLn('Open Exception');
  end;

A czemu nie tak?

Kopiuj
try
    fsInput := TFileStream.Create('C:\File.dat', fmOpenRead or fmShareDenyNone);
  except
    on Exception do
      WriteLn('Open Exception');
  end;
 
    try
      try
        fsInput.ReadBuffer(wCount, SizeOf(wCount));
      except
        on Exception do
          WriteLn('Read Exception');
      end;
    finally
      fsInput.Free();
    end;

Oczywiście nie masz tu jeszcze rozpoznania błędu, również on Exception do writeln('zgaduje blad'); jest złe bo równie dobrze może wystąpić jakiś problem którego nie przewidziałeś a ty go zaszufladkujesz jako 'coś'.

Ja nie mam nic przeciwko wyjątkom i mogę je stosować, jednak wychodzenie Exit'em z parametrem nie wchodzi w grę, bo ustalenie kodu wykonania funkcji zaimplementowane jest wewnątrz niej;

Zawsze możesz zrobić procedurę która będzie wywoływać generyczną procedurę i z dokładnych kodów błędów wyłapywać te które nas interesują i je zwracać w odpowiedni sposób. Będzie to rozwiązanie które pozwoli pisać zarówno dokładne procedury co poszło nie tak (nr. błędu) jak i ogólnie (wiadomość odpowiadająca zakresowi numerów).

nieprawidłowa budowa pliku,
nieprawidłowy nagłówek,
nieoczekiwany koniec pliku (czyli pętla wczytująca odpowiednie bloki danych zbyt wcześnie natrafia na koniec pliku)

Pierwszy błąd jest ogólną definicją kolejnych dwóch.
Błąd drugi może wynikać z błędu trzeciego.
Błąd trzeci może wynikać np. z uszkodzonego nagłówka który mimo wszystko przeszedł.

Jeżeli chcesz mieć debiloodporną konstrukcję to po prostu wszystko wrzuć do ogólnej definicji 'plik uszkodzony' bo to że wydaje ci się że plik nie powinien się jeszcze kończyć może równie dobrze oznaczać uszkodzenie nagłówka. Nie lubię zbyt dokładnych opisów błędu bo często wprowadzają w błąd.

Poza tym, właściwie nie wiem do czego Furious zmierzasz. Szukasz najlepszej metody odczytu pliku debilo-odpornie? Bo na początku pytałeś o procedury które się używa najczęściej: Najczęściej używa się jednego, góra dwóch komunikatów błędów o problemie z plikami, do tego można dorzucić jakiś numer który coś znaczy (ale nikt nie wie co).

flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:minuta
  • Lokalizacja:Tuchów
  • Postów:12165
0
-31oho napisał(a)

Oczywiście nie masz tu jeszcze rozpoznania błędu, również on Exception do writeln('zgaduje blad'); jest złe bo równie dobrze może wystąpić jakiś problem którego nie przewidziałeś a ty go zaszufladkujesz jako 'coś'.

Racja, to jest złe podejście, bo wyłapywanie wyjątków na zasadzie on Exception do jest zbyt ogóle i daje możliwości rozpoznania typu błędu i jego przyczyny;

-321oho napisał(a)

Pierwszy błąd jest ogólną definicją kolejnych dwóch.
Błąd drugi może wynikać z błędu trzeciego.
Błąd trzeci może wynikać np. z uszkodzonego nagłówka który mimo wszystko przeszedł.

Ah, za bardzo się rozpędziłem i zdublowałem niektóre... Ogólnie zamierzałem sprawdzić dlaczego nie można poprawnie wczytać danych ze strumienia (lub utworzyć danego strumienia) i przekazać wiadomość użyszkodnikowi; Interesują mnie błędy związane z:

  1. niemożnością otwarcia pliku (obojętne czy nie istnieje, czy jest blokowany),
  2. błędnym nagłówkiem (czyli albo jest uszkodzony, albo to niewłaściwy plik),
  3. nieprawidłową budową (czyli dane są niekompletne lub uszkodzone);
    czyli do pierwszego podchodzi wyjątek EFOpenError, do drugiego własny EInvalidHeader, a do trzeciego EReadError; Kod jakim sprawdzałem wyjątki (selektywnie) poniżej:
Kopiuj
type
  EInvalidHeader = class(Exception);
var
  wCount: Word;
begin
  try
    with TFileStream.Create('C:\File.dat', fmOpenRead) do
    begin
      ReadBuffer(wCount, SizeOf(wCount));

      if wCount <> 0 then
        raise EInvalidHeader.Create('');

      Free();
    end;
  except
    on EFOpenError    do WriteLn('Stream open error');
    on EReadError     do WriteLn('Stream read error');
    on EInvalidHeader do WriteLn('Invalid file header');
    else
      WriteLn('Unknown exception');
  end;

  ReadLn;
end.

A jeśli wystąpi wyjątek inny, niż sprawdzane to wyświetlony zostanie komunikat Unknown exception - w programie zostanie przekazany komunikat np. Wystąpił nieoczekiwany błąd podczas odczytu danych z pliku.;

Nigdy nie obsługiwałem ręcznie (selektywnie) wyjątków, więc musiałem się nauczyć ich obsługi, ale warto było;

-321oho napisał(a)

Nie lubię zbyt dokładnych opisów błędu bo często wprowadzają w błąd.

Ja też nie lubię, a skoro program ma być po części "debiloodporny" to ten wspomniany "debil" musi rozumieć co się do niego mówi, więc szczegółowe opisy błędów nie wchodzą w grę, ale też zbyt ogólne nie mogą być; Tak jak jest - jest dobrze;

-321oho napisał(a)

Poza tym, właściwie nie wiem do czego Furious zmierzasz. Szukasz najlepszej metody odczytu pliku debilo-odpornie? Bo na początku pytałeś o procedury które się używa najczęściej: Najczęściej używa się jednego, góra dwóch komunikatów błędów o problemie z plikami, do tego można dorzucić jakiś numer który coś znaczy (ale nikt nie wie co).

W sumie to miałem dwa cele - poznać Wasze sposoby obsługi błędów i zaimplementowanie w miarę debiloodpornego algorytmu na wypadek problemów z odczytem; I jedno i drugie zostało rozwiązane - wykorzystanie klasy TFileStream, selektywna obsługa wyjątków, w miarę ogólnikowe komunikaty; Dziękuję za odpowiedzi - teraz czas zaimplementować je w programie;


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 2x, ostatnio: flowCRANE
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)