Ako ukladáte binárne dáta a texty v C++?

Ako ukladáte binárne dáta a texty v C++?
« kdy: 03. 10. 2020, 21:34:20 »
Dobrý deň, C++ ukladá textové dáta v std::string-och čo je wrapper nad char*, no keďže tieto stringy sú null terminated, nie sú vhodné na ukladanie binárných dát (ale aj textov z iných vývojárskych platforiem), ktoré môžu obsahovať aj znak '\0'. Takže na bežné účely používam std::string a std::wstring, ale na špeciálne účely je potrebné použiť iný typ.

Aké úložisko sa teda podľa Vás hodí na ukladanie všetkých ASCII znakov?

Ja som používal:

Kód: [Vybrat]
typedef std::vector<char> bineries;
Čo je vlastne pascal-like spôsob. Lebo reťazec sa neukončujeme žiadnym znakom, ale si bokom uložíme jeho velkosť a podľa toho vieme kde končí buffer so znakmi. Nevýhoda je že na ukladanie veľkosti stringu spotrebujeme 4 znaky (typ int). A ak má string dajme tomu 2 znaky tak je to veľmi nešetrné. Teraz som si, ale uvedomil že možno skôr std::vector<unsigned char> zodpovedá tomu čo očakávame od takéhoto poľa znakov. Pretože keď si pozrieme ascii tabuľku, tak v nej nevidíme znaky s mínusovým poradovým číslom. Z pohľadu aplikácie je to síce jedno, ale ešte som nevidel ASCII tabuľku ktorá by nebola indexovaná od nuly.

A potom mám ešte:

Kód: [Vybrat]
typedef std::vector<wchar_t> wbinaries;
pre 16bit wide chars. Teda pre texty, ktoré sú multibyte, no zároveň nechceme aby boli ukončované znakom '\0'.

Okrem toho sa na binárne dáta hodia aj streamy.

Takže otázka... aký typ používate vy (v C++) na ukladanie binárnych, alebo textových dát, bez obmedzení klasického stringu?
« Poslední změna: 03. 10. 2020, 21:38:42 od fortran1986 »


Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #1 kdy: 03. 10. 2020, 22:05:02 »
pouzij klasicke C-like ukladanie:

char[] pole = {0x01, 0x02, 0x03}
char* string = "\x0A\x0D\0x61\x00";

Kit

  • *****
  • 704
    • Zobrazit profil
    • E-mail
Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #2 kdy: 03. 10. 2020, 22:16:42 »
Na binární data bych použil délku+posloupnost bajtů. Pokud tě trápí 6 bajtů kvůli dvěma znakům, můžeš tu délku zakódovat třeba do UTF-8. Do 127 bajtů tak budeš mít režii pouze 1 bajt.

Pro praktické účely bych však sáhl po SQLite, které tohle vše má už v sobě a řeší i modifikace. Pokud to má být přenositelné, tak bych pro texty a čísla použil XML.

Můžeš zkusit i formát BSON.

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #3 kdy: 04. 10. 2020, 00:21:43 »
pouzij klasicke C-like ukladanie:

char[] pole = {0x01, 0x02, 0x03}
char* string = "\x0A\x0D\0x61\x00";

Jo aj to je tiež jedna z možností (pohľadu pamate najšetrnejšia) a tiež ju z času na čas používam. Ale na niektoré use cases by som chcel niečo flexibilnejšie.

Na binární data bych použil délku+posloupnost bajtů. Pokud tě trápí 6 bajtů kvůli dvěma znakům, můžeš tu délku zakódovat třeba do UTF-8. Do 127 bajtů tak budeš mít režii pouze 1 bajt.

Pro praktické účely bych však sáhl po SQLite, které tohle vše má už v sobě a řeší i modifikace. Pokud to má být přenositelné, tak bych pro texty a čísla použil XML.

Asi sme si neporozumeli. Neviem či som otázku položil dostatočne zrozumiteľne, aj keď pripúšťam že slovo "ukladať" mohlo pôsobiť mätúco. Otázka sa týkala typu premennej pre ukladanie hodnôt v pamäti, náhrada za klasický C++ string, zaujímalo ma aké typy zvyknú C++ programátori používať pre ukladanie binárnych dát (v pamäti), nehľadal som databázu, persistentné úložisko ani formát serializácie či výmeny dát. Ono aj do toho SQL lite alebo XML musíte tie dáta nejakým spôsobom dostať a na to potrebujete premennú a o jej typ mi teraz ide.

Je to vpodste skôr taká všeobecná debata. Zaujíma ma aké sú bežne používané postupy v tomto jazyku.

_Jenda

  • *****
  • 1 550
    • Zobrazit profil
    • https://jenda.hrach.eu/
    • E-mail
Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #4 kdy: 04. 10. 2020, 04:37:17 »
Nevýhoda je že na ukladanie veľkosti stringu spotrebujeme 4 znaky (typ int). A ak má string dajme tomu 2 znaky tak je to veľmi nešetrné.
Já teda C++ nerozumím, ale čekal bych, že místo pro ten vector (případně nějaké tvé řešení) se alokuje podobně jako to dělá céčkový malloc (třeba bude dokonce malloc defaultní std::allocator), a tak budeš potřebovat ještě chunk size a možná nějaký next free chunk, a navíc ti stejně nedá 2 bajty, protože to všechno bude zarovnané. Nebo ti dá slab, ale ten asi taky nebude mít pro velikost 6 (mimochodem umí vector komunikovat s alokátorem, nebo si bude muset velikost, co alokoval, taky někde držet? protože vector má počet prvků a taky aktuálně alokovanou kapacitu). Takže celá ta věc kolem 2 bajtů může mít klidně 32 bajtů celkem. Pokud to potřebuješ ukládat efektivně, tak si to asi budeš muset napsat sám, se znalostí toho, jak velké věci ukládáš, kolik jich je, jestli je potřebuješ i uvolňovat a hrozí při tom fragmentace atd.

Teraz som si, ale uvedomil že možno skôr std::vector<unsigned char> zodpovedá tomu čo očakávame od takéhoto poľa znakov.
Pro binární data bych použil explicitně uint8_t. V céčku teda, jaké jsou zvyklosti v C++, to nevím.
« Poslední změna: 04. 10. 2020, 04:42:52 od _Jenda »


m1x

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #5 kdy: 04. 10. 2020, 06:08:37 »
Na binární data narážím když je potřebuju odněkud převzít nebo někam předat. Pak mi způsob určí ta knihovna či funkce.

Pokud musím navrhnout datový typ, pak tvořím co se mi zdá vhodné ke konkrétnímu účelu. Když to není kritická část kódu tak hledám přehlednost a ať nemusím moc přemýšlet a ať to pak už nemusím nijak konvertovat.

Když je kód v něčem kritický, ptám se v čem: paměťová náročnost, CPU náročnost, častá alokace a uvolnění, častá změna velikosti, mnoho malých kousků... a ptám se které vlastnosti budu znát předem: min. a max. velikost, pevnou velikost nebo jen několik druhů bloků, velikost dat např. vždy jako násobek 256bytů, že každý blok budu zvětšovat o 3byty nebo naopak že se velikost měnit nebude, jestli se bude plnit jednorázově pole ze souboru nebo naopak lepit a přehazovat kousky...

Pro data v paměti není žádný způsob ten jediný obecně správný a univerzální a nejoptimálnější. Příliš záleží na konkrétním použití a v C++ záleží hlavně na tobě, jak to chceš.

Obecně třeba unsigned char []
nebo uint16_t []

nebo sstream, s tím se pracuje jako se souborem

pro Cčkaře malloc()

Když bude každý řetězec měnit velikost, pak vector<unsigned char>

Pro řádky nebo odstavce malého textového editoru (max. desítky tisíc řádků) bych dal třeba
typedef vector<uint32_t> line_t;
nebo
typedef vector<uint8_t> line_t;
typedef vector<line_t> text_t;


Pro RGB data třeba struct {unsigned char  R, G, B; }

Ten nápad s délkou v utf-8 se mi líbí...

Pro mnoho krátkých kousků bych možná vytvořil něco úplně Pascalovského, protože vector<> má myslím režii tři pointery a jednu alokaci paměti, tedy jeden byte zabere asitak 48bytů? a má význam hlavně tam kde se bude velikost často měnit. Kdesi jsem řešil že délka datových bloků byla min. 6 a max. 259 a velikost se tedy dala uložit do jediného bytu...

Pro unicode text bych počítal s utf-8, znak 0x00 v textu nepotkávám a snad tam ani nemá co dělat, nevím. Pak by string nebo i char * klidně stačil. Naopak pozor na uložení textu do 16bitového typu, ten teoreticky nepojme všechny unicode znaky a bude vytvářet byty 0x00 i tam kde znak NUL (tedy 0x0000) není...

Pokud by výsledek měl být v 16bitových znacích, pak bych používal
jako základ uint16_t .

Pokud má být výsledek podle definice wchar_t (zatím neznám) tak ho použiju.

Pokud by výsledek měl být unicode text, třeba v utf-8 nebo v utf-16 tak bych pro zpracování použil raději unsigned int nebo uint32_t nebo tak něco a konvertoval to.

nula

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #6 kdy: 04. 10. 2020, 08:51:48 »
  • std::string muzete pouzivat i na ukladani binarnich dat. String si drzi velikost ulozenych dat zvlast. Navic dostanete sso. K datum se da pak jednoduse pristupovat pomoci .size(), .data() (c_str() je totez, ale imho semanticky .data() vypada lip)
  • Da se pouzit std::vector<>, nebo std::array<>.
  • Vyhnul bych se jakymkoli manualnim mallocum. Skoro nikdy to neni potreba.

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #7 kdy: 05. 10. 2020, 08:43:42 »
pouzij klasicke C-like ukladanie:

char[] pole = {0x01, 0x02, 0x03}
char* string = "\x0A\x0D\0x61\x00";

Není naprosto žádný rozumný důvod použít něco takového. Mnohem lépe použít std::array<unsigned char> pro fixed-length buffer.

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #8 kdy: 05. 10. 2020, 08:46:01 »
  • std::string muzete pouzivat i na ukladani binarnich dat. String si drzi velikost ulozenych dat zvlast. Navic dostanete sso. K datum se da pak jednoduse pristupovat pomoci .size(), .data() (c_str() je totez, ale imho semanticky .data() vypada lip)
  • Da se pouzit std::vector<>, nebo std::array<>.
  • Vyhnul bych se jakymkoli manualnim mallocum. Skoro nikdy to neni potreba.
Pod tohle bych se podepsal.

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #9 kdy: 05. 10. 2020, 08:56:35 »
std::string by měl zvládnout i nějakou tu nulu uvnitř, ale bude to křehké. Čtenáře kódu by mohlo třeba zaskočit pokud .size() vrátí něco jiného než strlen. Na druhou stranu jsou implementace docela vychytané. Třeba clangový string zvládne do svých 16B nacpat 15B SSO string. :)

Na obecnou sekvenci bytů bych pravděpodobně použil std::vector<uint8_t>, případně std::vector<std::byte>. Byte je tenounký obal nad uint8, pak to není číslo ani znak ale jenom hrst bitů.

Overhead vectoru bych neřešil, pokud jich nebude extrémně moc. Vector jsou obvykle 3 pointery + je tu samozřejmě overhead schovaný za mallocem. Pokud už budeš ten overhead muset řešit, pak se dá na pár místech ušetřit. Ale bude to za cenu kompromisů a šité na nějaké konkrétní použití.

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #10 kdy: 05. 10. 2020, 09:01:42 »
pouzij klasicke C-like ukladanie:

char[] pole = {0x01, 0x02, 0x03}
char* string = "\x0A\x0D\0x61\x00";

Není naprosto žádný rozumný důvod použít něco takového. Mnohem lépe použít std::array<unsigned char> pro fixed-length buffer.
Jop, rozumné implementace std::array v sobě mají navíc asserty na správných místech. Až se zase utneš při indexování (až, ne jestli ;) ) tak za to budeš jen rád.

nula

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #11 kdy: 05. 10. 2020, 11:48:11 »
std::string by měl zvládnout i nějakou tu nulu uvnitř, ale bude to křehké. Čtenáře kódu by mohlo třeba zaskočit pokud .size() vrátí něco jiného než strlen. Na druhou stranu jsou implementace docela vychytané. Třeba clangový string zvládne do svých 16B nacpat 15B SSO string. :)
...

No, kombinovat C a C++ funkce nikdy neni dobre. A pouzivat strlen na string::c_str() je jeden z pripadu :) Pokud se predava vsude jako kontejner std::string, tak nevim, jak by se k tomu strlenu clovek dostal. Krehky neni kontejner, krehke je michani C a C++
Jinak, ano, proto to tam zminuju vys, ze clovek dostane navic sso. Takze u mensich poli je i dobra pravdepodobnost, ze  to bude mit i vykonnejsi nez mallocy.

pouzij klasicke C-like ukladanie:

char[] pole = {0x01, 0x02, 0x03}
char* string = "\x0A\x0D\0x61\x00";

Není naprosto žádný rozumný důvod použít něco takového. Mnohem lépe použít std::array<unsigned char> pro fixed-length buffer.
Jop, rozumné implementace std::array v sobě mají navíc asserty na správných místech. Až se zase utneš při indexování (až, ne jestli ;) ) tak za to budeš jen rád.
Souhlasim, je opravdu malo pripadu, kdy je nutne pouzivat Cckove pole a ne C++ std::array (ci vector). Nerikam, ze nejsou, ale vetsinou je v te chvili jasne, proc je std::vector(/array) nevhodny ci nepouzitelny.

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #12 kdy: 05. 10. 2020, 13:16:33 »
No, kombinovat C a C++ funkce nikdy neni dobre. A pouzivat strlen na string::c_str() je jeden z pripadu :) Pokud se predava vsude jako kontejner std::string, tak nevim, jak by se k tomu strlenu clovek dostal. Krehky neni kontejner, krehke je michani C a C++
Kombinace C a C++ je standardní stav. OS mají Cčkové api, hromada knihoven má Cčkové api atd. C++ api bývá jen tenký wrapper. A pak stačí aby ten wrapper nečekal ve stringu nuly a jen ho přes c_str překlopil na char* a předal dál.

Raději ani nebudu počítat, kolikrát jsem něco takového viděl. Občas té obalované Cčkové knihovně ani ten pointer + délka předat nejde.

Není vlastně jedno, co přesně je křehké? String s nulama je past a bugreporty půjdou za tebou bez ohledu na to o kolik úrovní níž to bouchne. ;)

EDIT : Tím nechci říct aby to tazatel za žádnou cenu nedělal. Jen by si měl být vědom rizik.
« Poslední změna: 05. 10. 2020, 13:21:49 od Jiří Havel »

bmn

  • ***
  • 145
    • Zobrazit profil
    • E-mail
Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #13 kdy: 06. 10. 2020, 13:21:41 »
Raději ani nebudu počítat, kolikrát jsem něco takového viděl. Občas té obalované Cčkové knihovně ani ten pointer + délka předat nejde.

Skutečně existují situace, kdy je nulový bajt legitimní součástí řetězce a zároveň funkce nepřijímá pointer + délku, ale jen pointer a čeká, že bude ukončený nulovým bajtem?

Podle mého je využití std::string pro binární data v pořádku. String a char v C++ totiž nejsou textový řetězec a znak (byť se tak často používají), ale právě řetězec bajtů resp. bajt.

Řekl bych tedy, že jde o zcela mimoběžné případy, kde se jen shodou okolností používá stejný datový typ. Tam, kde jsou obsahem std::string binární data (potenciálně obsahující nulový bajt), nehrozí napojení na rozhraní, kde se pracuje s (textovými) řetězci ukončenými nulovým bajtem - nedávalo by to smysl. A tam, kde se s textovými nulou ukončenými řetězci pracuje, zase nedává smysl, aby někdo cpal nulový bajt resp. binární data do std::string.

Sice někdo takový program může napsat a půjde to zkompilovat, ale z hlediska návrhu to nedává smysl. Je to asi jako když použijeme int jednou pro rozměry v cm a podruhé pro teplotu ve stupních Celsia a tyto hodnoty (oboje int) pomícháme a přiřadíme do proměnných, kam nepatří. Tady se opravdu nelze zlobit na programovací jazyk nebo datový typ...

Re:Ako ukladáte binárne dáta a texty v C++?
« Odpověď #14 kdy: 06. 10. 2020, 15:34:41 »
Raději ani nebudu počítat, kolikrát jsem něco takového viděl. Občas té obalované Cčkové knihovně ani ten pointer + délka předat nejde.

Skutečně existují situace, kdy je nulový bajt legitimní součástí řetězce a zároveň funkce nepřijímá pointer + délku, ale jen pointer a čeká, že bude ukončený nulovým bajtem?
Legitimní nevím. Ale ve vrstevnatém legacy kódu existuje spousta situací, kdy funkce bere std::string a není jasné jestli nuly uvnitř snese nebo ne.
Citace
Podle mého je využití std::string pro binární data v pořádku. String a char v C++ totiž nejsou textový řetězec a znak (byť se tak často používají), ale právě řetězec bajtů resp. bajt.

Typický příklad : Mám nějakou síťovou nebo serializační vrstvu třetí party. Jsou tam metody co zapisují std::string. V dokumentaci samozřejmě není, jestli to tu nulu snese nebo ne.

Netvrdím že je to dobře. Jen že je s tím třeba počítat, protože se to vyskytuje nepříjemně často. A dualita std::stringu k tomuhle použití navíc dost svádí.