Jakie warunki muszą być spełnione, aby plik był plikiem tekstowym zdefiniowanym przez POSIX?

22

POSIX definiuje plik tekstowy jako:

Plik zawierający znaki zorganizowane w zero lub więcej wierszy. Wiersze nie zawierają znaków NUL i żaden z nich nie może przekraczać długości {LINE_MAX} bajtów, w tym znaku <nline>. Chociaż POSIX.1-2017 nie rozróżnia plików tekstowych od plików binarnych (patrz standard ISO C), wiele programów narzędziowych generuje przewidywalne lub znaczące wyniki tylko podczas pracy na plikach tekstowych. Standardowe narzędzia, które mają takie ograniczenia, zawsze określają „pliki tekstowe” w swoich sekcjach STDIN lub INPUT FILES.

Źródło: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_403

Jest jednak kilka rzeczy, które są niejasne:

  1. Czy plik tekstowy musi być zwykłym plikiem? W powyższym fragmencie nie jest wyraźnie powiedziane, że plik musi być zwykłym plikiem

  2. Czy plik można uznać za plik tekstowy, jeśli zawiera jeden znak i tylko jeden znak (tj. Pojedynczy znak, który nie jest zakończony znakiem nowej linii)? Wiem, że to pytanie może brzmieć dziwnie, ale używają słowa „znaki” zamiast „jednego lub więcej znaków”. Inni mogą się nie zgadzać, ale jeśli mają na myśli „jedną lub więcej postaci”, myślę, że powinni to wyraźnie powiedzieć

  3. W powyższym fragmencie odnosi się do „linii”. Znalazłem cztery definicje z linią w nazwie: „Pusta linia”, „Wyświetlana linia”, „Niekompletna linia” i „Linia”. Czy mam wnioskować, że mają na myśli „linię” z powodu pominięcia „pustych”, „wyświetlanych” i „niekompletnych” - czy też wszystkie cztery z tych definicji są zawarte jako uważane za wiersz w powyższym fragmencie?

Wszystkie pytania, które pojawiają się po tym bloku tekstu, zależą od wniosku, że „znaki” oznaczają „jeden lub więcej znaków”:

  1. Czy mogę bezpiecznie wywnioskować, że jeśli plik jest pusty, nie jest to plik tekstowy, ponieważ nie zawiera jednego lub więcej znaków?

Wszystkie pytania, które pojawiają się po tym bloku tekstu, zależą od wywnioskowania, że ​​w powyższym fragmencie wiersz jest zdefiniowany jako „Linia” i że należy wykluczyć pozostałe trzy definicje zawierające „Wiersz” w ich nazwie:

  1. Czy „zero” w „zero lub więcej wierszy” oznacza, że ​​plik nadal można uznać za plik tekstowy, jeśli zawiera jeden lub więcej znaków, które nie są zakończone znakiem nowej linii?

  2. Czy „zero lub więcej linii” oznacza, że ​​po wejściu w grę pojedynczej „linii” (0 lub więcej znaków plus kończąca nowa linia), nielegalne staje się, że ostatnia linia jest „niepełną linią” (jedna lub więcej nieukończonych linii) znaki nowego wiersza na końcu pliku)?

  3. Czy „żaden [brak wiersza] nie może przekraczać długości {LINE_MAX} bajtów, w tym znak nowego wiersza” oznacza, że ​​istnieje ograniczenie liczby znaków dozwolonych w dowolnej „linii” w pliku tekstowym (na marginesie wartość LINE_MAX na Ubuntu 18.04 i FreeBSD 11.1 to „2048”)?

Harold Fischer
źródło
Dobre pytanie, Harold! Stanowi świetną dyskusję na temat terminologii. Chciałbym móc jeszcze raz głosować nad pytaniem
Sergiy Kolodyazhnyy

Odpowiedzi:

23
  1. Czy plik tekstowy musi być zwykłym plikiem? W powyższym fragmencie nie jest wyraźnie powiedziane, że plik musi być zwykłym plikiem

    Nie; fragment nawet zwraca uwagę na standardowe wejście jako potencjalny plik tekstowy. Inne standardowe narzędzia, takie jak make, w szczególności wykorzystać ten specjalny plik znakowy /dev/null postaci pliku tekstowego .

  2. Czy plik można uznać za plik tekstowy, jeśli zawiera jeden znak i tylko jeden znak (tj. Pojedynczy znak, który nie jest zakończony znakiem nowej linii)?

    Ten znak musi być <nową linią>, w przeciwnym razie nie jest to linia , więc plik, w którym się znajduje, nie jest plikiem tekstowym. Plik zawierający dokładnie bajt 0A to jednowierszowy plik tekstowy. Pusta linia jest prawidłową linią.

  3. W powyższym fragmencie odnosi się do „linii”. Znalazłem cztery definicje z linią w nazwie: „Pusta linia”, „Wyświetlana linia”, „Niekompletna linia” i „Linia”. Czy mam wywnioskować, że mają na myśli „linię” z powodu pominięcia „pustych”, „wyświetlanych” i „niekompletnych”

    To tak naprawdę nie jest wnioskowanie, to po prostu to, co mówi. Słowo „linia” otrzymało odpowiednią kontekstowo definicję, dlatego właśnie o tym mówi.

  4. Czy mogę bezpiecznie wywnioskować, że jeśli plik jest pusty, nie jest to plik tekstowy, ponieważ nie zawiera jednego lub więcej znaków?

    Pusty plik składa się z zerowych (lub więcej) linii i dlatego jest plikiem tekstowym.

  5. Czy „zero” w „zero lub więcej wierszy” oznacza, że ​​plik nadal można uznać za plik tekstowy, jeśli zawiera jeden lub więcej znaków, które nie są zakończone znakiem nowej linii?

    Nie, te znaki nie są zorganizowane w linie.

  6. Czy „zero lub więcej linii” oznacza, że ​​po wejściu w grę pojedynczej „linii” (0 lub więcej znaków plus kończąca nowa linia), nielegalne staje się, że ostatnia linia jest „niepełną linią” (jedna lub więcej nieukończonych linii) znaki nowego wiersza na końcu pliku)?

    To nie jest nielegalne , to po prostu plik tekstowy. Narzędzie wymagające podania pliku tekstowego może zachowywać się niekorzystnie, jeśli zamiast tego otrzyma ten plik.

  7. Czy „żaden [brak linii] nie może przekraczać długości {LINE_MAX} bajtów, w tym znak nowego wiersza” oznacza, że ​​istnieje ograniczenie liczby znaków dozwolonych w dowolnej „linii” w pliku tekstowym

    Tak.

Ta definicja próbuje po prostu ustalić pewne granice tego, co narzędzie tekstowe ( na przykładgrep ) na pewno zaakceptuje - nic więcej. Mają też swobodę akceptowania rzeczy bardziej liberalnie i dość często robią to w praktyce. Mogą używać bufora o stałym rozmiarze do przetwarzania linii, aby założyć, że nowa linia pojawia się przed zapełnieniem i tak dalej. Być może za dużo czytasz w rzeczy.

Michael Homer
źródło
1
Czy jesteś pewien punktu 2? Standard wyraźnie określa „ 0 lub więcej wierszy”. Utworzyłby więc printf "a" > fileplik tekstowy zgodnie z tą definicją. Twoja odpowiedź na 4 wydaje się być sprzeczna z twoimi odpowiedziami na 2 i 5, ponieważ sugerujesz, że touch filetworzy plik tekstowy, podczas gdy printf "a" > filenie.
terdon
4
@terdon: Nie widzę żadnej sprzeczności w odpowiedzi Michaela. Zasadniczo wydaje się, że mówi, że plik tekstowy POSIX to każdy plik, którego zawartość jest zgodna z wyrażeniem regularnym (.{0,M}\n)*(domyślnie zakotwiczonym i na obu końcach), w którym \npasuje do nowej linii i .pasuje do dowolnego znaku, który nie jest nową linią, i Mjest symbolem zastępczym dla wartości liczbowej LINE_MAX-1. W szczególności oznacza to, że pusty plik jest prawidłowym plikiem tekstowym składającym się z zerowych linii, ale każdy niepusty plik tekstowy musi kończyć się nową linią (ponieważ w przeciwnym razie zawierałby niekompletną linię, a niekompletna linia nie jest linią ).
Ilmari Karonen
@Michael Homer Jeśli chodzi o zwykły plik, czy istnieją inne przykłady oprócz / dev / null? To nie jest tak naprawdę plik tekstowy, ponieważ zawiera jeden lub więcej znaków zerowych.
Harold Fischer
1
@HaroldFischer /dev/nulljest pustym plikiem. Myślisz o /dev/zero.
Michael Homer
@HaroldFischer, nie, /dev/nullczyta jako puste, ponieważ nie dostajesz żadnych danych podczas czytania. Nie jestem pewien, czy warto tutaj uwzględniać nieregularne pliki, ponieważ wiele z nich ma charakter dynamiczny. Dotyczy to rur, gniazd, urządzeń char, które są po prostu transportem interfejsów do / z jakiegoś innego obiektu. Nie przechowują żadnego statycznego zestawu danych, więc rozsądniej byłoby rozważyć właściwości przesłanych danych zamiast właściwości pliku .
ilkkachu
7

Zgodnie z definicją POSIX:

Tak, plik tekstowy to (w zasadzie):

Plik zawierający znaki zorganizowane w zero lub więcej wierszy.

Przydatne byłoby również włączenie tych definicji:

3,92 Ciąg znaków

Ciągła sekwencja znaków zakończona pierwszym bajtem zerowym włącznie.

3.195 Niekompletna linia

Sekwencja jednego lub więcej znaków innych niż <lineline> na końcu pliku.

3,206 linii

Sekwencja zero lub więcej znaków innych niż <lineline> oraz kończący znak <lineline>.

3.243 Znak nowej linii (<nowa linia>)

Znak, który w strumieniu wyjściowym wskazuje, że drukowanie powinno rozpocząć się na początku następnego wiersza. Jest to znak oznaczony przez „\ n” w języku C. Nie jest określone, czy ten znak jest dokładną sekwencją przesyłaną do urządzenia wyjściowego przez system w celu wykonania przejścia do następnej linii.

3.247 NUL

Znak ze wszystkimi bitami ustawionymi na zero.

Zauważ, że „plik tekstowy” nie może zawierać bajtów NUL.


Więc:

  1. Czy plik tekstowy musi być zwykłym plikiem?
    Nie, nie musi tak być. „Plik tekstowy” jest definiowany w kategoriach tego, co zawiera podczas odczytu. Jeśli plik zawiera „zero lub więcej wierszy”, jest to plik tekstowy. Niektóre pliki, na przykład /dev/stdin, mogą zawierać plik tekstowy, jeśli zostaną odczytane za jednym razem, a nie następnym razem.
  2. Czy plik można uznać za plik tekstowy, jeśli zawiera tylko jeden znak i jeden znak…?
    Nie, to niepełna linia (3.195).
    Plik tekstowy może zawierać tylko „Niekompletne linie”.
  3. Czy mam wywnioskować, że mają na myśli „Line”…?
    Tak, powinieneś.
  4. Czy mogę bezpiecznie wywnioskować, że jeśli plik jest pusty, nie jest to plik tekstowy…?
    Nie, pusty plik (zero znaków) jest prawidłowym „plikiem tekstowym”.
    Z góry: ... zero lub więcej linii ... . Zero linii (zero znaków) jest prawidłowym „plikiem tekstowym”.
  5. … Rozważany plik tekstowy, jeśli zawiera jeden lub więcej znaków, które nie są zakończone znakiem nowej linii?
    Nie, „Niekompletna linia” nie jest (technicznie) prawidłową „linią”.
  6. Czy „zero” w „zero lub więcej wierszy” oznacza, że ​​plik nadal można uznać za plik tekstowy, jeśli zawiera jeden lub więcej znaków, które nie są zakończone znakiem nowej linii?
    Nie, niepełna linia nie jest „linią”. Plik tekstowy nie może zawierać niekompletnych wierszy.

  7. … Istnieje ograniczenie liczby znaków dozwolonych w dowolnej „linii” w pliku tekstowym…?
    Tak, nie może być więcej niż {LINE_MAX} bajtów (w przeciwieństwie do znaków) w dowolnym wierszu prawidłowego „pliku tekstowego”.
    Wartość {LINE_MAX} jest podana w pliku <limit.h>
    (czytaj także Rozsądny rozmiar bufora linii w C? ):

    {LINE_MAX}
    O ile nie zaznaczono inaczej, maksymalna długość wiersza wejściowego narzędzia (w standardzie lub innym pliku), w bajtach, gdy narzędzie jest opisywane jako przetwarzanie plików tekstowych. Długość obejmuje miejsce na spływ.
    Minimalna dopuszczalna wartość: {_POSIX2_LINE_MAX}

    W przypadku systemu opartego na GNU nie ma ustalonego limitu (oprócz pamięci) :

    Makro: int LINE_MAX
    Największa linia tekstu obsługiwana przez tekstowe narzędzia POSIX.2. (Jeśli używasz wersji GNU tych narzędzi, nie ma rzeczywistego limitu, z wyjątkiem limitu narzuconego przez dostępną pamięć wirtualną, ale biblioteka nie może tego powiedzieć).

    Wygląda na to, posix_lim.hże jest to 2048 (przynajmniej dla 64-bitowych systemów GNU Linux):

    $ grep -ri 'POSIX2_LINE_MAX' /usr/include/ 
    
    /usr/include/x86_64-linux-gnu/bits/xopen_lim.h:#define NL_LANGMAX       _POSIX2_LINE_MAX
    /usr/include/x86_64-linux-gnu/bits/posix2_lim.h:#define _POSIX2_LINE_MAX                2048
    /usr/include/x86_64-linux-gnu/bits/posix2_lim.h:#define LINE_MAX                _POSIX2_LINE_MAX
    

    Można go również znaleźć za pomocą narzędzia getconf POSIX :

    $ getconf LINE_MAX
    2048
    

Powiązane: Dlaczego pliki tekstowe powinny kończyć się nową linią?

Izaak
źródło
2
Ta odpowiedź jest w większości poprawna, ale prawidłowa odpowiedź na „plik tekstowy musi być zwykłym plikiem” to nie . Każdy plik może być plikiem tekstowym, to kwestia zawartości, typ pliku jest nieistotny. fileNarzędzie zgłasza tylko typ pliku dla plików specjalnych, ale to tylko jak prace użytkowych, użycie file - <…lub (Linux) file -s …, aby zobaczyć jego heurystyki na zawartości pliku na specjalnym pliku. Plik specjalny może mieć różną zawartość za każdym razem, gdy go otwierasz, więc może być plikiem tekstowym za każdym razem. /dev/nulljest zawsze plikiem tekstowym, ponieważ jego zawartość jest zawsze plikiem tekstowym.
Gilles „SO- przestań być zły”
1
Zamiast używać grepna plikach, możesz użyć, getconfaby uzyskać systemowe wartości konf, np. getconf LINE_MAXKtóre przy okazji zwracają 2048 (bajtów) w moim systemie (Ubuntu 16.04).
heemayl
Chciałem znaleźć plik, w którym zmienna została zdefiniowana, dlatego grep był konieczny i wykonałem zadanie (dość szybko). Ale tak, getconfpozwala odczytać aktualną wartość config.
Izaak