Розвінчання Струструпа розвінчанням міфу "C ++ - це лише для великих, складних програм"


161

Нещодавно Stroustrup опублікував серію дописів, де розкриваються популярні міфи про 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;
    }
}

Давайте покажемо Stroustrup, що насправді є маленькою та читаною програмою.

  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 ++ у набагато менших рядках коду. Проблема полягає в тому, що жоден із прикладів не робить те саме. Зокрема, жодна з відповідей не здійснює перевірки помилок, жодна з них не забезпечує функцій багаторазового використання, більшість відповідей не передбачають повноцінної програми. Приклад Stroustrup пропонує все це.
Данк

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

27
@Dunk Інші приклади не містять функцій багаторазового використання, оскільки вони виконують всю функціональність цих функцій в одному рядку, і немає сенсу робити цілу функцію самостійно, а приклад C ++ не виконує перевірки помилок це не обробляється на самому рівні майже однаково, і фраза "повна програма" майже безглузда.
Джейсон

16
"Ви можете використовувати будь-яку мову, але сторонні бібліотеки заборонені." Я не думаю , що це справедлива вимога з урахуванням boost/asioвикористовуються там , який є бібліотекою третьої сторони. Я маю на увазі, як змагатимуться мови, які не включають отримання URL-адреси url / tcp як частину його стандартної бібліотеки?
greatwolf

Відповіді:


115

Вольфрам

Це відчувається як повне обман

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-посилань. Блискуча.
Сет Баттін

Сюди приїхали запропонувати саме це рішення, із задоволенням побачили, що і інші оцінили його.
Майкл Стерн

@ MartinBüttner У цьому випадку ви можете розглянути питання про заборону
Девід Малдер

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, Хаскель все ще може бути import Network.HTTP, але C ++-кодер повинен виправдовуватися #include <boost/asio.hpp>, ніби маючи метричний краптон спеціалізованих, спеціально побудованих C ++ (і C!) Бібліотек Доступне для обраного - це те, чого слід соромитися лише тому, що комітет не
покладав сили наситити

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

15
@DevSolar System.Netне змушений, це просто високоякісна бібліотека, яка дотримується всіх рекомендацій .NET, що входять до мови. Там ви альтернативні реалізації, але з підтримкою HTTP в стандартній бібліотеці означає написання простих додатків , просто, означають кращу взаємодію між сторонніми бібліотеками, тим менше залежностей, означає просто реалізація фасадів і т.д. Уявіть собі світ без std::string, уявіть , як кожен використовує власну бібліотеку, уявіть усі труднощі, які виникають із нею.
Афарій

17
@DevSolar: неurllib2 є третьою стороною. Це в stdlib, як у C ++. в Python завжди доступний на відміну від C ++. Якби нам дозволяли використовувати сторонні модулі; Я б використовував або в Python. <iostream>urllib2<boost/asio.hpp>lxmlBeautifulSoup
jfs

22
Крім того, я думаю, що найважливіший коментар тут полягає лише в тому, що C ++ не стандартизує стільки матеріалів у своїх стандартних бібліотеках, як інші мови, але все ще є широко використовувані надійні портативні бібліотеки для багатьох тих самих завдань, які є стандартними для мов як пітон, і деякі з цих ліб майже не є фактичним стандартом. І це є результатом того, що 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 для ручок для труб.
javadba

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

@Ruslan Yep, mapfileпоставляється з bash 4.x. Те саме можна виконати і з while readциклом.
Цифрова травма

3
@Ruslan Я змінив його на, while readа не на mapfile. Я думаю, що більш портативний та легкий для читання.
Цифрова травма

1
Працює і в OS X!
Алекс Кон

65

Пітон 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відголити кілька символів.
Супербест

15
@Superbest Я можу зробити імена односимвольними і htmlвзагалі позбутися змінної, але це не те, що я переслідую.
Афарі

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

5
Що ж, це також покращує читабельність. Чи є колись причина не використовувати, varколи це не вплине на семантику коду?
Супербест

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
Заявка на адресу точки сторонніх ліб, але: лайно, перетворення коду для багаторазового використання на C ++ так само легко, як і на іншій мові. Використовувати та особливо знаходити код для багаторазового використання може бути складніше, але єдине, що серйозно проблематично, - це повторне використання зібраних артефактів, але це часто не проблема в інтерпретованих мовах, таких як Perl тощо.
Мартін Ба,

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, оскільки завдання полягає в тому, щоб написати програму для виконання blahblahblah, і ви щойно зателефонували в програму вигук.
Девід Річербі

2
Я думаю, що краще замінити рись на завиток або wget. Вони частіше використовуються для завантаження веб-сторінки.
Павло Страхов

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

2
@SteveJessop під "спеціальним" я маю на увазі насправді розбір або повторне вирівнювання або що завгодно. За допомогою рисі я просто витискаю список посилань (які згортання та wget не списку) та видаляю нумерацію. Ви можете вважати це обманом чи будь-яким іншим, але я подумав, що це цікаво {використовувати інструмент, який майже ідеально виконує те, що потрібно}, просто налагоджуючи вихід.
Руслан

7
"але сторонні бібліотеки заборонені" . Я стверджую, що 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
ricdesi

36

Clojure

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

27
Сірка ?! Мені потрібно навчитися 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
Я трохи розчарований, враховуючи, наскільки компактний і чітко читабельний цей код, що він не має більше голосів. Молодці.
Spacemoose

28

Скала

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


Про що ftp://ftp.research.att.com/pub/c++std/WP/CD2?
Тобіас Кіенцлер

22
@quetzalcoatl - Це один вираз , а не лише один рядок. Ви можете просто видалити всі розриви рядків із коду C ++, але це не те саме, що виконувати все завдання в одному виразі.
DaoWen

4
@DaoWen: Вибачте, але починати вирази проти рядка просто нерозумно. Додайте кілька функторів і C ++ ви також можете це зробити. Але це лише питання про те, які лібри вважаються "наданими" та мають "нульовий код всередині". Це не змінює того факту, що упаковка його в рядок призводить до читабельності. Можна зберегти його як єдиний вираз і просто переформатувати його в кілька рядків, щоб отримати багато і втратити нічого, крім .. кількість рядків. Це моя суть. Нерозумна упаковка - C ++ теж може це зробити. Якщо хтось хоче вийти з поля "дурне пакування", тоді слід відформатувати код для читабельності, а не лінійки.
quetzalcoatl

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 +).
Афарій

8
Ви можете зловживати бажанням PowerShell згладжувати колекції і робити: (iwr "http://www.stroustrup.com/C++.html").Links.href(або (iwr "http://www.stroustrup.com/C++.html").Links.href-match":"лише для абсолютних URI)
Mathias R. Jessen

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(_); }. Зауважте, що я лише спробував цей код і не довів, що він є правильним чи "ідіоматичним". :)
Фр

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 і скоротити інший рядок.
Уніхедрон

@Unihedro Це так.
TimWolla

9
@Unihedro Це так, але це не змагання з гольфу.
cPu1

Вам не потрібно використовувати жодні групи захоплення.
Ри-

Я думаю, що це 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'лінія.
Hauleth

20

Хаскелл

Деякі проблеми з "\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ні TextRegex.Posixїх немає base. (Хоча вони знаходяться на платформі Haskell, і, звичайно, на Hackage, тому ...)
перестали повертати проти годинника,

1
@JanDvorak, я починаю писати в ghci (напевно, я повинен розміщувати це без змін). Але ваше запитання актуальне, дякую.
vlastachu

@leftaroundabout, не знав. Схоже, я не міг би цього зробити, якби використав базовий пакет.
vlastachu

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суворого пошуку. Ви також можете опустити дужки. Я не зовсім впевнений, але iirc ви також можете скинути 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економить кілька символів.
l0b0

7
"але сторонні бібліотеки заборонені" . Я здогадуюсь, оскільки wgetGNU (як це баш), ви можете стверджувати, що він не є стороннім. Але, curlбезумовно, стороннє.
Цифрова травма

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

4
@TobiasKienzler Я думаю, що оригінальний код Stroustrup теж не знайде їх
Руслан

14

Java

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
Чи можете ви правильно відформатувати код у своїх відповідях? Це не конкуренція за найменш читабельний код. Ви можете відформатувати його, щоб принаймні уникнути горизонтальних смуг прокрутки.
Афарі

Якщо ви використовуєте a, Scannerви можете змусити його обробляти схему регулярних виразів для посилань безпосередньо і перебирати Scannerрезультати.
Хольгер

5
Так .. ява для вас. Використовувати його для коду гольфу - це сміливе починання.
javadba

4
Ніколи не думав, що побачу рішення Java, яке насправді коротше C ++!
slebetman

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

11

Groovy

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

Можна вдосконалити за допомогою ?. оператор, щоб уникнути NPE?
Кріс К

2
@ChrisKaminski і будьте першим (поруч із Bjarne), який перевірив помилки? ніколи! крім цього: тут я бачу лише винятки, пов'язані з IO. де ти бачиш NPE?
cfrick

findAll () може повернути нуль, ні? Або поверне порожній список? Ще трохи новий для Groovy. EDIT: nm, схоже на findAll () повертає порожній список. Ті грові хлопці були такі розумні. :-)
Кріс К

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
Код був би більш зрозумілим, якби ви уникали змінних поля та роздільника записів і просто зробили: надрукувати карту {"$ _ \ n"} $ response-> content = ~ m <"(https?: //.+ ?) "> г;
Даніель Руосо

@DanielRuoso погодився.
примо

або навіть use v5.10;і say for $response->content...
Марк Рід

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

@MarkReed Я погоджуюся, що say, мабуть, в цьому випадку читається, особливо для тих, хто менш знайомий з perl.
прим

9

R

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

... хоча R написано головним чином на C ... тому, ймовірно, кілька рядків коду C позаду цих 2 рядків коду R.


2
Це (або щось подібне) справедливо для всіх відповідей тут.
JLRishe

8

Ціль-С

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 :: дані всередині ставок. Не потрібно створювати тимчасову змінну. І я також відформатувати його, розмістивши нові рядки та відступи на кожному [. Але це вибір стилю.
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 типу Match

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)

Редагувати Я зробив getLinks власну функцію


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

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