Страницы

Поиск по вопросам

воскресенье, 24 ноября 2019 г.

Как распарсить HTML в .NET?


Необходимо извлечь все URL из атрибутов href тегов a в HTML странице. Я попробова
воспользоваться регулярными выражениями:

Uri uri = new Uri("http://google.com/search?q=test");
Regex reHref = new Regex(@"]+href=""([^""]+)""[^>]+>");
string html = new WebClient().DownloadString(uri);
foreach (Match match in reHref.Matches(html))
    Console.WriteLine(match.Groups[1].ToString());


Но возникает множество потенциальных проблем:


Как отфильтровать только специфические ссылки, например, по CSS классу?
Что будет, если кавычки у атрибута другие?
Что будет, если вокруг знака равенства пробелы?
Что будет, если кусок страницы закомментирован?
Что будет, если попадётся кусок JavaScript?
И так далее.


Регулярное выражение очень быстро становится монструозным и нечитаемыми, а проблемны
мест обнаруживается всё больше и больше.

Что делать?
    


Ответы

Ответ 1



Регулярные выражения предназначены для обработки относительно простых текстов, которы задаются регулярными языками. Регулярные выражения со времени своего появления сильн усложнились, особенно в Perl, реализация регулярных выражений в котором является вдохновение для остальных языков и библиотек, но регулярные выражения всё ещё плохо приспособлен (и вряд ли когда-либо будут) для обработки сложных языков типа HTML. Сложность обработк HTML заключается ещё и в очень сложных правилах обработки невалидного кода, которы достались по наследству от первых реализаций времён рождения Интернета, когда никаки стандартов не было и в помине, а каждый производитель браузеров нагромождал уникальны и неповторимые возможности. Итак, в общем случае регулярные выражения — не лучший кандидат для обработки HTML Обычно разумнее использовать специализированные парсеры HTML. CsQuery Лицензия: MIT Один из современных парсеров HTML для .NET. В качестве основы взят парсер validator.n для Java, который в свою очередь является портом парсера из движка Gecko (Firefox) Это гарантирует, что парсер будет обрабатывать код точно так же, как современные браузеры. API черпает вдохновение у jQuery, для выбора элементов используется язык селекторо CSS. Названия методов скопированы практически один-в-один, то есть для программистов знакомых с jQuery, изучение будет простым. Обладает высокой производительностью. На порядки превосходит HtmlAgilityPack+Fizzle по скорости на сложных запросах. CQ cq = CQ.Create(html); foreach (IDomObject obj in cq.Find("a")) Console.WriteLine(obj.GetAttribute("href")); Если требуется более сложный запрос, то код практически не усложняется: CQ cq = CQ.Create(html); foreach (IDomObject obj in cq.Find("h3.r a")) Console.WriteLine(obj.GetAttribute("href")); HtmlAgilityPack Лицензия: Ms-PL Самый старый, и потому самый популярный парсер для .NET. Однако возраст не означае качество, например, уже пять лет (!!!) висит незакрытым критический баг Incorrect parsin of HTML4 optional end tags, который приводит к некорректной обработке тегов HTML, закрывающи теги для которых опциональны. В API присутствуют странности, например, если ничего н найдено, возвращается null, а не пустая коллекция. Для выбора элементов используется язык XPath, а не селекторы CSS. На простых запроса код получается более-менее удобочитаемый: HtmlDocument hap = new HtmlDocument(); hap.LoadHtml(html); HtmlNodeCollection nodes = hap.DocumentNode.SelectNodes("//a"); if (nodes != null) foreach (HtmlNode node in nodes) Console.WriteLine(node.GetAttributeValue("href", null)); Однако если нужны сложные запросы, то XPath оказывается не очень приспособленны для имитации CSS селекторов: HtmlDocument hap = new HtmlDocument(); hap.LoadHtml(html); HtmlNodeCollection nodes = hap.DocumentNode.SelectNodes( "//h3[contains(concat(' ', @class, ' '), ' r ')]/a"); if (nodes != null) foreach (HtmlNode node in nodes) Console.WriteLine(node.GetAttributeValue("href", null)); Fizzler Лицензия: LGPL Надстройка к HtmlAgilityPack, позволяющая использовать селекторы CSS. HtmlDocument hap = new HtmlDocument(); hap.LoadHtml(html); foreach (HtmlNode node in hap.DocumentNode.QuerySelectorAll("h3.r a")) Console.WriteLine(node.GetAttributeValue("href", null)); AngleSharp Лицензия: BSD (3-clause) Новый игрок на поле парсеров. В отличие от CsQuery, написан с нуля вручную на C# Также включает парсеры других языков. API построен на базе официальной спецификации по JavaScript HTML DOM. В некоторы местах есть странности, непривычные для разработчиков на .NET (например, при обращени к неверному индексу в коллекции будет возвращён null, а не выброшено исключение; ест свой отдельный класс Url; пространства имён очень гранулярные, даже базовое использовани библиотеки требует три using и т. п.), но в целом ничего критичного. Из других странностей — библиотека тащит за собой Microsoft BCL Portability Pack Поэтому, когда подключите AngleSharp через NuGet, не удивляйтесь, если обнаружите подключенным три дополнительных пакета: Microsoft.Bcl, Microsoft.Bcl.Build, Microsoft.Bcl.Async. Обработка HTML простая: IHtmlDocument angle = new HtmlParser(html).Parse(); foreach (IElement element in angle.QuerySelectorAll("a")) Console.WriteLine(element.GetAttribute("href")); Она не усложняется, и если нужна более сложная логика: IHtmlDocument angle = new HtmlParser(html).Parse(); foreach (IElement element in angle.QuerySelectorAll("h3.r a")) Console.WriteLine(element.GetAttribute("href")); Regex Страшные и ужасные регулярные выражения. Применять их нежелательно, но иногда возникае необходимость, так как парсеры, которые строят DOM, заметно прожорливее, чем Regex они потребляют больше и процессорного времени, и памяти. Если дошло до регулярных выражений, то нужно понимать, что вы не сможете построит на них универсальное и абсолютно надёжное решение. Однако если вы хотите парсить конкретны сайт, то эта проблема может быть не так критична. Ради всего святого, не надо превращать регулярные выражения в нечитаемое месиво Вы не пишете код на C# в одну строчку с однобуквенными именами переменных, так и регулярны выражения не нужно портить. Движок регулярных выражений в .NET достаточно мощный, чтоб можно было писать качественный код. Например, вот немного доработанный код для извлечения ссылок из вопроса: Regex reHref = new Regex(@"(?inx) ]* href \s* = \s* (? ['""] ) (? [^""]+ ) \k [^>]* >"); foreach (Match match in reHref.Matches(html)) Console.WriteLine(match.Groups["url"].ToString());

Ответ 2



Используйте библиотеку CefSharp для решения подобных задач. Почему следует применять именно такой подход? У вас намного упрощается процесс разработки за счёт того, что вместо написания XPath, условий и/или циклов в C# вы просто в консоли браузера (желательно основанного на Chromium) просто разрабатываете всё что вам нужно, затем когда уже написан небольшой костяк из класса (покажу его ниже), вы просто вставляете JavaScript-код, который вам нужен. Надёжность. Вы не пытаетесь парсить HTML и не изобретаете велосипед, что являетс почти всегда очень плохой идеей. Проект основан на Chromium, поэтому вам не приходитс доверять какому-то новому/незнакомому продукту. Активно поддерживается для синхронизаци с новой версией. Для Javascript-обращений для простоты и демонстрации используется jQuery, предполагая что на целевом сайте он тоже есть. Но это может быть также чистый JavaScript либо друга библиотека при условии, что эта библиотека используется на сайте. Если вы проскроллите вниз, то заметите, что помимо написания небольшой прослойк кода и инициалиации, решение занимает одну-две строки: string[] urls = await wrapper.GetResultAfterPageLoad("https://yandex.ru", async () => await wrapper.EvaluateJavascript( "$('a[href]').map((index, element) => $(element).prop('href')).toArray()")); Что это такое? Это управляемая оболочка над CEF (Chromium Embedded Framework). То есть Вы получает мощь Chromium, которой управляете программно. Почему именно CEF/CefSharp? Не стоит заморачиваться парсингом страниц (а это сложная и неблагодарная задача которую крайне не рекомендую делать). Можно работать с уже загруженной страницей (после выполнения скриптов). Есть возможность выполнять произвольный JavaScript с последними возможностями. Даёт возможность вызывать AJAX с помощью JavaScript, а затем при успехе (success) дёргать события в C#-коде с результатом AJAX. Подробно и с примером рассмотрел здесь. Разновидности CefSharp CefSharp.WinForms CefSharp.Wpf CefSharp.OffScreen Первые две используются если вам надо дать пользователям элемент управления "Браузер" Концептуально похоже на WebBrowser в Windows Forms, который является оболочкой для управлени IE, а не Chromium, как в нашем случае. Поэтому мы будем использовать CefSharp.OffScreen (закадровую) разновидность. Написание кода Допустим у нас консольное приложение, но это уже зависит от Вас. Устанавливаем Nuget-пакет CefSharp.OffScreen 57-ой версии: Install-Package CefSharp.OffScreen -Version 57.0.0 Дело в том, что C# всё массивы маппает к List, результат JavaScript обёрну в object, в котором уже содержатся List, string, bool, int в зависимости о результата. Для того чтобы сделать результаты строго типизированными, создаём небольшо ConvertHelper: public static class ConvertHelper { public static T[] GetArrayFromObjectList(object obj) { return ((IEnumerable)obj) .Cast() .ToArray(); } public static List GetListFromObjectList(object obj) { return ((IEnumerable)obj) .Cast() .ToList(); } public static T ToTypedVariable(object obj) { if (obj == null) { dynamic dynamicResult = null; return dynamicResult; } Type type = typeof(T); if (type.IsArray) { dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetArrayFromObjectList)) .MakeGenericMethod(type.GetElementType()) .Invoke(null, new[] { obj }); return dynamicResult; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetListFromObjectList)) .MakeGenericMethod(type.GetGenericArguments().Single()) .Invoke(null, new[] { obj }); return dynamicResult; } return (T)obj; } } Для обработки с ошибками Javascript создаём класс JavascriptException. public class JavascriptException : Exception { public JavascriptException(string message) : base(message) { } } У вас может быть свой способ обработки ошибок. Создаём класс CefSharpWrapper: public sealed class CefSharpWrapper { private ChromiumWebBrowser _browser; public void InitializeBrowser() { Cef.EnableHighDPISupport(); // Perform dependency check to make sure all relevant resources are in ou output directory. Cef.Initialize(new CefSettings(), performDependencyCheck: false, browserProcessHandler null); _browser = new ChromiumWebBrowser(); // wait till browser initialised AutoResetEvent waitHandle = new AutoResetEvent(false); EventHandler onBrowserInitialized = null; onBrowserInitialized = (sender, e) => { _browser.BrowserInitialized -= onBrowserInitialized; waitHandle.Set(); }; _browser.BrowserInitialized += onBrowserInitialized; waitHandle.WaitOne(); } public void ShutdownBrowser() { // Clean up Chromium objects. You need to call this in your applicatio otherwise // you will get a crash when closing. Cef.Shutdown(); } public Task GetResultAfterPageLoad(string pageUrl, Func> onLoadCallback) { TaskCompletionSource tcs = new TaskCompletionSource(); EventHandler onPageLoaded = null; T t = default(T); // An event that is fired when the first page is finished loading. // This returns to us from another thread. onPageLoaded = async (sender, e) => { // Check to see if loading is complete - this event is called twice one when loading starts // second time when it's finished // (rather than an iframe within the main frame). if (!e.IsLoading) { // Remove the load event handler, because we only want one snapsho of the initial page. _browser.LoadingStateChanged -= onPageLoaded; t = await onLoadCallback(); tcs.SetResult(t); } }; _browser.LoadingStateChanged += onPageLoaded; _browser.Load(pageUrl); return tcs.Task; } public async Task EvaluateJavascript(string script) { JavascriptResponse javascriptResponse = await _browser.GetMainFrame().EvaluateScriptAsync(script); if (!javascriptResponse.Success) { throw new JavascriptException(javascriptResponse.Message); } } public async Task EvaluateJavascript(string script) { JavascriptResponse javascriptResponse = await _browser.GetMainFrame().EvaluateScriptAsync(script); if (javascriptResponse.Success) { object scriptResult = javascriptResponse.Result; return ConvertHelper.ToTypedVariable(scriptResult); } throw new JavascriptException(javascriptResponse.Message); } } Далее вызываем наш класс CefSharpWrapper из метода Main. public class Program { private static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { CefSharpWrapper wrapper = new CefSharpWrapper(); wrapper.InitializeBrowser(); string[] urls = await wrapper.GetResultAfterPageLoad("https://yandex.ru" async () => await wrapper.EvaluateJavascript("$('a[href]').map((index element) => $(element).prop('href')).toArray()")); wrapper.ShutdownBrowser(); } } Также: в данной библиотеке есть особенность, что пустой JavaScript-массив приводитс к null. Поэтому, возможно, есть смысл добавить в ConvertHelper соотвествующий код (эт зависит от вашего кода и потребностей), либо в вызывающем коде писать что-то вроде if (urls == null) urls = new string[0] Также установите x64 или x86 в качестве платформы. Платформа Any CPU поддерживается но требует дополнительного кода.

Ответ 3



Если требования к производительности не очень высокие, можно использовать COM-объек Internet Explorer (добавить ссылку на Microsoft HTML Object Library): public static List ParseLinks(string html) { List res = new List(); mshtml.HTMLDocument doc = null; mshtml.IHTMLDocument2 d2 = null; mshtml.IHTMLDocument3 d = null; try { doc = new mshtml.HTMLDocument();//инициализация IE d2 = (mshtml.IHTMLDocument2)doc; d2.write(html); d = (mshtml.IHTMLDocument3)doc; var coll = d.getElementsByTagName("a");//получить коллекцию элементов п имени тега object val; foreach (mshtml.IHTMLElement el in coll)//извлечь атрибут href из всех элементов { val=el.getAttribute("href"); if (val == null) continue; res.Add(val.ToString()); } } finally { //освобождение ресурсов if (doc != null) Marshal.ReleaseComObject(doc); if (d2 != null) Marshal.ReleaseComObject(d2); if (d != null) Marshal.ReleaseComObject(d); } return res; }

Ответ 4



Вставлю свои пять копеек, если нет желания возиться с COM-объектами mshtml, можн создать объект WebBrowser() из Windows.Forms, причём, если вам не нужно срабатывани всех скриптов, то, я так понимаю, страницу можно грузить не самим браузером, а чем попроще вроде WebClient.DownloadString(), а далее загружаем полученный текст страницы для парсинг в WebBrowser: var itemPageText = _webClient.DownloadString(url); using (var pageHtml = new WebBrowser()) { pageHtml.DocumentText = itemPageText; var elem = pageHtml.Document.GetElementById("imainImgHldr"); } ну и т.п., главное, что методы вроде GetElementById() тоже представляют собой нескольк более удобоваримые обёртки в отличие от mshtml.

Ответ 5



F# Поиск на странице всех ссылок на книги по F#: let fsys = "https://www.google.com/search?tbm=bks&q=F%23" let doc2 = HtmlDocument.Load(fsys) let books = doc2.CssSelect("div.g h3.r a") |> List.map(fun a -> a.InnerText().Trim(), a.AttributeValue("href")) |> List.filter(fun (title, href) -> title.Contains("F#")) F# Data F# Data HTML Parser F# Data HTML CSS selectors

Ответ 6



У меня все замечательно получается при помощи XElement Попробуйте :) var htmlDom = XElement.Parse("[Код HTML]"); Как подсказали в комментариях, это будет работать если нужная нам страница являетс валидным XHTML документом.

Как вернуться (откатиться) к более раннему коммиту?


Я хочу вернуться к более раннему коммиту. Как мне это сделать?

Вот что показывает команда git log:

$ git log
commit dddddd
Author: Me 
Date:   Thu Nov 4 18:59:41 2010 -0400

Add buzz

commit cccccc
Author: Me 
Date:   Thu Nov 4 05:13:39 2010 -0400

Add fizz

commit bbbbbb
Author: Me 
Date:   Thu Nov 4 00:55:06 2010 -0400

Add bar

commit aaaaaa
Author: Me 
Date:   Wed Nov 3 23:56:08 2010 -0400

Add foo

    


Ответы

Ответ 1



Этот вопрос можно понять по-разному: Что значит вернуться или откатиться: просто посмотреть, изменить содержимое рабоче области, изменить историю Git? Что именно откатить: рабочую область (worktree), индекс (область подготовки коммита staging area), текущую ветку, удаленную ветку? К какой позиции откатить: к индексу, к последнему коммиту, к произвольному коммиту? Обозначим начальную ситуацию на следующей схеме: (i) (wt) A - B - C - D - ? - ? ↑ master (HEAD) A, B, C, D — коммиты в ветке master. (HEAD) — местоположение указателя HEAD. (i) — состояние индекса Git. Если совпадает c (HEAD) - пуст. Если нет - содержи изменения, подготовленные к следующему коммиту. (wt) — состояние рабочей области проекта (working tree). Если совпадает с (i) — не неиндексированных изменений, если не совпадает — есть изменения. ↑ обозначает коммит, на который указывает определенная ветка или указатель. Вот решения, в зависимости от задачи: 1. Временно переключиться на другой коммит Если вам нужно просто переключиться на другой коммит, чтобы, например, посмотрет на его содержимое, достаточно команды git checkout: git checkout aaaaaa (wt) (i) A - B - C - D ↑ ↑ (HEAD) master Сейчас репозиторий находится в состоянии «detached HEAD». Чтобы переключиться обратно используйте имя ветки (например, master): git checkout master 2. Переключиться на коммит и продолжить работу с него Если вы хотите продолжить работу с другого коммита, вам понадобится новая ветка Можно переключиться и создать ее одной командой: git checkout -b имя-новой-ветки aaaaaa (wt) (i) A - B - C - D ↑ ↑ new master (HEAD) 3. Удалить изменения в рабочей области и вернуть ее к состоянию как при последне коммите. Начальное состояние: (i) (wt) A - B - C - D - ? - ? ↑ master (HEAD) 3.1 Безопасно — с помощью кармана (stash) 3.1.1 Только неиндексированные Можно удалить прикарманить только те изменения, которые еще не были индексирован (командой add): git stash save --keep-index Конечное состояние: (wt) (i) A - B - C - D - ? ? ↑ ↑ master stash{0} (HEAD) 3.1.2 Индексированные и нет Эта команда отменяет все индексированные и неиндексированные изменения в рабоче области, сохраняя их в карман (stash). git stash save Конечное состояние: (wt) (i) A - B - C - D ? ↑ ↑ master stash{0} (HEAD) Восстановление несохраненных изменений: легко и просто. git stash apply Если stash совсем не нужен, его можно удалить. # удалить последнюю запись кармана git stash drop Подробнее про использование stash. После этого восстановить изменения всё ещё можно, но сложнее: How to recover a droppe stash in Git? 3.2 Опасный способ Осторожно! Эта команда безвозвратно удаляет несохраненные текущие изменения и рабочей области и из индекса Если они вам все-таки нужны, воспользуйтесь git stash. Восстановление несохраненных изменений: неиндексированные потеряны полностью, н вы можете восстановить то, что было проиндексировано. Здесь мы будем использовать git reset --hard Выполняем: git reset --hard HEAD Конечное состояние: (wt) (i) A - B - C - D - х - х ↑ master (HEAD) 4. Перейти к более раннему коммиту в текущей ветке и удалить из нее все последующи (неопубликованные) Осторожно! Эта команда переписывает историю Git-репозитория. Если вы уже опубликовал (git push) свои изменения, то этот способ использовать нельзя (см. почему). Используйт вариант из пункта 5 (git revert). 4.1 При этом сохранить изменения в индекс репозитория: git reset --soft bbbbbb После этого индекс репозитория будет содержать все изменения от cccccc до dddddd Теперь вы можете сделать новый коммит (или несколько) на основе этих изменений. (wt) (i) A - B - C - D ↑ master (HEAD) 4.2 Сохранить изменения в рабочей области, но не в индексе. git reset bbbbbb Эта команда просто перемещает указатель ветки, но не отражает изменения в индекс (он будет пустым). (i) (wt) A - B - C - D ↑ master (HEAD) 4.3 Просто выбросить изменения. Осторожно! Эта команда безвозвратно удаляет несохраненные текущие изменения. Есл удаляемые коммиты не принадлежат никакой другой ветке, то они тоже будут потеряны. Восстановление коммитов: Используйте git reflog и этот вопрос чтобы найти и восстановит коммиты; иначе сборщик мусора удалит их безвозвратно через некоторое время. Восстановление несохраненных изменений: неиндексированные потеряны полностью, н вы можете восстановить то, что было проиндексировано. Начальное состояние: (i) (wt) A - B - C - D - ? - ? ↑ master (HEAD) Выполняем: git reset --hard bbbbbb Конечное состояние: (wt) (i) A - B - C - D - х - х ↑ master (HEAD) 5. Отменить уже опубликованные коммиты с помощью новых коммитов Воспользуйтесь командой git revert. Она создает новые коммиты, по одному на кажды отменяемый коммит. Таким образом, если нужно отменить все коммиты после aaaaaa: # можно перечислить отменяемые коммиты git revert bbbbbb cccccc dddddd # можно задать диапазон от более раннего к более позднему (новому) git revert bbbbbb..dddddd # либо в относительных ссылках git revert HEAD~2..HEAD # можно отменить коммит слияния, указывая явным образом номер предка (в нашем пример таких нет): git revert -m 1 abcdef # после этого подтвердите изменения: git commit -m'детальное описание, что и почему сделано' Восстановление: Если revert-коммит оказался ошибочным, используйте этот ответ.

Как вернуть значение из события или из функции обратного вызова? Или хотя бы дождаться их окончания


Пытаюсь делать вот так, но ничего не получается:

var result = "";

someInput.onchange = function() {
  result = someInput.value;
};

$.get("someapi", function (data) {
  result = data.foo;
});

some.api.call(42, function (data) {
  result = data.bar;
});

someDiv.textContent = result;


Почему-то в someDiv ничего не отображается.
    


Ответы

Ответ 1



Дождались! ES2017 8-ая редакция. Внесено описание для функций с модификатором async, и использование await Пример уже работает в хроме: (async function() { var data = await fetch('https://jsonplaceholder.typicode.com/users'); console.log(await data.json()); })(); ES2015 В данном стандарте введено понятие функции-генератора - функции которая может передат управление из середины и затем вернуться в то же место. Обычно их используют для получени последовательностей function* foo(){ yield 1; yield 2; while(true) yield 3; } Данная функция возвращает итератор для последовательности 1,2,3,3,3,..., которы может быть проитерирован. Хотя это интересно и само по себе, но есть один специфически случай. Если получаемая последовательность - это последовательность действий, а не чисел мы можем приостановить функцию всякий раз запуская действие и ждать результата, прежд чем вернуться к выполнению функции. Таким образом получаем не последовательность чисел а последовательность будущих значений: т.е. обещаний. Это несколько сложнее, но очень мощный трюк позволяет нам писать асинхронный ко в синхронном режиме. Есть несколько "запускальщиков", которые делают это. Для пример будет использован Promise.coroutine из Bluebird, но есть и другие упаковщики, как с или Q.async. var foo = coroutine(function*(){ var data = yield fetch("/echo/json"); // обратите внимание на yield // код здесь будет выполнен после получения ответа на запрос return data.json(); // data здесь определена }); Этот метод тоже возвращает обещание, которое может быть использовано в других сопрограммах Например: var main = coroutine(function*(){ var bar = yield foo(); // ожидаем окончания нашей сопрограммы она вернет обещание // код ниже выполнится когда будет получен ответ от сервера var baz = yield fetch("/api/users/"+bar.userid); // зависит от результата возвращенног функцией foo console.log(baz); // выполнится когда завершатся оба запроса }); main(); ES2016 (ES7) Недалекое будущее В стандартах есть намеки на введение новых ключевых слов async, await позволивши бы сделать работу с обещаниями более простой. async function foo(){ var data = await fetch("/echo/json"); // обратите внимание на await // код тут выполнится только после выполнения запроса return data.json(); // data определена } Но пока это просто зарезервированные слова и неизвестно попадут ли они в следующи стандарт и когда будут реализации. На данный момент для их использования можно воспользоваться сборщиками, например Babel. частичный перевод данного ответа

Ответ 2



Проблема в том, что в коде нет операции ожидания. Ни подписка на событие, ни AJAX-вызов ни даже вызов API не ждут поступления данных - а сразу же передают управление дальше Поэтому строка someDiv.textContent = result; выполняется ДО того, как переменная resul получит значение! Способов сделать это присваивание после получения значения - несколько. Способ 0 - переместить присваивание внутрь Возможно, этот способ выглядит как-то глупо - но он решает задачу и наиболее прос в понимании. Если ваше приложение достаточно простое - то так и надо делать. Смотрите: someInput.onchange = function() { someDiv.textContent = someInput.value; }; $.get("someapi", function (data) { someDiv.textContent = data.foo; }); some.api.call(42, function (data) { someDiv.textContent = data.bar; }); someDiv.textContent = ""; В данном случае я вообще избавился от переменной result. Недостаток у данного способа ровно 1 - отсутствует разбиение на слои. Данные обрабатываютс там же, где и получаются. Если вы чувствуете, что ваши скрипты становятся при использовани такого способа все менее понятными, или вам приходится писать одно и то же в нескольки местах - надо переходить к другим способам. Способ 0+ - вынесение присваивания в именованную функцию. Простейшая модификация прошлого способа, позволяющая избавиться от дублирования кода. someInput.onchange = function() { setResult(someInput.value); }; $.get("someapi", function (data) { setResult(data.foo); }); some.api.call(42, function (data) { setResult(data.bar); }); setResult(""); function setResult(result) { someDiv.textContent = result; } Напомню, что в js объявления функций "поднимаются на верх", т.е. объявленной в само низу функцией setResult можно пользоваться где угодно. Это позволяет начинать скрип не с объявления 100500 функций - а с того кода, который непосредственно начнет выполняться. Такой способ неплохо подходит для небольших скриптов, которые не разбиты на модули. Проблема макаронного кода Иногда, асинхронный запрос делается в одном модуле или его части, а получить ег результат надо в другой. Прямое использование способа 0+ приводит к коду, который называю "макаронным": // модуль 1 function getResult() { $.get("someapi", function (data) { setResult(data.foo); }); } // модуль 2 function someFunc() { getResult(); } function setResult(result) { someDiv.textContent = result; } Обращаю внимание: someFunc вызывает getResult, которая вызывает setResult. В итог два модуля вызывают друг друга. Это и есть макаронный код. Для борьбы с таким кодом и предназначены способы ниже. Способ 1 - обратные вызовы ("колбеки", callbacks) Добавим той функции, которая делает запрос, параметр callback, куда будем передават функцию, получающую ответ: function getResult(callback) { $.get("someapi", function (data) { callback(data.foo); }); } Теперь такую функцию можно вызвать вот так: getResult(function(result) { someDiv.textContent = result; }) Или вот так: getResult(setResult); function setResult(result) { someDiv.textContent = result; } Способ 2 - обещания ("промизы", promises) Обещание в js - это шаблон программирования, обозначающий значение, которого сейча нет, но предполагается, что оно будет в будущем. Имеется несколько реализаций обещаний. Основной сейчас являются ES6 Promises, он поддерживаются современными браузерами кроме IE. (Но для тех браузеров, которые их н поддерживают, есть куча полифилов). Создаются обещания вот так: function getResult(N) { return new Promise(function (resolve, reject) { some.api.call(N, function (data) { resolve(data.bar); }); }); } Также в качестве обещания можно использовать JQuery Deferred: function getResult(N) { var d = $.Deferred(); some.api.call(N, function (data) { d.resolve(data.bar); }); return d.promise(); } Или Angular $q: function getResult(N) { var d = $q.defer(); some.api.call(N, function (data) { d.resolve(data.bar); }); return d.promise; } Кстати, Angular $q можно использовать и подобно es6 promise: function getResult(N) { return $q(function (resolve, reject) { some.api.call(N, function (data) { resolve(data.bar); }); }); } В любом случае, использование такой функции getResult будет выглядеть одинаково: getResult(42).then(function (result) { someDiv.textContent = result; }); Или же можно использовать новый синтаксис async/await, описанный в ответе ниже от Grundy Обращаю внимание, что здесь я для примера взял именно some.api.call, но не событи или ajax-вызов - и это не случайно! Дело в том, что обещание может быть выполнено (resolved) только 1 раз, а большинств событий происходят несколько раз. Поэтому использовать обещания для того же onchange - нельзя. Что же до ajax-вызова - то надо помнить, что он УЖЕ возвращает обещание! А потом все способы выше в комбинации с ним будут выглядеть смешными. Все делается гораздо проще: function getResult() { return $.get("someapi") .then(function (data) { return data.foo; }); } Кстати, здесь тоже можно было использовать async/await На случай если вы запутались в коде выше, вот его "развернутая" версия: function getResult() { var q1 = $.get("someapi"); var q2 = q1.then(function (data) { return data.foo; }); return q2; } Тут все просто. Сам по себе вызов $.get возвращает обещание, которое при выполнени будет содержать прищедшие с сервера данные. Далее мы создаем для него продолжение, которое обработает эти данные (достанет поле foo). Ну и потом это продолжение (которое тоже является обещанием) мы и возвращаем. Способ 3 - наблюдаемые значения (observables) в Knockout Обычно про Knockout вспоминают как про библиотеку для двусторонней привязки данны к виду - но ее возможности могут пригодиться и при решении подобных задач. Можно сделать так. Для начала, заведем наблюдаемое значение: var result = ko.observable(""); Это значение можно менять по событию: someInput.onchange = function() { // вызов result с параметром устанавливает значение равным параметру result(someInput.value); }; И теперь можно выполнять некоторый блок кода каждый раз когда это значение меняется: ko.computed(function() { // вызов result без параметров возвращает текущее значение someDiv.textContent = result(); }); Функция, переданная в ko.computed, будет вызвана каждый раз, когда ее зависимост изменятся. PS код выше приведен как пример ручной работы с наблюдаемыми значениями. Но имейт в виду, что в Knockout есть более простые способы для работы с содержимым элементов DOM: var vm = { result: ko.observable() }; ko.applyBindings(vm);
Способ 3.1 - наблюдаемые значения (observables) в MobX Тут все почти так же, как и в knockout. В примере ниже я использую синтаксис ES201 и старше, потому что библиотека подразумевает использование новых средств языка: import { observable, autorun } from 'mobx'; var result = observable(""); someInput.onchange = () => { result.set(someInput.value); }; autorun(() => someDiv.textContent = result.get()); Однако, обычно в MobX используются классы, а не одиночные obervable: class ViewModel { @observable result = ""; } var vm = new ViewModel(); someInput.onchange = () => { vm.result = someInput.value; }; autorun(() => someDiv.textContent = vm.result);

Ответ 3



Код с асинхронными функциями можно исполнять синхронно используя альтернативный J движок nsynjs Если асинхронная функция возвращает promise то просто вызываем функцию, а значение промиса получаем через свойство data: function synchronousCode() { var getURL = function(url) { return window.fetch(url).data.text().data; }; var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'; console.log('received bytes:',getURL(url).length); }; nsynjs.run(synchronousCode,{},function(){ console.log('synchronousCode done'); }); Если асинхронная функция вызывает callback Шаг 1. Оборачиваем асинхронную функцию в nsynjs-обертку (либо в промис): var ajaxGet = function (ctx,url) { var res = {}; var ex; $.ajax(url) .done(function (data) { res.data = data; }) .fail(function(e) { ex = e; }) .always(function() { ctx.resume(ex); }); return res; }; ajaxGet.nsynjsHasCallback = true; Шаг 2. Помещаем логику в функцию, как если бы логика исполнялась синхронно function process() { console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data); } Шаг 3. Исполняем функцию через nsynjs nsynjs.run(process,this,function () { console.log("synchronous function finished"); }); Nsynjs будет последовательно исполнять код функции, останавливаясь и дожидаясь результат вызовов всех аснихронных функций.

Книги и другие материалы для обучения


Какие книги могут пригодиться для обучения?

1. Stack Overflow на русском

2. Stack Overflow на английском

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

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

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


Ответы

Ответ 1



Stack Overflow на русском Книги и учебные ресурсы по фундаментальным знаниям и навыкам разработчика android Книги и учебные ресурсы по Android c# Книги и учебные ресурсы по C# c++ Книги и учебные ресурсы по С++ c++ Где взять стандарт C++? htmlcss Книги и учебные ресурсы по HTML и CSS java Книги и учебные ресурсы по Java javascript Книги и учебные ресурсы по JavaScript kotlin Книги и учебные ресурсы по Kotlin linux Книги и учебные ресурсы по Linux php Книги и учебные ресурсы по PHP python Книги и учебные ресурсы по Python r Книги и учебные ресурсы по языку R svg Книги и учебные материалы по SVG sqlnosql Книги и учебные ресурсы по проектированию БД: SQL && NoSQL unity3d Книги и учебные ресурсы по Unity3D алгоритмы Книги по теме "Алгоритмы" безопасность Источники по безопасному (Secure) программированию машинное-обучение Книги и учебные ресурсы по машинному обучению тестирование Книги и другие учебные материалы по тестированию

Ответ 2



Stack Overflow на английском What is the single most influential book every programmer should read Language Books/Tutorials for popular languages node.js: How do I get started with Node.js javascript: Best resources to learn JavaScript d3.js: Good books for learning D3.js c++: The Definitive C++ Book Guide and List

Для чего нужны свойства?


Допустим есть это:     

private int a { get; set; }


Какой в этом смысл, если я могу сделать так:

public int a;

    


Ответы

Ответ 1



Смотрите, какие есть преимущества у свойства перед полем. Если ваше свойство определено так: public int A { get; set; } — то непосредственных выгод, конечно, нету. Но выгоды придут позже. Вы можете навесить свою логику на запись и считывание значения. Применений може быть море. Например, вы хотите посчитать, сколько раз считывалось значение: private int a; private int readcount_a = 0; public int A { get { readcount_a++; return a; } set { a = value; } } Вы можете сделать триггер на изменение поля: class Data : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private int a; public int A { get { return a; } set { if (a == value) return; a = value; RaisePropertyChanged(); } } private void RaisePropertyChanged([CallerMemberName] string propertyName = null) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } Вы можете залогировать все изменения поля: public int A { get { return a; } set { Trace.TraceInformation("Changing value of a from {0} to {1}", a, value); a = value; } } Вы можете навесить проверку значения на корректность при записи, или ленивую инициализаци при чтении. class Data { private string a = null; public string A { get { return a ?? (a = LazyComputeInitialA()); } set { if (value == null) throw new ArgumentException(nameof(A) + " cannot be null", nameof(A)); a = value; } } } В конце-концов, вы можете не расходовать память на значение, если в большинстве случае оно одинаковое (как это сделано у DependencyProperty): class Data { static Dictionary aValues = new Dictionary(); public int A { get { int result; if (aValues.TryGetValue(this, out result)) return result; else return -1; // default value } set { aValues[this] = value; } } } Вы хотите сделать разную степень видимости у геттера и сеттера, так что наприме только ваш класс и его наследники смогут установить значение, а считать смогут все (Это, пожалуй, самое лучшее применение свойств, на мой вкус.) public int A { get; protected set; } Вы можете вообще не определять setter, и возвращать какую-то высчитанную вами величину: public int A { get { return b + c; } } (впрочем, такое можно сделать и при наличии сеттера). Например, вы можете предоставлят данные в разных форматах: public double Radians { get; set; } public double Degrees { get { return Radians * 180.0 / Math.PI; } set { Radians = value * Math.PI / 180.0; } } Вы можете установить брейкпойнт на запись или чтение свойства! Брейкпойнты на запис или чтение данных в отладчике управляемого кода Visual Studio пока (по крайней мер до текущей на данный момент версии Visual Studio 2017, версия 15.7) не поддерживаются. Вы можете задать наличие свойства в интерфейсе, в отличие от поля: interface ISupportsA { int A { get; } } class Data : ISupportsA { public int A { get; set; } } Вы можете объявить свойство виртуальным! То есть вы сможете переопределить поведени свойства в классах-наследниках. Попробуйте-ка сделать такое с полем. Свойства не хуже полей в том смысле, что вы можете заставить свойство работать так будет это просто поле (public int A { get; set; }), но вы не сможете поле заставит работать как свойство. То есть практически всегда лучше «наружу» выставлять свойство а не поле. XML-сериализация и WPF-овский Binding работает лишь со свойствами, но не с полями Да, это можно считать ошибкой во фреймворке, но фактически это так. Но не излишни ли свойства в языке? Кажется, что вместо свойства можно определит просто две функции: class Data { private int a; public int GetA() { return a; } public int SetA(int a) { this.a = a; } } Ответ на это таков. Во-первых, одно свойство вместо двух функций представляет собой логическую группу В хорошем языке вы говорите то, что думаете. На самом деле вы предоставляете пользовател «переменную» A с дополнительной, часто невидимой снаружи семантикой. Значит, и выглядет она должна как одна переменная, чтобы пользователи класса думали в тех же терминах что и вы. Во-вторых, это читаемость текста. Сравните код со свойствами: player.car.speed++; и без них: getPlayer().getCar().setSpeed(getPlayer().getCar().getSpeed()+1); Что легче воспринимается? Справедливости ради, нужно отметить и недостатки свойств по сравнению с полями. Свойства нельзя использовать как out/ref-параметр, поля можно. Доступ к полям очень быстр, а вот доступ к свойствам может быть медленным, если ко внутри геттера/сеттера медленный. Однако, медленный сеттер или (ещё хуже) геттер считаютс порочной практикой, их рекомендуется избегать, чтобы не разрушать ментальную модел «переменная с небольшим довеском». Доступ к свойству может выбросить исключение или зависнуть, в то время как боле простые поля ведут себя крайне просто. Конечно, правильно написанное свойство не буде зависать, а исключения я бы порекомендовал выбрасывать только в случаях, когда пользовател класса нарушил контракт на доступ к полю. Ещё одно тонкое отличие свойства от поля состоит в том, что геттер возвращает ва копию значения, в то время как при работе с полем вы получаете доступ непосредственн к переменной. При работе со полями reference-типов (то есть, тип которых есть класс практической разницы нет, так как работа с объектом по копии ссылки не отличается о работы по оригиналу ссылки. Разница, однако, есть, когда поле является изменяемой структуро (хотя, сами по себе изменяемые структуры — плохая идея). Пример случая, когда это важно во фрагменте кода ниже. Часто считают, что можно для начала объявить данные как поле, а потом, при необходимости «превратить» его в свойство. Это лишь отчасти верно: при этом вы теряете бинарную совместимость Код, который использовал ваш класс, должен быть перекомпилирован, так как на уровн скомпилированного кода обращение к полю и к свойству — не одно и то же. Кроме того смысл кода может поменяться, приводя к тонким ошибками. Пример из статьи по ссылке выше: using System; struct MutableStruct { public int Value { get; set; } public void SetValue(int newValue) { Value = newValue; } } class MutableStructHolder { public MutableStruct Field; public MutableStruct Property { get; set; } } class Test { static void Main(string[] args) { MutableStructHolder holder = new MutableStructHolder(); // Меняет значение holder.Field holder.Field.SetValue(10); // Получает *копию* holder.Property и изменяет её holder.Property.SetValue(10); Console.WriteLine(holder.Field.Value); // 10 Console.WriteLine(holder.Property.Value); // 0 } } Кстати, согласно Википедии, геттер и сеттер правильно называть акцессор и мутато соответственно. Вы об этом знали? [Хотя, MSDN пишет просто «методы доступа».]

Ответ 2



Я думаю, что use-case'ов, в которых полезно делать именно поле, довольно таки много Попробую привести вам один пример, поясняющий мотивацию использования свойства вместо поля. Представим, что вы разрабатываете библиотеку классов. И реализовали класс как-то так: class SuperClass { public int a; // То есть, решили, что поля достаточно. } Вашей библиотекой пользуются другие разработчики (то есть, клиенты по отношению вашему коду). И вот оказывается, что вы хотите добавить некую логику чтения или запис a (например, считывание значения из конфига). Полем здесь не обойдешься - делаем свойство: class SuperClass { public int a { get { /*какая-то логика*/ } set { /*какая-то логика*/ } } } Вы поставили библиотеку клиентам и оп-па... Их код больше не работает с вашим. Он не могут просто поставить обновление в виде вашей библиотеки, им придется пересобрат свои приложения, потому что поля a больше нет. Есть свойство, но не поле - внешний интерфей класса изменился. Код постоянно меняется, везде потенциально может появиться новая логика. Описанног неудобства не случилось бы, если бы вы сразу объявили свойство. Общий смысл примерно таков: используйте свойства даже когда это просто запись и чтени из поля, тогда внешний интерфейс класса не изменится, даже если вы измените логику работ с этим полем.

Ответ 3



Использование свойства вместо поля является реализацией принципа инкапсуляции - одног из принципов объектно ориентированого программирования.

Ответ 4



Плюсов очень много. Но важен тот, который приносит пользу в нужный момент. Эта вещь мне принесла пользу. Был класс, ну допустим с A. Этот класс использовали из кучи мест. Даже не знаеш из каких. А обновить надо сейчас, через 15 минут. Поиск использования показывается 110 позиций, а надо поменять только там, где в него что-то пишем. Делаем из public int A; -> public int A {get; private set;} И любое присвоение выпадет списком ошибок.

Ответ 5



В дополнение к подробнейшему ответу @VladD Еще удобно инициализировать: class Filter { public int ID { get; set;} public string Name { get; set;} } Filter filter = new Filter { ID = 15, Name = "Vasya" };

Ответ 6



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

Что значит “use strict”?


Нередко встречается код со строкой "use strict";. Например, библиотеки часто начинаются с этого:

(function () {
    "use strict";


Что значит "use strict", и зачем его использовать?



На других языках: English • Español
    


Ответы

Ответ 1



"use strict"; (перевод: «использовать строгий») - это установка, которая заставляе код обрабатываться в строгом режиме. Без этой установки код обрабатывается в неограниченно режиме. Строгий режим был введён в ECMAScript 5, и старые браузеры (IE9 и младше) его н поддерживают. То есть, не обращают внимания на установку по умолчанию и всё обрабатываетс в неограниченном режиме. Зачем использовать "use strict";? В строгом режиме: некоторые ошибки можно найти быстрее, более опасные и не полезные черты JavaScript либо запрещены, либо приводят к ошибке. Как использовать "use strict";? Чтобы включить строгий режим в целом скрипте, надо поставить установку "use strict" или 'use strict'; в начало скрипта. "use strict"; // код здесь обрабатывается в строгом режиме Чтобы включить строгий режим в функции, надо поставить установку в начало кода функции. // код здесь обрабатывается в неограниченном режиме function f() { "use strict"; // код здесь обрабатывается в строгом режиме } // код здесь обрабатывается в неограниченном режиме В чём различие между строгим режимом и неограниченным режимом? В строгом режиме: нельзя присваивать значение в неопределённую переменную (спецификация §11.13.1) В неограниченном режиме создается глобальная переменная. (function() { "use strict"; x = 5; // ReferenceError: x is not defined })(); x = 5; // Создает глобальную переменную x Также нельзя присваивать значение в свойство данных только для чтения. (function() { "use strict"; window.undefined = 5; // TypeError: Cannot assign to read only })(); // property 'undefined' of [object Object] window.undefined = 5; // Ничего не делает нельзя использовать инструкцию with (спецификация §12.10). (function() { "use strict"; with(Object) {} // SyntaxError: Strict mode code may not include a with statement })(); в ES5 нельзя определить повторные свойства в литерале объекта (спецификация §11.1.5). (function() { "use strict"; var x = { a: 1, a: 2 }; // SyntaxError: Duplicate data property in object literal })(); // not allowed in strict mode var x = { a: 1, a: 2 }; // x равно {a: 2} нельзя определить повторные формальные параметры функции (спецификация §13.1, §15.3.2). function f(a, a) { "use strict"; } // SyntaxError: Strict mode function may not have duplicate parameter names function f(a, a) { return a; } f(1,2); // возвращает 2 изменения объекта arguments не изменяют аргументы (спецификация §10.6). function f(x) { "use strict"; arguments[0] = 5; return x; } f(10); // возвращает 10 function f(x) { arguments[0] = 5; return x; } f(10); // возвращает 5 delete приводит к ошибке, если аргумент - не изменяемое свойство объекта (спецификаци §11.4.1). (function f(x) { "use strict"; var y = 4; delete f; // SyntaxError: Delete of an unqualified identifier in strict mode. delete x; // SyntaxError: Delete of an unqualified identifier in strict mode. delete y; // SyntaxError: Delete of an unqualified identifier in strict mode. delete window.undefined; // TypeError: Cannot delete property })(); // 'undefined' of [object Object] (function f(x) { var y = 4; delete f; // Возвращает false delete x; // Возвращает false delete y; // Возвращает false delete window.undefined; // Возвращает false })(); eval не может инстанциировать переменные и функции в контексте вызова (спецификаци §10.4.2). (function () { "use strict"; eval("var x = 5"); return x; // ReferenceError: x is not defined })(); (function () { eval("var x = 5"); return x; })(); // Возвращает 5 this не преобразуется в объект, а если значение this - undefined или null, то н преобразуется в глобальный объект (спецификация §10.4.3). function f() { "use strict"; return this; }; f.call(4); // возвращает 4 f.call(null); // возвращает null f.call(undefined); // возвращает undefined function f() { return this; }; f.call(4); // возвращает [object Number] f.call(null); // возвращает [object global] f.call(undefined); // возвращает [object global] eval и arguments - нельзя изменить или использовать в качестве имени (спецификаци §11.4.4, §11.4.5, §11.13, §12.2.1, §12.10, §12.14.1, §13.1). function eval(arguments) { // SyntaxError: Unexpected eval or arguments in stric mode "use strict"; eval = "5"; // SyntaxError: Unexpected eval or arguments in stric mode ++eval; // SyntaxError: Unexpected eval or arguments in stric mode arguments++; // SyntaxError: Unexpected eval or arguments in stric mode try { var arguments = 5; // SyntaxError: Unexpected eval or arguments in stric mode } catch(eval) {} // SyntaxError: Unexpected eval or arguments in stric mode return arguments.eval; } function eval(arguments) { eval = "5"; ++eval; arguments++; try { var arguments = 5; } catch(eval) {} return arguments.eval; } eval(); // возвращает 5 нельзя использовать argument.caller и arguments.callee (спецификация §13.2). (function f() { "use strict"; arguments.caller; // TypeError: 'caller', 'callee', and 'arguments' propertie may not be accessed on strict mode functions or the arguments objects for calls to them arguments.callee; // TypeError: 'caller', 'callee', and 'arguments' propertie may not be accessed on strict mode functions or the arguments objects for calls to them f.arguments; // TypeError: 'caller', 'callee', and 'arguments' propertie may not be accessed on strict mode functions or the arguments objects for calls to them })(); больше слов, зарезервированных для использования в будущем (спецификация §7.6.1.2). (function () { "use strict"; var implements, let, private, public, yield, interface, package, protected, static; })(); // SyntaxError: Unexpected strict mode reserved word нельзя использовать литералы восьмеричной СС (спецификация B.1.1, B.1.2). (function () { "use strict"; return 010 + // SyntaxError: Octal literals are not allowed in strict mode. "\077"; // SyntaxError: Octal literals are not allowed in strict mode. })(); (function () { return 010 + "\077"; })(); // возвращает "8?"

Ответ 2



Если в двух словах то 'use strict' этим ты указываешь что используешь ES6 (ECMAScript6) самый последний стандарт JavaScript, который отличается синтаксисом от предыдущего (ES5).

Hello world с пустой функцией main


Столкнулся с такой задачей на собеседовании: 


  Написать программу, которая печатает в консоли фразу Hello world 


И всё бы ничего, если бы после этого стояла точка. Но фраза продолжается: 


  при условии, что функция main() данной программы выглядит следующим образом:


int main() 
{
    return 0;
}

    


Ответы

Ответ 1



Ответ очень прост. Их даже несколько. Самый простой, основанный на свойстве глобальны статических объектов. int printHelloWorld() { printf("Hello World"); return 0; } static int testVar = printHelloWorld(); int main () { return 0; } Это, если С стиль. С++ стиль: создаем класс, в конструкторе добавляем вывод, буде вывод до main, в деструкторе - после.

Ответ 2



Используем препроцессор. #define return puts("Hello World"); return int main () { return 0; } Не такой красивый вариант как со статикой, но тоже работает.

Ответ 3



True programmer напишет это в hex редакторе в машинных кодах. Даже функция main не нужна. BB 11 01 B9 0D 00 B4 0E 8A 07 43 CD 10 E2 F9 CD 20 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 Немного исправим, воспользовавшись примером выше для соответствию заданию. using namespace std; #include class hello{ public: hello() { ofstream hello; hello.open ("hello.com"); hello << "»..№..ґ.Љ.CН.вщН Hello, World!"; hello.close(); system("hello.com"); }put; int main(){ return 0; } Вуаля мы получили программу на языке С++ которая возвращает .com файл, затем запускае его, после чего этот файл через BIOS прерывание выводит на экран Hello, world! При это наша программа имеет пустую функцию main и не обращается напрямую к стандартном вывод в командную строку. Работает только в DOS command promt.

Ответ 4



Статические переменные и дефайны не обязательны: #include int s = printf("Hello world\n"); int main() { return 0; }

Ответ 5



Вот ещё способ: #include void hello() { puts("Hello, World!"); } #pragma data_seg(".CRT$XIY") void(*pinit)()=&hello; int main() { return 0; }; Только оказалось, что он только в дебаге работает. ПРАВКА: А ещё можно подменить точку входа: #pragma comment(linker, "/ENTRY:Main") //Вместо прагмы можно использовать парамет командной строки #include //CRT с его printf и puts (не говоря уже о cout) мы, к сожалению потеряли, поэтому придётся использовать средства ОС int main(void) { return 0; } void Main() { WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), "Hello, World!\n", 14, nullptr nullptr); } Кстати, у меня эта программка занимает 656 байт после того, как я немного похимичи с ключами компиляции.

Ответ 6



#include class hello { public: hello(){ std::cout << "Hello, world!\n"; } } put; int main(){ return 0; }

Ответ 7



#include const std::ostream& out = std::cout << "Hello World" << std::endl;

Ответ 8



Помимо многих предложенных ранее вариантов с выводом текста до выполнения main, предлож вариант с выводом после выполнения main: #include #include void hw() { puts("Hello world"); } int v = atexit(hw); int main() { return 0; }

Ответ 9



gcc поддерживает атрибут "constructor", который помещает функцию в init-секцию ELF-файла Что, в свою очередь, означает, что эта функция вызовется перед функцией main. То есть то, что нам нужно. void __attribute__((constructor)) init { printf("Hello world!"); }

Ответ 10



$ cat hellower.cpp #include "helper.cpp" int main(void) { return 0; } второй файл: $ cat helper.cpp #include int hello_helper() { printf("Hello World\n"); return 0; } static int a = hello_helper(); Лаунч: $ g++ hellower.cpp $ ./a.out Hello World

Ответ 11



Первый файл (строго соответствует условию задачи): $ cat main.cpp int main(void) { return 0; } Второй файл: $ cat hello.cpp #include int hello_helper() { printf("Hello World\n"); return 0; } static int a = hello_helper(); Makefile для компилятора g++: $ cat makefile TARGET = hello PREFIX = /usr/local/bin .PHONY: all clean install uninstall all: $(TARGET) clean: rm -f $(TARGET) *.o main.o: main.cpp g++ -c -o main.o main.cpp hello.o: hello.cpp g++ -c -o hello.o hello.cpp $(TARGET): main.o hello.o g++ -o $(TARGET) main.o hello.o install: cp $(TARGET) $(PREFIX)/$(TARGET) uninstall: rm -f $(PREFIX)/$(TARGET)

Ответ 12



Вот в стиле C++ #include class StaticOutput { static int print(); static int out; }; int StaticOutput::out = StaticOutput::print(); int StaticOutput::print() { std::cout << "Hello World!" << std::endl; return 0; } int main() { return 0; }