Разоблачение Разоблачение Страуструпа мифа «C ++ предназначен только для больших, сложных программ»


161

Страуструп недавно опубликовал серию публикаций, разоблачающих популярные мифы о C ++ . Пятый миф: «C ++ предназначен только для больших, сложных программ». Чтобы разоблачить его, он написал простую программу на C ++, загружающую веб-страницу и извлекающую из нее ссылки . Вот:

#include <string>
#include <set>
#include <iostream>
#include <sstream>
#include <regex>
#include <boost/asio.hpp>

using namespace std;

set<string> get_strings(istream& is, regex pat)
{
    set<string> res;
    smatch m;
    for (string s; getline(is, s);)  // read a line
        if (regex_search(s, m, pat))
            res.insert(m[0]);              // save match in set
    return res;
}

void connect_to_file(iostream& s, const string& server, const string& file)
// open a connection to server and open an attach file to s
// skip headers
{
    if (!s)
        throw runtime_error{ "can't connect\n" };

    // Request to read the file from the server:
    s << "GET " << "http://" + server + "/" + file << " HTTP/1.0\r\n";
    s << "Host: " << server << "\r\n";
    s << "Accept: */*\r\n";
    s << "Connection: close\r\n\r\n";

    // Check that the response is OK:
    string http_version;
    unsigned int status_code;
    s >> http_version >> status_code;

    string status_message;
    getline(s, status_message);
    if (!s || http_version.substr(0, 5) != "HTTP/")
        throw runtime_error{ "Invalid response\n" };

    if (status_code != 200)
        throw runtime_error{ "Response returned with status code" };

    // Discard the response headers, which are terminated by a blank line:
    string header;
    while (getline(s, header) && header != "\r")
        ;
}

int main()
{
    try {
        string server = "www.stroustrup.com";
        boost::asio::ip::tcp::iostream s{ server, "http" };  // make a connection
        connect_to_file(s, server, "C++.html");    // check and open file

        regex pat{ R"((http://)?www([./#\+-]\w*)+)" }; // URL
        for (auto x : get_strings(s, pat))    // look for URLs
            cout << x << '\n';
    }
    catch (std::exception& e) {
        std::cout << "Exception: " << e.what() << "\n";
        return 1;
    }
}

Давайте покажем Страуструпу, что такое небольшая и читаемая программа.

  1. Скачать http://www.stroustrup.com/C++.html
  2. Перечислите все ссылки:

    http://www-h.eng.cam.ac.uk/help/tpl/languages/C++.html
    http://www.accu.org
    http://www.artima.co/cppsource
    http://www.boost.org
    ...
    

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

победитель

Ответ C ++ выиграл голосами, но он опирается на стороннюю библиотеку (которая запрещена правилами), и вместе с другим близким конкурентом Bash полагается на взломанный вместе HTTP-клиент (он не будет работать с HTTPS, gzip, перенаправляет и т. д.). Так что Вольфрам явный победитель. PowerShell (с улучшением по комментариям) приближается к другому решению, которое приближается к размеру и удобочитаемости , но ему не уделяется особого внимания. Основные языки ( Python , C # ) тоже подошли довольно близко.


43
Каждому свое, меня называют хуже. Если бы целью ОП не было попытаться как-то доказать, что Страуструп неправ, то я бы согласился с вашей оценкой. Но вся предпосылка вопроса заключается в том, чтобы показать, как «ваш любимый язык» может делать то же самое, что и эти 50 строк C ++, с гораздо меньшим количеством строк кода. Проблема в том, что ни один из примеров не делает то же самое. В частности, ни один из ответов не выполняет какую-либо проверку ошибок, ни один из ответов не предоставляет многократно используемые функции, большинство ответов не предоставляют полную программу. Пример Страуструпа обеспечивает все это.
Данк

19
Что грустно, его веб-страница даже не действительна в формате UTF-8 . Теперь я должен обойти это, несмотря на его рекламу на сервере Content-Type: text/html; charset=UTF-8... Я отправлю ему письмо.
Cornstalks

27
@Dunk Другие примеры не предоставляют многократно используемые функции, потому что они выполняют всю функциональность этих функций в одной строке, и нет смысла делать эту функцию отдельной, а пример C ++ не выполняет никакой проверки ошибок. это не обрабатывается изначально почти идентичным образом, а фраза «полная программа» практически бессмысленна.
Джейсон

16
«Вы можете использовать любой язык, но сторонние библиотеки запрещены». Я не думаю , что это справедливое требование с учетом boost/asioиспользуются там , который является библиотекой третьей стороны. Я имею в виду, как будут конкурировать языки, которые не включают выборку url / tcp как часть своей стандартной библиотеки?
великий волк

Ответы:


116

вольфрам

Это похоже на полный обман

Import["http://www.stroustrup.com/C++.html", "Hyperlinks"]

Так что просто добавьте честный разбор сверху

Cases[
 Import["http://www.stroustrup.com/C++.html", "XMLObject"],
 XMLElement["a", {___, "href" -> link_, ___}, ___] :> 
  link /; StringMatchQ[link, RegularExpression["((http://)?www([./#\\+-]\\w*)+)"]]
, Infinity]

49
Нет, я не вижу здесь никакого обмана. Задача состоит в том, чтобы выявить лучшее из вашего языка. И эта первая строка является воплощением «маленький и читаемый».
Мартин Эндер

Ответ, который может игнорировать глупые аргументы о перехвате FTP-ссылок. Brilliant.
Сет Бэттин

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

@ MartinBüttner В этом случае вы можете подумать о том, чтобы понизить голосование meta.codegolf.stackexchange.com/a/1078/12130
Дэвид Малдер,

6
@DavidMulder Технически, лазейка в настоящее время недействительна, так как разбивка голосов составляет + 41 / -21 (и вопрос о лазейке гласит, что лазейки принимаются, если количество голосов как минимум вдвое больше, чем голосов вниз). По общему признанию, правда, но все же. ;) Кроме того, это соревнование популярности, а не игра в гольф, и, в частности, это поп-шоу о том, как легко это можно сделать на данном языке, поэтому я думаю, что лазейка на самом деле не относится к это вызов в любом случае (так как вызов в основном требует его).
Мартин Эндер

115

C ++

#include <boost/asio.hpp>
#include <regex>
#include <iostream>
int main() {
    std::string server = "www.stroustrup.com";
    std::string request = "GET http://" + server + "/C++.html HTTP/1.0\r\nHost: " + server + "\r\n\r\n";
    boost::asio::ip::tcp::iostream s{server, "http"};
    s << request;
    std::regex pat{R"((http://)?www([./#\+-]\w*)+)"};
    std::smatch m;
    for (std::string l; getline(s, l);)
        if (std::regex_search(l, m, pat))
            std::cout << m[0] << "\n";
}

Основным недостатком является неуклюжий характер boost :: asio, я уверен, что он может быть еще короче с лучшей библиотекой.


166
Забавно, что «никакие сторонние библиотеки» означают, что Python все еще может import urllib2, C3 все еще может быть using System.Net, Haskel все еще может import Network.HTTP, но кодер C ++ должен оправдываться, #include <boost/asio.hpp>как если бы он имел метрический набор специализированных, специально созданных библиотек C ++ (и C!) есть из чего выбрать то, за что стыдно, потому что комитет не стал насильно кормить вас конкретным ...
DevSolar

19
@DevSolar почти пошел на создание 2-й учетной записи, чтобы дать вам еще один голос за этот комментарий
пользователь

15
@DevSolar System.Netне обязателен, это просто высококачественная библиотека, соответствующая всем рекомендациям .NET, включенным в язык. Существуют альтернативные реализации, но наличие поддержки HTTP в стандартной библиотеке означает, что писать простые приложения просто, означает лучшую совместимость между сторонними библиотеками, означает меньше зависимостей, означает простую реализацию для фасадов и т. Д. Представьте себе мир без std::string, представьте, как все используют свою собственную библиотеку, представьте все трудности, которые с ней связаны.
Athari

17
@DevSolar: неurllib2 является третьей стороной. Это в stdlib, как в C ++. в Python всегда доступен в отличие от C ++. Если бы нам было разрешено использовать сторонние модули; Я бы использовал или в Python. <iostream>urllib2<boost/asio.hpp>lxmlBeautifulSoup
Jfs

22
Кроме того, я думаю, что самый важный комментарий здесь заключается в том, что C ++ не стандартизирует столько же в своих стандартных библиотеках, сколько в других языках, но все еще существуют широко используемые надежные переносимые библиотеки для множества тех же задач, которые являются стандартными в языках. как Python, и некоторые из этих библиотек являются стандартом де-факто. И отчасти это является результатом того, что C ++ способен нацеливаться на встроенные системы с небольшими двоичными файлами и небольшими библиотеками.
Питер Кордес

85

Pure Bash в Linux / OS X (без внешних утилит)

Программное обеспечение HTTP-клиента, как известно, раздутый. Мы не хотим такого рода зависимостей. Вместо этого мы можем протолкнуть соответствующие заголовки в поток TCP и прочитать результат. Не нужно вызывать архаичные утилиты, такие как grep или sed, чтобы проанализировать результат.

domain="www.stroustrup.com"
path="C++.html"
exec 3<> /dev/tcp/$domain/80
printf "GET /$path HTTP/1.1\r\nhost: %s\r\nConnection: close\r\n\r\n" "$domain" >&3
while read -u3; do
    if [[ "$REPLY" =~ http://[^\"]* ]]; then
        printf '%s\n' "$BASH_REMATCH"
    fi
done

Мех - я полагаю, это может быть более читабельным ...


1
Как этот, используя файловые дескрипторы Unix для каналов.
Джавадба

2
Ничего себе, никогда не думал, что можно сделать это без внешних утилит. Хотя кажется, что мой bash 3.2.17 на LFS немного устарел, так что не поддерживает mapfile:)
Руслан

@Ruslan Да, mapfileидет с Bash 4.x. То же самое можно сделать и с помощью while readцикла.
Цифровая травма

3
@ Руслан Я изменил это while readвместо mapfile. Думаю, более портативным и более читабельным.
Цифровая травма

1
Работает на OS X тоже!
Алекс Кон

65

Python 2

import urllib2 as u, re
s = "http://www.stroustrup.com/C++.html"
w = u.urlopen(s)
h = w.read()
l = re.findall('"((http)s?://.*?)"', h)
print l

Хромает, но работает


9
Почему бы не связать много этих звонков? l = re.findall('"((http)s?://.*?)"', u.urlopen(s).read())
поддельное имя

13
Он короткий, но не идиоматичный (читаемость в Python)
jfs

24
Хммм ... если бы весь мой код игнорировал ошибки, подобные этому примеру, то от 75% до 90% моей работы было бы выполнено в каждом проекте, над которым я работаю.
Данк

20
@Dunk: Предположим, что пример действительно поймал какое-то исключение (например, из urlopen()). Что он должен делать с таким исключением, кроме краха и смерти? Если он все равно рухнет и умрет, то почему бы просто не позволить Python справиться с падением и смертью и вообще прекратить обработку исключений?
Кевин

8
@Dunk: Если бы я использовал чужой код Python, я бы предпочел, чтобы они не ловили urlopenошибки, а не (скажем) ловили их и вызывали sys.exit("something's borked!"). Если они делают последнее, я должен поймать SystemExit, что никогда не бывает весело.
Кевин

55

C #

using System;
using System.Net;
using System.Text.RegularExpressions;

class Program {
    static void Main() {
        string html = new WebClient().DownloadString("http://www.stroustrup.com/C++.html");
        foreach (Match match in Regex.Matches(html, @"https?://[^""]+"))
            Console.WriteLine(match);
    }
}

4
Вы можете использовать var html, и, вероятно, var matchсбрить несколько символов.
Superbest

15
@ Superbest Я могу делать имена односимвольными и htmlвообще избавляться от переменных, но это не то, что мне нужно.
Athari

6
@ Superbest не код-гольф . : D
Кролтан

5
Ну, это также улучшает читаемость. Есть ли причина не использовать, varкогда это не повлияет на семантику кода?
Superbest

6
@Superbest: «улучшает читабельность» - это субъективно. Лично я думаю, что явное указание типа переменной улучшает удобочитаемость (обычно, как в этом коде здесь). Я не хочу обсуждать это, хотя; Я просто хочу указать, что существуют альтернативные взгляды.
Cornstalks

54

«Нет третьей стороны» - заблуждение

Я думаю, что предположение «без третьей стороны» является ошибкой. И это специфическая ошибка, которая поражает разработчиков C ++, так как очень трудно сделать код многократного использования в C ++. Когда вы вообще что-то разрабатываете, даже если это небольшой скрипт, вы всегда будете использовать любые куски кода, которые можно использовать повторно.

Дело в том, что в таких языках, как Perl, Python, Ruby (если назвать несколько), повторное использование чужого кода не только легко, но и то, как большинство людей на самом деле пишут код большую часть времени.

C ++ с его почти невозможными в поддержании совместимыми ABI-требованиями делает эту работу гораздо сложнее, и в итоге вы получаете такой проект, как Boost, который представляет собой чудовищное хранилище кода и очень мало компоновается за его пределами.

Пример CPAN

Просто для удовольствия, вот пример, основанный на CPAN, с правильным синтаксическим анализом HTML, вместо того, чтобы пытаться использовать регулярное выражение для анализа HTML

#!/usr/bin/perl
use HTML::LinkExtor;
sub callback {
   my ($tag, %links) = @_;
   print map { "$_\n" } values %links
}
$p = HTML::LinkExtor->new(\&callback, "http://www.stroustrup.com/C++.html");

6
Upvote для решения проблемы сторонних библиотек, но: дерьмо, создание повторно используемого кода на C ++ так же легко, как и на других языках. Использование и особенно поиск кода многократного использования может быть немного сложнее, но единственное, что серьезно проблематично, - это повторное использование скомпилированных артефактов, но это часто не проблема в интерпретируемых языках, таких как Perl и т. Д.
Martin Ba

4
Чтобы провести аналогию, Boost больше похож на CPAN - выбирай и выбирай. Вы не называете CPAN «чудовищным хранилищем кода» только потому, что там так много вещей, которые вы не используете?
Мартин Ба

22
CPAN - это «чудовищное хранилище кода», по любому разумному определению этих четырех слов.
JWG

3
@MartinBa Я не согласен, C ++ - это скомпилированный язык, требующий, чтобы каждый исполняемый файл перестраивал свой полный стек зависимостей, потому что трудно поддерживать совместимость ABI, что серьезно затрудняет возможность повторного использования кода. Чтобы создать повторно используемую библиотеку в C ++, вы должны пройти очень длинные отрезки, чтобы убедиться, что вы не навязываете себя постоянно несовместимым с ABI изменениям.
Даниэль Руосо

6
@MartinBa, потому что необходимость перестраивать всю вселенную каждый раз, когда вы хотите реализовать простую задачу, невыносима.
Даниэль Руосо

47

Оболочка UNIX

lynx -dump http://www.stroustrup.com/C++.html | grep -o '\w*://.*'

Также находит ftp://ссылку :)

Другой способ, не полагаясь на ://синтаксис:

lynx -dump -listonly http://www.stroustrup.com/C++.html | sed -n 's/^[ 0-9.]\+//p'

38
Я не могу понять, стоит ли +1, потому что использование веб-браузера для загрузки веб-страницы - правильный инструмент для работы, или -1, потому что задача состоит в том, чтобы написать программу для бла-бла, а вы просто вызвали программу для бред.
Дэвид Ричерби

2
Я думаю, что лучше заменить рысь на curl или wget. Они чаще используются для загрузки веб-страницы.
Павел Страхов

4
@PavelStrakhov Я выбрал lynx именно потому, что он может сбрасывать ссылки, не делая ничего особенного :)
Руслан

2
@SteveJessop под «особенным» я имею в виду на самом деле разбор или регулярное выражение или что-то еще. С lynx я просто вытаскиваю список ссылок (которые curl и wget не перечисляют) и удаляю нумерацию. Вы можете считать это мошенничеством или чем-то еще, но я подумал, что забавно {использовать инструмент, который почти идеально делает то, что требуется}, просто точно настраивая вывод.
Руслан

7
msgstr "но сторонние библиотеки не допускаются" . Я утверждаю, что lynxэто функционально эквивалентно сторонней библиотеке в этом сценарии.
Цифровая травма

43

CSS 3

* {
  margin: 0;
  padding: 0;
}
*:not(a) {
  font: 0/0 monospace;
  color: transparent;
  background: transparent !important;
}
a {
  content: "";
}
a[href*="://"]::after {
  content: attr(href);
  float: left;
  clear: left;
  display: block;
  font: 12px monospace;
  color: black;
}

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

Он работает правильно с http://www.stroustrup.com/C++.html(обратите внимание !importantна background). Чтобы работать на других страницах с большим количеством стилей, его необходимо расширить (сбросить больше свойств, пометить свойства как важные и т. Д.).

Альтернативная версия, которая включает относительные ссылки, кроме внутристраничных ссылок, начинающихся с хэшей (к сожалению, она основана на жестко закодированной абсолютной ссылке):

* {
  margin: 0;
  padding: 0;
}
*:not(a) {
  font: 0/0 monospace;
  color: transparent;
  background: transparent !important;
  float: none !important;
  width: auto !important;
  border: none !important;
}
a {
  content: "";
}
a::after {
  display: none;
}
a:not([href^="#"])::after {
  content: attr(href);
  float: left;
  clear: left;
  display: block;
  font: 12px monospace;
  color: black;
}
a:not([href*="://"])::after {
  content: "http://www.stroustrup.com/" attr(href);
}

16
Это худшее, что я когда-либо видел. +1
Эммет Р.

1
Это красиво и совершенно ужасно. +1
Рикдези

36

Clojure

(->> (slurp "http://www.stroustrup.com")
     (re-seq #"(?:http://)?www(?:[./#\+-]\w*)+"))

28
Slurp ?! Мне нужно выучить Clojure.
11684

10
@ 11684 - Clojure также имеет стандартные функции с именами spit, zipperи lazy-cat... :-)
Боб Джарвис

2
Вау, я думаю, что это будет поздняя новогодняя резолюция. @BobJarvis
11684

30

Emacs Lisp

(with-current-buffer (url-retrieve-synchronously "http://www.stroustrup.com/C++.html")
  (while (re-search-forward "https?://[^\\\"]*")
    (print (match-string 0))))

2
Я немного разочарован, учитывая, насколько компактным и в высшей степени читаемым является этот код, что у него нет больше голосов. Отлично сработано.
Космический лось

28

Scala

"""\"(https?://.*?)\"""".r.findAllIn(scala.io.Source.fromURL("http://www.stroustrup.com/C++.html").mkString).foreach(println)

8
упаковать все в одну строку - C ++ тоже может это сделать
quetzalcoatl

Как насчет ftp://ftp.research.att.com/pub/c++std/WP/CD2?
Тобиас Кинцлер

22
@quetzalcoatl - это одно выражение , а не одна строка. Вы можете просто удалить все разрывы строк из кода C ++, но это не то же самое, что выполнить всю задачу в одном выражении.
Дао Вэнь

4
@DaoWen: Извините, но запуск выражений vs-line просто глупый. Добавьте несколько функторов и C ++, вы тоже можете это сделать. Но это только вопрос о том, какие библиотеки считаются «предоставленными» и имеют «нулевой код внутри». Это не меняет того факта, что упаковка в строку ухудшает читабельность. Можно сохранить его как единое выражение и просто переформатировать его в несколько строк, чтобы получить много и ничего не потерять, кроме ... количества строк. Это моя точка зрения. Глупая упаковка - C ++ тоже может это сделать. Если кто-то хочет выйти из поля «глупой упаковки», то следует отформатировать код для удобства чтения, а не для linecount.
Кетцалькоатль

3
@quetzalcoatl Тобиас не разместил там ссылку, чтобы мы могли перейти по ней. Он спрашивал автора этого ответа, почему это не было в его результатах.
JLRishe

25

PHP 5

<?php
preg_match_all('/"(https?:\/\/.*?)"/',file_get_contents('http://www.stroustrup.com/C++.html'),$m);
print_r($m[1]);

5
Предлагаемые изменения: '/"((http)s?://.*?)"/''|"((http)s?://.*?)"|'(в настоящее время ошибка); удалить array_unshift($m);(в настоящее время вы, скорее всего, имели в виду ошибку array_shift); print_r($m);print_r($m[1]);(выводить только URL-адреса).
Прим

исправлено, спасибо за ваш вклад
Дэвид Сюй

@DavidXu За исключением того, что вы не исправили это ...?
Шахар

теперь это исправлено!
Дэвид Сюй,

25

PowerShell

Текстовый поиск по всем полным URL - адресам (включая JavaScript, CSS и т. Д.):

[string[]][regex]::Matches((iwr "http://www.stroustrup.com/C++.html"), '\w+://[^"]+')

Или получить ссылки только в тегах привязки (включая относительные URL):

(iwr "http://www.stroustrup.com/C++.html").Links | %{ $_.href }

Укороченные версии из комментариев:

(iwr "http://www.stroustrup.com/C++.html").Links.href
(iwr "http://www.stroustrup.com/C++.html").Links.href-match":"

6
Если кто-то задается вопросом, iwrэто псевдоним для Invoke-WebRequest(PS3 +).
Athari

8
Вы можете злоупотребить стремлением PowerShell сгладить коллекции и сделать: (iwr "http://www.stroustrup.com/C++.html").Links.href(или (iwr "http://www.stroustrup.com/C++.html").Links.href-match":"только для абсолютных URI)
Матиас Р. Джессен

1
Это очень удобно!
Джастин Данлэп

22

D

import std.net.curl, std.stdio;
import std.algorithm, std.regex;

void main() {
foreach(_;byLine("http://www.stroustrup.com/C++.html")
    .map!((a)=>a.matchAll(regex(`<a.*?href="(.*)"`)))
    .filter!("a")){ writeln(_.front[1]); }
}

Для того, чтобы сделать список похож на оригинальный , например, вы могли бы трубу вывода программы через | sort | uniqили вместо добавления import std.arrayи изменить строку .filter!("a")){ writeln(_.front[1]); }в этом: .filter!("a").map!(a => a.front[1]).array.sort.uniq){ writeln(_); }. Заметьте, однако, что я только попробовал этот код и не доказал, что он правильный или «идиоматический». :)
Frg

22

Node.js

var http = require('http');

http.get('http://www.stroustrup.com/C++.html', function (res) {
    var data = '';
    res.on('data', function (d) {
        data += d;
    }).on('end', function () {
        console.log(data.match(/"https?:\/\/.*?"/g));
    }).setEncoding('utf8');
});

3
Интересно, require('http').getработает ли ? Если это так, то мы можем отказаться от оператора var и сократить другую строку.
Unihedron

@Unihedro Это делает.
TimWolla

9
@Unihedro Да, но это не соревнование по гольфу.
cPu1

Вам не нужно использовать какие-либо группы захвата.
Ry- 15.01.15

Я думаю, что это JavaScript, а не имя фреймворка.
mr5

20

Рубин

require 'net/http'
result = Net::HTTP.get(URI.parse('http://www.stroustrup.com/C++.html'))
result.scan(/"((http)s?://.*?)"/)

1
Ваше регулярное выражение не удастся, вам нужно использовать %r{"(https?://[^"]+)"}. Также вы можете использовать, Net::HTTP.get('www.stroustrup.com', '/C++.html')чтобы сократить запрос (и сохранить его читабельным). Поэтому весь код может быть в одной строке (удерживая ее чтения): puts Net::HTTP.get("www.stroustrup.com", "/C++.html").scan(%r{"(https?://[^"]+)"}). Запустите его, ruby -rnet/httpи вам даже не нужна require 'net/http'линия.
Хаулет

20

Haskell

Некоторые проблемы "\w"в Text.Regex.Posix

import Network.HTTP
import Text.Regex.Posix
pattern = "((http://)?www([./#\\+-][a-zA-Z]*)+)"
site = "http://www.stroustrup.com/C++.html"

main = do
    file <- getResponseBody =<< simpleHTTP (getRequest site)
    let result = getAllTextMatches $ file =~ pattern
    putStr $ unlines result -- looks nicer

Почему тип resultуказан явно? Это должно быть полностью ограничено его использованием в unlines.
Джон Дворак

1
Это немного растягивает правила, поскольку в пакете нет Network.HTTPни того, ни другого . (Хотя они находятся на платформе Haskell и, конечно, на Hackage, так что ...)TextRegex.Posixbase
перестал поворачиваться против часовой стрелки с

1
@JanDvorak, я начинаю писать в ghci (вероятно, я должен опубликовать его без изменений). Но ваша заметка актуальна, спасибо.
Властачу

@ leftaroundabout, не знал. Похоже, я бы не сделал, если бы использовал базовый пакет.
Властачу

networkне в baseтом, так что, за исключением того, чтобы свернуть ваши собственные привязки сокетов, нет никакого практического способа сделать это просто base.
Лямбда Фея

18

PHP

Насколько я могу судить, большинство современных установок PHP поставляются с обработкой DOM, поэтому вот тот, который фактически проходит через якоря внутри HTML:

foreach (@DOMDocument::loadHTMLFile('http://stroustrup.com/C++.html')->getElementsByTagName('a') as $a) {
    if (in_array(parse_url($url = $a->getAttribute('href'), PHP_URL_SCHEME), ['http', 'https'], true)) {
        echo $url, PHP_EOL;
    }
}

Внутренний цикл может быть сокращен до:

preg_match('~^https?://~', $url = $a->getAttribute('href')) && printf("%s\n", $url);

На самом деле хотел придумать это (как мой первый ответ здесь). Вы сделали это в первую очередь, вот ваш +1 (за то, что вы не используете Regex, склонный к ошибкам) Подсказка: вы могли бы использовать хромой 1вместо trueдля in_arrayстрогого поиска. Вы также можете опустить скобки. Я не совсем уверен, но, возможно, вы могли бы бросить httpи оставить только ://(идти без схемы). ,
Кайзер

И еще одна возможность - отказаться if ( ) {}в пользу in_array() and print $url.PHP_EOL. Но да, вы получите еще один +1 (если бы мог) для лучшей читабельности :)
kaiser

Только что попробовал ваш пример и получил ошибку для строгих стандартов (PHP 5.4). Похоже, что в источнике есть где-то поврежденная или неправильно отформатированная ссылка с пропущенной точкой с запятой. Вы можете отключить отчеты об ошибках с помощью @\DOMDocument. Только что попробовал и могу подтвердить, что это работает.
Кайзер

Нет, это документация, которая не так; технически вы не должны звонить ::loadHTMLFile()статически, а добавлять @только шкуры, которые являются артефактом.
Джек,

2
Это, безусловно, одно из самых «правильных» решений, одно из немногих, которое я видел в производстве. хорошая работа
Джордон Биондо

14

Unix Shell

wget -q -O - http://www.stroustrup.com/C++.html | sed -n '/http:/s/.*href="\([^"]*\)".*/\1/p' | sort

Хотя я должен признать, что это не сработает, если в строке более одной ссылки.


1
curl http://www.stroustrup.com/C++.htmlсохраняет несколько символов.
10

7
msgstr "но сторонние библиотеки не допускаются" . Я предполагаю, что поскольку wgetGNU (как и bash), вы можете утверждать, что он не является сторонним. Но, curlбезусловно, является сторонним.
Цифровая травма

Что о ftp://ftp.research.att.com/pub/c++std/WP/CD2и https://www.youtube.com/watch?v=jDqQudbtuqo&feature=youtu.be?
Тобиас Кинцлер

4
@TobiasKienzler Полагаю, оригинальный код Страуструпа их тоже не находит
Руслан

14

Ява

import java.util.regex.*;
class M{
    public static void main(String[]v)throws Throwable{
        Matcher m = Pattern.compile( "\"((http)s?://.*?)\"" )
            .matcher(
                 new Scanner(
                         new URL( "http://www.stroustrup.com/C++.html" )
                             .openStream(),
                         "UTF-8")
                     .useDelimiter("\\A")
                     .next());
        while(m.find())
            System.out.println(m.group());
    }
}

3
Не могли бы вы правильно отформатировать код в своих ответах? Это не соревнование за наименее читаемый код. Вы можете отформатировать его, чтобы избежать горизонтальных полос прокрутки по крайней мере.
Athari

Если вы используете a, Scannerвы можете сделать так, чтобы он обрабатывал шаблон регулярных выражений непосредственно для ссылок и просматривал Scannerрезультаты.
Хольгер

5
Да .. это Java для вас. Использование этого кода для гольфа - смелое начинание.
Джавадба

4
Никогда не думал, что увижу Java-решение, которое на самом деле короче, чем C ++!
Slebetman

2
Исправление к моему последнему комментарию: я должен признать, что это в значительной степени самый короткий и чистый код, который может быть написан на Java. Я попробовал подход синтаксического анализатора SAX, который может быть еще короче с лямбдами, но веб-страница не является XHTML, и анализатор создает исключения. Regex - единственный путь.
Мистер Смит

11

Groovy

"http://www.stroustrup.com/C++.html".toURL().text.findAll(/https?:\/\/[^"]+/).each{println it}

Может быть улучшен с помощью? оператор, чтобы избежать NPE?
Крис К

2
@ChrisKaminski и быть первым (рядом с Бьярне) здесь, чтобы проверить ошибки? никогда! кроме этого: я вижу только связанные с IO исключения здесь. где вы видите NPE?
cfrick

findAll () может вернуть ноль, нет? Или он вернет пустой список? Все еще немного нового в Groovy. EDIT: nm, похоже, что findAll () возвращает пустой список. Эти парни из Groovy были такими умными. :-)
Крис К

11

SQL (SQL Anywhere 16)

Определите хранимую процедуру для извлечения веб-страницы

CREATE OR REPLACE PROCEDURE CPPWebPage()
URL 'http://www.stroustrup.com/C++.html'
TYPE 'HTTP';

Создайте набор результатов, используя один запрос

SELECT REGEXP_SUBSTR(Value,'"https?://[^""]+"',1,row_num) AS Link  
FROM (SELECT Value FROM CPPWebPage() WITH (Attribute LONG VARCHAR, Value LONG VARCHAR) 
      WHERE Attribute = 'Body') WebPage, 
      sa_rowgenerator( 1, 256 ) 
WHERE Link IS NOT NULL;

Ограничения: это дает до 256 ссылок. Если существует больше ссылок, увеличьте 256 до соответствующего значения.


2
Я не верил, что в SQL будет гольф ... до сих пор.
vaxquis

Я понял ... "ссылки". :-)
Джек на SAP Canada

10

CoffeeScript / NodeJS

require('http').get 'http://www.stroustrup.com/C++.html', (r) ->
    dt = '';
    r.on 'data', (d) -> dt += d
    r.on 'end' , (d) -> console.log dt.match /"((http)s?:\/\/.*?)"/g

1
Я предполагаю, что это CoffeeScript / Node? Я думаю, вы должны указать, что ...
Джон Дворак

Ух ты. Это очень читабельно.
Slebetman

@ Slebetman это определенно маленький, хотя
Джон Дворак

@slebetman Да, CoffeeScript гораздо удобнее для чтения, чем JavaScript :) Я был рад избавиться от всех фигурных скобок} :)
RobAu

9

Perl

use LWP;
use feature 'say';

my $agent = new LWP::UserAgent();
my $response = $agent->get('http://www.stroustrup.com/C++.html');

say for $response->content =~ m<"(https?://.+?)">g;

1
Код был бы более понятным, если бы вы избегали переменных разделитель полей и разделитель записей и просто делали это: print map {"$ _ \ n"} $ response-> content = ~ m <"(https?: //.+ ?) "> г;
Даниэль Руосо

@DanielRuoso согласился.
Прим

или даже use v5.10;и say for $response->content...
Марк Рид

Полагаю, каждому свое. Некоторые из перенесенных функций perl6 были проблематичными (умное сопоставление, я смотрю на вас), но они sayвесьма полезны, и, на мой взгляд, более понятны здесь. (Кроме того, за последние 13 лет было довольно много совершенно не связанных с perl6ism улучшений в perl5; возможно, стоит попробовать.)
Марк Рид

@MarkReed Я согласен, что sayв этом случае, вероятно, более читабельным, особенно для тех, кто менее знаком с Perl.
Прим

9

р

html<-paste(readLines("http://www.stroustrup.com/C++.html"),collapse="\n")
regmatches(html,gregexpr("http[^([:blank:]|\\\"|<|&|#\n\r)]+",html))

... хотя R написан в основном на C ... так что, вероятно, несколько строк кода C за этими двумя строками кода R.


2
Это (или что-то подобное) верно для почти всех ответов здесь.
JLRishe

8

Objective-C

NSString *s;
for (id m in [[NSRegularExpression regularExpressionWithPattern:@"\"((http)s?://.*?)\"" options:0 error:nil] matchesInString:(s=[NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.stroustrup.com/C++.html"]])]){
    NSLog(@"%@",[s substringWithRange:[m range]]);
}

3
Какая? Пожалуйста, напишите версию Swift. Эта бессмысленная квадратная скобка ранит мои глаза :)
Мистер Смит,

2
Ура для []! Кроме того, мы должны полностью добавить версию Smalltalk;)
Bersaelor

@MisterSmith Swift ответ доступен здесь .
JAL

7

Tcl

package require http
set html [http::data [http::geturl http://www.stroustrup.com/C++.html]]
puts [join [regexp -inline -all {(?:http://)?www(?:[./#\+-]\w*)+} $html] \n]

Вы можете уйти, выполнив http :: data в путах. Нет необходимости создавать временную переменную. И я также отформатировал бы это, вставляя новые строки и отступы в каждом [. Но это выбор стиля.
Slebetman

7

Идти

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "regexp"
)

func main() {
    resp, err := http.Get("http://www.stroustrup.com/C++.html")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer resp.Body.Close()
    data, _ := ioutil.ReadAll(resp.Body)
    results := regexp.MustCompile(`https?://[^""]+`).FindAll(data, -1)
    for _, row := range results {
        fmt.Println(string(row))
    }
}

PS этот код считывает весь источник в память, поэтому рассмотрите возможность использования regexp.FindReaderIndexпоиска в потоке, что сделает приложение пуленепробиваемым.


6

CJam

CJam не имеет регулярных выражений, поэтому мне пришлось использовать другой подход в этом:

"http://www.stroustrup.com/C++.html"g''/'"*'"/(;2%{_"http://"#!\"https://"#!e|},N*

Сначала я конвертирую все 'в ", затем делю на все ", беру каждую альтернативную строку, а затем, наконец, фильтрую этот список для строк, начинающихся с http://или https://. После этого просто напечатайте каждую отфильтрованную строку в новой строке.

Попробуйте использовать интерпретатор Java, например

java -jar cjam-0.6.2.jar file.cjam

где file.cjam содержит содержимое кода выше.


9
Не знаю о читаемой части ... не знал, что Cjam имеет веб-функциональность
Def

Если вы хотите играть в гольф ... ''/'"f/:+для ''/'"*'"/'"f/0f=.
jimmy23013

... подожди, почему '"f/0f=там? Это должно что-то делать ( 2%например)?
jimmy23013

6

F #

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

open System.Net

let (|MatchValue|) (reMatch: Match) : string = reMatch.Value

let getHtml (uri : string) : string = 
    use webClient = WebClient() in
        let html : string = webClient.DownloadString(uri)
        html

let getLinks (uri : string) : string list =
    let html : string = getHtml uri
    let matches : MatchCollection = Regex.Matches(html, @"https?://[^""]+") 
    let links = [ for MatchValue reMatch in matches do yield reMatch ]
    links

let links = getLinks "http://www.stroustrup.com/C++.html" 
for link in links do
    Console.WriteLine(link)

Edit я сделал getLinks свою собственную функцию


Мне очень нравится, как вы использовали аннотации типов. Я думаю, что именование значений для описания того, что вы возвращаете, это нормально, но имя функции достаточно выразительно: getHTML и значение html, getLinks и значение ссылки. Последние две строки могут быть ссылками |> Seq.iter (printfn "% s")
MichalMa

@MichalMa Я согласен с тем, что имя функции само по себе достаточно выразительно, переменные html и links существуют по прагматическим причинам: так что есть где-то установить точку останова. Я использовал цикл for вместо List.iter только потому, что мне больше нравится, как он читается, хотя в ответе я, вероятно, использовал бы List.iter.
SourceSimian
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.