LINUX.ORG.RU
решено ФорумTalks

Я люблю тебя, PHP!

 ,


0

2

Написал серверное приложение на чистом С. Оно слушает порт, а PHP с ним соединяется и получает данные в json-формате.

Несколько часов убил на то, чтобы понять почему PHP не может сделать json_decode. Наконец, написал минимальный пример:

$ cat test.php 
<?php

$s1 = '["1", "2"]';
echo "1 ", var_dump(json_decode($s1)), "\n";

$s2 = $s1 . "\0";
echo "2 ", var_dump(json_decode($s2)), "\n";

$ php test.php
1 array(2) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
}

2 NULL

Да это же просто феерично! Я добавил нулевой символ в конец строки, а она перестала быть корректным json, твою мать! Я даже сравнил побайтово две строки, вывев их в файл: отличие только в \0 на конце.

Что за специалист писал такой парсер json-а в php?!

---

Отныне это тред о впечатлениях, оставшихся после встречи с неочевидными, глупыми и неграмотными решениями в разных ЯП. Делись своими впечатлениями, лоровец.

★★

Последнее исправление: CYB3R (всего исправлений: 1)
Ответ на: комментарий от note173

Нужно добавить полноценное описание потокового парсинга, а не просто \0.

Что такое потоковый парсинг? Я понимаю, что есть грамматика языка, и если встречается терминал \0, то это прекращает работу парсера и он пытается десериализовать то, что он пропарсил до сих пор. Это полный аналог EOF.

Разумно

Согласен.

bk_ ★★
() автор топика
Ответ на: комментарий от note173

Проблема ТС ортогональна проблеме потокового парсинга

vasily_pupkin ★★★★★
()
Ответ на: комментарий от bk_

Тебя смутил префикс с, а не срр?

Распакуй исходники Java. Посмотри, на чём она написана.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Видел такую в конце 1980-х

Но мы же говорим, во-первых, о 2012 году, и, во-вторых, о реальной проблеме, а не о коне в вакууме.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Ты сам выбрал язык, в котором нет нормальной поддержки строк. Все остальные претензии либо к самому себе, либо к автору конкретной реализации json-сериализации для си… Wait… Си не может в простую сериализацию :3

PolarFox ★★★★★
()
Ответ на: комментарий от bk_

наверно потому что в любом более-менее вменяемом приложении на C не используются сишные строки, а делаются надстройки над ними.

maloi ★★★★★
()
Ответ на: комментарий от bk_

Что такое потоковый парсинг?

Это когда часть данных пришла, а мы не знаем, это у нас все, половина, или мусор. В этом случае знак конца потока может быть нужен.

note173 ★★★★★
()
Ответ на: комментарий от bk_

Но мы же говорим, во-первых, о 2012 году

В 2012-м году у нормальных языков нет никакой надобности как-то особо выделять символ \0.

и, во-вторых, о реальной проблеме, а не о коне в вакууме.

Эта «реальная проблема» пока только у тебя.

KRoN73 ★★★★★
()
Ответ на: комментарий от bk_

почему тогда автор стандарта JSON, зная, что в С строки оканчиваются на \0

Фигасе ЧСВ у сишников) И вообще даже в С не обязательно хранить JSON в строке, а можно просто выделить память от и до

goingUp ★★★★★
()
Ответ на: комментарий от note173

Если первый символ был }, то последний закрывающий } — конец потока. Если первый был [ то последний закрывающий ] — конец потока

vasily_pupkin ★★★★★
()
Ответ на: комментарий от bk_

Да, там упоминается Си, поэтому и спросил. Много раз уже писали, что в языках с нормальными строками можно использовать в своих целях все символы, включая контрольные.

note173 ★★★★★
()
Ответ на: комментарий от goingUp

можно просто выделить память от и до

Попробуй так написать хоть один проект от начала до конца, а потом расскажешь, каково это.

bk_ ★★
() автор топика
Ответ на: комментарий от vasily_pupkin

Я и написал — может быть. Потоковый парсинг не только к json может применяться.

note173 ★★★★★
()
Ответ на: комментарий от bk_

Проблема в отношении парсера JSON к \0, а не в строках С.

Я думаю, что парсер одинаково спотыкнётся и на «{}\0» и на «{}Ы». Стоит ли парсеру особенным образом относиться к букве Ы?

PolarFox ★★★★★
()
Ответ на: комментарий от note173

что в языках с нормальными строками

Все вдруг решили, что нулевой символ не нужен. Тем, кому было по барабану, сказали - «да, он нам не нужен!». Тем, кому он нужен - он просто нужен, а тем, кому по барабану, кричат о том, что он никому не нужен, хотя им ничего не стоит сделать аналогичное поведение.

Банальная человеческая глупость.

bk_ ★★
() автор топика
Ответ на: комментарий от PolarFox

Да, конечно. На «Ы» надо падать с ошибкой, на \0 - останавливаться и парсить все до этого.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Ручное управление памятью это же достоинство С)

goingUp ★★★★★
()
Ответ на: комментарий от bk_

Дело не в нулевом символе, а в том, что разрешено использовать _все_. И строка становится тем, чем она и должна быть, — последовательностью _любых_ символов.

note173 ★★★★★
()
Ответ на: комментарий от bk_

language-independent
Есть два вариант, что имел ввиду автор: language как ЯП или как человеческий язык.
Если ЯП, то это мой ответ на твое утверждение

Б-р-р. Извиняюсь, н я начинаю терять мысль рассуждений. Какой Ваш ответ, при условии что это ЯП? ответ, что '\0' обязана быть концом строки во всех ЯП? Если это то, что подразумевалось, то мой ответ: нет, не обязано это так быть. Повторюсь: '\0' - это ограничение далёкого прошлого с восьмибитными игрушками. «language-independent» , причём без разницы от типа языка: искусственный или человеческий - ДОЛЖНЫ ПРИСУТСТВОВАТЬ ТОЛЬКО ПЕЧАТАЕМЫЕ СИМВОЛЫ. Точка. Мне непонятно, что Вам непонятно :)

Да, на каком-нибудь высокоуровневом ЯП, который живет в одном потоке и убивается со временем, типа php - такие жертвы простительны.
Но в низкоуровневых ЯП это - херня. На то они и низкоуровневые. Ведь там ты можешь сделать для своего приложения такой формат строки и использовать его, пожалуйста! Но представь, сколько геморроя прибудет тебе, если ты захочешь сделать это стандартом во всей С среде.
Подводя итог, твоя абстракция хороша для относительно небольшого количества строк (или на блейд-серверах со 128 ГБ ОЗУ).

тут у нас ход обсуждения немного уходит от изначальной темы «нуль-терминированные строки в JSON», но я всё-таки отвечу: на современном железе, с современными объёмами оперативной памяти будет абсолютно оправданным превращение простого типа char* в структуру struct String*. Предвидя претензии к повышенному использованию ресурсов: почему-то и без предлагаемых мно. новшевств современные хрензнаетсколько ядерные компы тормозят не хуже, чем компы двадцатилетней давности. То есть, с точки зрения порльзователя, комп 94-го года с виндой 3.11 на борту работает примерно по скорости отзыва пользовательского интерфейса также, как современный комп с Win7 на борту. То же самое относится и к Линуксам. Причина: стало больше вычислений «под капотом». При этом в том числе и стало чудовищно много вычислений, связанных именно в поддержкой строк с ранее определённой длинной и кодировкой (UTF-16, UCS и т.д.). Скажем так, если прямо сейчас в стандарт Си встроят конструкцию типа:
[code]
char *str = <UTF-8> «тестовый текст»;
size_t len = str->length; // 15
codepage_t enc = str->encoding
[/code]

то из-за этого не понадобится всем резко покупать по 128 гигабайт ОЗУ, поверьте.

Представим, что вся Linux-среда, в том числе ядро, оперируют строками в виде твоей структуры. Тогда как ты сделаешь аналог NSMutableString из Cocoa или struct strbuf из git? Нужен параметр capacity.

ну... в рамках новой доктрины, если сишные компиляторы будут поддерживать структуры строк НАТИВНО, то все стандартные функции чси все теперяшние стандартные строковые функции будут прозрачно работать с такими строками. Например:
char *result<UTF-8> = strcat(<KOI8-R> «bla-bla», <UTF-32> «bla2-bla2»);
size_t len = result->len; // или strlen(result)


Твоя абстракция протекает, когда мне нужна строковый буфер, а не строка в чистом виде. А строка в чистом виде без примесей и есть null-terminated строка. А строковый буфер уже будет хранить, например, длину и емкость.

не понял, почему «протекает». Что такое «строка в чистом виде»? В какой она должна быть кодировке? UTF-16? UTF-32? UCS?


Потом, человеку куда проще читать предложения, зная что в конце стоит точка, а имея в начале предложения число, обозначающее количество слов в предложении.

Да, с точкой человеку проще. Но человеку и незачем знать количество строк.
char *str = «some string.»;

Человек в конце предложения видит точку, но не видит '\0'. Почему компьютер должен это выражение преобразовать к «some string.\0», а не к {len=13;data=«some string.\»}? Человеку совсем необязательно знать, что компилятор вместо \0 в конец строки добавил число в начало строки. Человек всегда видит строку. И работает с этой строкой через привычные strcpy(), strcmp() strcat() и т.д. (а не через гланды в виде подключения хидеров многобайтных строк).

Если ты заговорил об интуитивной понятности, то знание наперед, сколько символов предстоит читать - нужно компьютеру, а не человеку.

вё правильно.

Коротко говоря, твоя абстракция хороша в одном примере и плоха в другом. А null-terminated строки - универсальны и интуитивно понятны, в этом их прелесть.

нет, не везде, не всегда и не во всех случаях нуль-терминированные строки интуитивно понятны. Тем более, они непонятны в стандартах, которые language-independent

Slavaz ★★★★★
()
Ответ на: комментарий от bk_

ВО! ВО! ТЫ наткнулся на мою мысль!

афигеть.. а ничо, что я это с первого моего сообщения писал. хорошо, что где-то сходимся..

А теперь я тебя поведу далее: почему тогда автор стандарта JSON, зная, что в С строки оканчиваются на \0, не прописал в стандарте соответствующее поведение?

потому, что формат language indepentent. И плевать, что в си они оканчиваются на 0, а в паскале сначала записывается число символов, проблемы как передать парсеру строку в нативном для языка формате это только проблемы либы

qnikst ★★★★★
()
Ответ на: комментарий от bk_

почему тогда автор стандарта JSON, зная, что в С строки оканчиваются на \0, не прописал в стандарте соответствующее поведение?

А должен был? «На Си свет клином не сошёлся».

Slavaz ★★★★★
()
Ответ на: комментарий от bk_

чтобы не было претензий к выбранному языку, пример на другом:

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import Data.ByteString as B
import Data.ByteString.Lazy as BL
import Data.Attoparsec.ByteString

main = do
  let o = object [("foo",String "\0\0\0")]
      b = encode o
      b'= B.concat $ BL.toChunks b
      c = parseOnly json b'
  print o
  print b
  print c
Object fromList [("foo",String "\NUL\NUL\NUL")]
Chunk "{\"foo\":\"\\u0000\\u0000\\u0000\"}" Empty
Right (Object fromList [("foo",String "\NUL\NUL\NUL")])

тут как видно используется объект {foo:«\0\0\0»} и он в предлагаемом варианте зафейлится.

qnikst ★★★★★
()

Кстати.

package main

import (
    "fmt"
    "encoding/json"
)

type test struct {
    X int
    Y int
}

func main() {
    a := test{1, 2}
    str, err := json.Marshal(a)
    if (err != nil) {
        panic(err);
    }
    fmt.Printf("%s\n", str)
}
{"X":1,"Y":2}

Эх, нравится мне Go. Прямо аж чуть не фапфапфап (-:

PolarFox ★★★★★
()
Ответ на: комментарий от PolarFox

(Оно и обратно из json в структуры может, и с xml также).

PolarFox ★★★★★
()
Ответ на: комментарий от note173

И строка становится тем, чем она и должна быть, — последовательностью _любых_ символов.

Ты путаешь bytearray и строку - это последовательность _читабельных_ символов.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Юникод определяет таблицу символов. В ней есть символ под номером 0. Пруф в таблице юникодных символов. \n — тоже не самый читабельный символ, но он есть в юникоде. Символ beep (забыл как он экранируется) тоже есть в юникоде и он ещё менее читабельный, чем предыдущие два.

PolarFox ★★★★★
()
Ответ на: комментарий от bk_

что такое «_читабельный_ символ»?
В какой кодировке он «читабельный»?
Может ли этот символ состоять из 4-х байт в кодировке UTF-32, два из которых - '\0' вперемешку с другими символами?

Slavaz ★★★★★
()
Ответ на: комментарий от PolarFox

И все эти символы должна сожрать не подавившись любая абстракция, оперирующая строкой как последовательностью символов, а не последовательностью байт.

PolarFox ★★★★★
()
Ответ на: комментарий от bk_

Ты путаешь bytearray и строку - это последовательность _читабельных_ символов.

Ой, как всё запущено. Как читаются, скажем, символы мягких или жёстких переносов? Давно ли в Си стали невозможны строки со всеми символами \1 — \1F?

KRoN73 ★★★★★
()
Ответ на: комментарий от qnikst

афигеть

Взаимно :) Я тебя совсем не сразу понял.

потому, что формат language indepentent. И плевать, что в си они оканчиваются на 0, а в паскале сначала записывается число символов, проблемы как передать парсеру строку в нативном для языка формате это только проблемы либы

Я тебя понял, отлично. Ты перекладываешь ответственность со стандарта языка на местную библиотеку, которая оперирует строками + местный парсер, написанный по этому стандарту - Я ВЕРНО ПОНЯЛ?

Если так, то теперь давай представим, если бы в стандарте это прописали. Исчезли бы *все* недомолвки. Но нет - в стандарте об этом умолчали. Я понимаю, если бы это было редкая последовательность символов типа \1\0\0\5\0\0\, но блеать это же стандартная последовательность символов в строках на языке, на котором *все* написано, включая интерпретатор пхп с парсером!

Получается, что это просто недосмотр автора стандарта. А все комментаторы, которые мне приводят мне контраргументы, я уверен, оперируют лишь одним фактом: в моем любимым ЯП это работает точно также, значит - это правильно, а то, что у ТСа - по определению неверно, чем бы оно ни было.

bk_ ★★
() автор топика
Ответ на: комментарий от Slavaz

А должен был?

Есть очевиднейший формат строки, которому уже почти полвека. Приходят дяди с книжками пхп и js в руках и говорят, что

«На Си свет клином не сошёлся»

:) Вывод очевиден

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Получается, что это просто недосмотр автора стандарта

С какого рожна авторы стандарта должны думать о личных половых проблемах одного конкретного языка?

KRoN73 ★★★★★
()
Ответ на: комментарий от bk_

То есть ты хочешь, чтобы авторы стандарта javascript object notation специально для упоротых сишников упомянули в стандарте, что символ \0 является корректным и его надо обрабатывать так же, как и все остальные символы юникода (скорее всего там ещё жёстко оговорено, что это utf8, но не суть)?

PolarFox ★★★★★
()

(Ну вот, пришёл в тред пообсирать пыхпых, а пришлось обосрать си).

PolarFox ★★★★★
()
Ответ на: комментарий от bk_

Есть очевиднейший формат строки, которому уже почти полвека.

Сразу видно молодёжь, не заставшую ни холиворов с пасквилянтами, ни Форта, ни даже Бейсиков с ассмеблерами...

KRoN73 ★★★★★
()
Ответ на: комментарий от bk_

которому уже почти полвека

Это не формат, а говно мамонта для внутреннего представления ASCII-only строк без возможности представить один из 256 возможных символов.

PolarFox ★★★★★
()
Последнее исправление: PolarFox (всего исправлений: 1)
Ответ на: комментарий от qnikst

Лень качать эту библиотеку, так как я использую libjansson.

Но такой код выдает -2.

$ cat d.c
main()
{
	const char *input = " \0 ";
	const char *expected = "\" \\u0000 \"";

	printf("%d\n", strcmp(input, expected));
}
$ gcc d.c
d.c: In function ‘main’:
d.c:6: warning: incompatible implicit declaration of built-in function ‘printf’
$ ./a.out 
-2
$
bk_ ★★
() автор топика
Ответ на: комментарий от bk_

char в нормальных языках называется byte. И что ты хочешь, подставляя в функцию, которая по логике оперирует строкой, указатель на байт?

PolarFox ★★★★★
()
Ответ на: комментарий от KRoN73

В десятый раз, причем здесь С? Вопрос о \0 в конце строки в ЯП, которые оперируют им не как терминалом строки.

личных половых проблемах одного конкретного языка?

А ничего, что на этом проблемном языке написаны большинство современных систем?

Еще раз: язык С к проблеме никакого отношения не имеет. С таким же успехом php примет строку с \0 откуда угодно, хоть из локального файла, который json-xs утилита посчитаем валидным, а пыхпых завалится при парсинге.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Получается, что это просто недосмотр автора стандарта

Может быть автор стандарта просто делал его для JavaScript, а то что он потом стал широко использоваться это заслуга его компактности. И если у С с реализацией его трудности, то это означает только то, что у С с его реализацией трудности.

goingUp ★★★★★
()
Ответ на: комментарий от PolarFox

И что ты хочешь, подставляя в функцию, которая по логике оперирует строкой, указатель на байт?

Бессмысленно и беспощадно. Я хочу сравнить _байты_ в _строках_ вплоть до ближайшего _байта_ \0.

bk_ ★★
() автор топика
Ответ на: комментарий от goingUp

а то что он потом стал широко использоваться это заслуга его компактности.

Да, формат элегантен за исключением тех нюансов, которые я уже описал. Эти нюансы и требуют доработки в стандарте, раз, как ты говоришь, язык дошел до того, что стал широкораспространен.

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

В десятый раз, причем здесь С?

При том, что null-terminated строки — это его порождение, достоинство и проблемы. Но другие языки совершенно не обязаны использовать этот подход. И не используют на практике, как тебя давно уже должен был убедить этот тред.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Но другие языки совершенно не обязаны использовать этот подход.

Все делается с какой-то целью. С _какой целью_ новые языки сломали роль \0 как терминала строки? Ведь _строка_ - это не массив байт, это последовательность читабельных символов, и пользователю от \0 в середине строки ни холодно, ни жарко. Вопрос: _зачем_ было ломать это правило, что \0 - признак конца строки, если никакой пользы оно не дает?

bk_ ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.