Desacreditando el mito de Stroustrup: "C ++ es solo para programas grandes y complicados"

161

Stroustrup ha publicado recientemente una serie de publicaciones que desacreditan mitos populares sobre C ++ . El quinto mito es: "C ++ es solo para programas grandes y complicados". Para desacreditarlo, escribió un sencillo programa en C ++ que descarga una página web y extrae enlaces de ella . Aquí está:

#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;
    }
}

Vamos a mostrarle a Stroustrup qué es un programa pequeño y legible .

  1. Descargar http://www.stroustrup.com/C++.html
  2. Listar todos los enlaces:

    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
    ...
    

Puede usar cualquier idioma, pero no se permiten bibliotecas de terceros.

Ganador

La respuesta de C ++ ganada por votos, pero se basa en una biblioteca de terceros (que no está permitida por las reglas) y, junto con otro competidor cercano, Bash , se basa en un cliente HTTP pirateado (no funcionará con HTTPS, gzip, redirecciones, etc.). Entonces Wolfram es un claro ganador. Otra solución que se acerca en términos de tamaño y legibilidad es PowerShell (con la mejora de los comentarios), pero no ha recibido mucha atención. Los lenguajes convencionales ( Python , C # ) también se acercaron bastante.

Athari
fuente
43
A cada uno lo suyo, me han llamado peor. Si el objetivo del OP no era intentar probar de alguna manera que Stroustrup está equivocado, entonces estaría de acuerdo con su evaluación. Pero la premisa completa de la pregunta es mostrar cómo "su lenguaje favorito" puede hacer lo mismo que estas 50 líneas de C ++ en muchas menos líneas de código. El problema es que ninguno de los ejemplos hace lo mismo. En particular, ninguna de las respuestas realiza ninguna comprobación de errores, ninguna de las respuestas proporciona funciones reutilizables, la mayoría de las respuestas no proporcionan un programa completo. El ejemplo de Stroustrup proporciona todo eso.
Dunk
19
Lo triste es que su página web ni siquiera es válida UTF-8 . Ahora tengo que solucionar eso, a pesar de la publicidad de su servidor Content-Type: text/html; charset=UTF-8... Voy a enviarle un correo electrónico.
Cornstalks
27
@Dunk Los otros ejemplos no proporcionan funciones reutilizables porque logran la funcionalidad completa de esas funciones en una sola línea y no tiene sentido hacer que una función completa por sí sola, y el ejemplo de C ++ no realiza ninguna comprobación de errores eso no se maneja de forma nativa de manera casi idéntica, y la frase "programa completo" es casi sin sentido.
Jason
16
"Puede usar cualquier idioma, pero no se permiten bibliotecas de terceros". No creo que sea un requisito justo teniendo en cuenta que boost/asiose usa allí, que es una biblioteca de terceros. Quiero decir, ¿cómo competirán los idiomas que no incluyen la obtención de url / tcp como parte de su biblioteca estándar?
greatwolf

Respuestas:

116

Wolfram

Esto se siente como una trampa completa

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

Así que solo agrega un análisis honesto en la parte superior

Cases[
 Import["http://www.stroustrup.com/C++.html", "XMLObject"],
 XMLElement["a", {___, "href" -> link_, ___}, ___] :> 
  link /; StringMatchQ[link, RegularExpression["((http://)?www([./#\\+-]\\w*)+)"]]
, Infinity]
silbido
fuente
49
No, no veo trampas aquí. Este desafío se trata de sacar lo mejor de tu idioma. Y esa primera línea es el epítome de "pequeño y legible".
Martin Ender
Una respuesta que puede ignorar los argumentos tontos sobre la captura de enlaces ftp. Brillante.
Seth Battin
Vine aquí para ofrecer esta solución exacta, me alegra ver que otros también la han apreciado.
Michael Stern el
@ MartinBüttner En ese caso, es posible que desee considerar el voto negativo meta.codegolf.stackexchange.com/a/1078/12130
David Mulder el
66
@DavidMulder Técnicamente, el vacío legal no es válido actualmente, ya que el desglose de votos es + 41 / -21 (y la pregunta del vacío legal dice que se aceptan vacíos legales si hay al menos el doble de votos positivos que votos negativos). Una llamada cercana, es cierto, pero aún así. ;) Además, este es un concurso de popularidad, no un código de golf, y en particular, es un pop-con sobre mostrar cuán fácilmente se puede hacer en un idioma dado, por lo que creo que la laguna no se aplica realmente a este desafío de todos modos (ya que el desafío básicamente lo pide).
Martin Ender
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";
}

La principal deficiencia es la naturaleza incómoda de boost :: asio, estoy seguro de que puede ser aún más corto con una mejor biblioteca.

congusbongus
fuente
166
Es curioso cómo "no hay bibliotecas de terceros" significa que Python aún puede import urllib2, C3 aún puede ser using System.Net, Haskel aún puede import Network.HTTP, pero un codificador de C ++ debe dar excusas #include <boost/asio.hpp>como si tuviera un crapton métrico de bibliotecas especializadas de C ++ (y C). disponible para elegir es algo de lo que avergonzarse solo porque el comité no se molestó en alimentarlo a la fuerza con uno específico ...
DevSolar
19
@DevSolar estuvo a punto de crear una segunda cuenta para darle otro voto a favor para ese comentario
usuario
15
@DevSolar System.Netno es forzado, es solo una biblioteca de alta calidad que sigue todas las recomendaciones de .NET incluidas con el lenguaje. Hay implementaciones alternativas, pero tener soporte HTTP en la biblioteca estándar significa que escribir aplicaciones simples es simple, significa una mejor interoperabilidad entre bibliotecas de terceros, significa menos dependencias, significa una fácil implementación para fachadas, etc. Imagine un mundo sin std::string, imagine cómo todos usan su propia biblioteca, imagina todas las dificultades que vienen con ella.
Athari
17
@DevSolar: nourllib2 es un tercero. Está en stdlib como <iostream>en C ++. urllib2en Python siempre está disponible a diferencia <boost/asio.hpp>de C ++. Si se nos permitiera usar módulos de terceros; Yo usaría lxmlo BeautifulSoupen Python.
jfs
22
Además, creo que el comentario más importante aquí es solo que C ++ no estandariza tantas cosas en sus bibliotecas estándar como otros lenguajes, pero todavía hay bibliotecas portátiles robustas ampliamente utilizadas para muchas de las mismas tareas que son estándar en lenguajes como Python, y algunas de estas bibliotecas son casi un estándar de facto. Y algo de esto es el resultado de que C ++ puede apuntar a sistemas embebidos con pequeños binarios y pequeñas bibliotecas.
Peter Cordes
85

Pure Bash en Linux / OS X (sin utilidades externas)

El software de cliente HTTP está notoriamente inflado. No queremos ese tipo de dependencias. En su lugar, podemos empujar los encabezados apropiados hacia abajo en una secuencia TCP y leer el resultado. No es necesario llamar a servicios arcaicos como grep o sed para analizar el resultado.

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

Meh: supongo que podría ser más legible ...

Trauma digital
fuente
1
Al igual que este, que utiliza identificadores de archivos Unix para las tuberías.
Java
2
Wow, nunca pensé que se podía hacer esto sin utilidades externas. Aunque parece que mi bash 3.2.17 en LFS es un poco obsoleto, por lo que no es compatible mapfile:)
Ruslan
@Ruslan Sí, mapfileviene con bash 4.x. Lo mismo es totalmente factible con un while readbucle también.
Trauma digital
3
@Ruslan Lo cambié a en while readlugar de mapfile. Más portátil y más legible, creo.
Trauma digital
1
¡Funciona también en OS X!
Alex Cohn
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

Cojo, pero funciona

eptgrant
fuente
99
¿Por qué no encadenar muchas de esas llamadas? l = re.findall('"((http)s?://.*?)"', u.urlopen(s).read())
Nombre falso
13
Es corto pero no es idiomático (la legibilidad cuenta en Python)
jfs
24
Hmmm ... si todo mi código ignora errores como este ejemplo, entonces el 75% al ​​90% de mi trabajo ya estaría hecho en cada proyecto en el que trabajo.
Dunk
20
@Dunk: Supongamos que el ejemplo captó alguna excepción (por ejemplo, de urlopen()). ¿Qué debería hacer con tal excepción, aparte de estrellarse y morir? Si se va a estrellar y morir de todos modos, ¿por qué no dejar que Python se encargue de los accidentes y las muertes, y dejar de lado el manejo de excepciones?
Kevin
8
@Dunk: Si estuviera usando el código Python de otra persona, preferiría que no detectaran urlopenerrores que (digamos) los atrapen y llamen sys.exit("something's borked!"). Si hacen lo último, tengo que atraparlo SystemExit, lo cual nunca es divertido.
Kevin
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);
    }
}
Athari
fuente
44
Puede usar var html, y probablemente var matchpara afeitarse algunos caracteres.
Excelente
15
@Superbest Puedo hacer nombres de un solo carácter y deshacerme de la htmlvariable por completo también, pero no es lo que busco.
Athari
66
@ Superbest no code-golf . : D
Kroltan
55
Bueno, también mejora la legibilidad. ¿Hay alguna razón para no usar varcuando no afectará la semántica del código?
Excelente
66
@Superbest: "mejora la legibilidad" es subjetiva. Personalmente, creo que establecer explícitamente el tipo de la variable mejora la legibilidad (generalmente, como en este código aquí). Sin embargo, no quiero debatir esto; Solo quiero señalar que existen puntos de vista alternativos.
Cornstalks
54

"Ningún tercero" es una falacia

Creo que la suposición de "no terceros" es una falacia. Y es una falacia específica que afecta a los desarrolladores de C ++, ya que es muy difícil crear código reutilizable en C ++. Cuando desarrolle algo, incluso si se trata de un script pequeño, siempre utilizará las piezas de código reutilizables disponibles.

La cuestión es que, en lenguajes como Perl, Python, Ruby (por nombrar algunos), reutilizar el código de otra persona no solo es fácil, sino que es cómo la mayoría de las personas realmente escriben código la mayor parte del tiempo.

C ++, con sus requisitos ABI casi imposibles de mantener, hace que sea un trabajo mucho más difícil, terminas con un proyecto como Boost, que es un depósito monstruoso de código y muy poca compostura fuera de él.

Un ejemplo de CPAN

Solo por diversión, aquí va un ejemplo basado en CPAN, con el análisis adecuado del html, en lugar de tratar de usar regex para analizar 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");
Daniel Ruoso
fuente
66
Voto por abordar el punto de las librerías de terceros, pero: mierda, hacer que el código reutilizable en C ++ sea tan fácil como en otro lenguaje. Utilizando y sobre todo encontrar reutilizable código puede ser un poco más difícil, pero la única cosa que es seriamente problemático es la reutilización compilados artefactos, pero que a menudo no sea un problema en lenguajes interpretados como Perl, etc
Martin Ba
44
Para estirar una analogía, Boost es más como CPAN: elige y elige. ¿No llamas a CPAN un "depósito monstruoso de código" solo porque hay muchas cosas allí que no usas?
Martin Ba
22
CPAN es un "depósito monstruoso de código", por cualquier definición razonable de esas cuatro palabras.
jwg
3
@MartinBa No estoy de acuerdo, ya que C ++ es un lenguaje compilado, que requiere que cada ejecutable reconstruya su pila completa de dependencias porque es difícil mantener la compatibilidad ABI dificulta seriamente la reutilización del código. Para producir una biblioteca reutilizable en C ++, debe pasar por largos tramos muy largos para asegurarse de no forzarse a realizar cambios incompatibles con ABI todo el tiempo.
Daniel Ruoso
66
@MartinBa porque tener que reconstruir todo el universo cada vez que quieres implementar una tarea simple es insoportable.
Daniel Ruoso
47

Shell de UNIX

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

También encuentra un ftp://enlace :)

Otra forma, sin depender de la ://sintaxis:

lynx -dump -listonly http://www.stroustrup.com/C++.html | sed -n 's/^[ 0-9.]\+//p'
Ruslan
fuente
38
No puedo determinar si hacer +1 porque usar un navegador web para descargar una página web es la herramienta adecuada para el trabajo o -1 porque el desafío es escribir un programa para hacer blahblahblah y acabas de llamar a un programa para hacer el blahing
David Richerby
2
Creo que es mejor reemplazar lynx con curl o wget. Se usan más comúnmente para descargar una página web.
Pavel Strakhov
44
@PavelStrakhov Elegí Lynx exactamente porque puede volcar los enlaces sin que yo haga nada especial :)
Ruslan
2
@SteveJessop por "especial" me refiero a en realidad analizar o regexing o lo que sea. Con lynx simplemente elimino la lista de enlaces (que curl y wget no enumeran) y elimino la numeración. Puede considerarlo hacer trampa o lo que sea, pero pensé que es divertido {utilizar la herramienta que hace casi perfectamente lo que se requiere}, simplemente ajustando la salida.
Ruslan
77
"pero no se permiten bibliotecas de terceros" . Sostengo que lynxes funcionalmente equivalente a una biblioteca de terceros en este escenario.
Trauma digital el
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;
}

Este código puede usarse como un estilo de usuario para mostrar solo enlaces absolutos en una página en una lista sin formato. Es posible que no funcione correctamente si su navegador exige un tamaño de fuente mínimo.

Funciona correctamente con http://www.stroustrup.com/C++.html(nota !importantsobre background). Para trabajar en otras páginas con más estilos, debe ampliarse (restablecer más propiedades, marcar propiedades como importantes, etc.).

Versión alternativa que incluye enlaces relativos, excepto los enlaces intrapágina que comienzan con hashes (lamentablemente, se basa en un enlace absoluto codificado):

* {
  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);
}
Athari
fuente
16
Esto es lo peor que he visto. +1
Emmett R.
1
Esto es hermoso y completamente horrible. +1
ricdesi
36

Clojure

(->> (slurp "http://www.stroustrup.com")
     (re-seq #"(?:http://)?www(?:[./#\+-]\w*)+"))
Adán
fuente
28
¡¿Sorber?! Necesito aprender Clojure.
11684
10
@ 11684 - Clojure también tiene funciones estándar nombradas spit, zippery lazy-cat... :-)
Bob Jarvis
2
Wow, creo que será una Resolución de Año Nuevo tardía. @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))))
Jordon Biondo
fuente
2
Estoy un poco decepcionado, dado lo compacto y eminentemente legible que es este código, que no tiene más votos. Bien hecho.
Spacemoose
28

Scala

"""\"(https?://.*?)\"""".r.findAllIn(scala.io.Source.fromURL("http://www.stroustrup.com/C++.html").mkString).foreach(println)
David Xu
fuente
8
empacar todo en una línea - C ++ también puede hacerlo
quetzalcoatl
¿Qué hay de ftp://ftp.research.att.com/pub/c++std/WP/CD2?
Tobias Kienzler
22
@quetzalcoatl: esta es una expresión , no solo una línea. Simplemente puede eliminar todos los saltos de línea del código C ++, pero eso no es lo mismo que hacer toda la tarea en una sola expresión.
DaoWen
44
@DaoWen: Lo siento, pero comenzar expresiones-vs-line se está volviendo tonto. Agregue algunos functores y C ++ también puede hacerlo. Pero esa es solo la cuestión de qué bibliotecas se consideran "otorgadas" y tienen "código cero adentro". No cambia el hecho de que empaquetarlo en una línea impida la legibilidad. Uno puede mantenerlo quieto como una sola expresión y simplemente formatearlo en unas pocas líneas para ganar mucho y perder nada más que ... recuento de líneas. Ese es mi punto. Embalaje tonto: C ++ también puede hacerlo. Si alguien quiere salir de la caja de "embalaje tonto", debe formatear el código para facilitar la lectura, no para el recuento de líneas.
quetzalcoatl
3
@quetzalcoatl Tobias no puso el enlace allí para que lo sigamos. Le preguntaba al escritor de esta respuesta por qué no estaba en sus resultados.
JLRishe
25

PHP 5

<?php
preg_match_all('/"(https?:\/\/.*?)"/',file_get_contents('http://www.stroustrup.com/C++.html'),$m);
print_r($m[1]);
David Xu
fuente
55
Ediciones sugeridas: '/"((http)s?://.*?)"/''|"((http)s?://.*?)"|'(actualmente un error); eliminar array_unshift($m);(actualmente es un error, probablemente quisiste decirlo en su array_shiftlugar); print_r($m);print_r($m[1]);(solo genera las URL).
primo
arreglado, gracias por tu aporte
David Xu
@DavidXu ¿Excepto que no lo arreglaste ...?
Shahar
ahora está arreglado.
David Xu
25

Potencia Shell

Búsqueda de texto para todas las URL totalmente calificadas (incluidos JavaScript, CSS, etc.):

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

O para obtener enlaces solo en etiquetas de anclaje (incluye URL relativas):

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

Versiones más cortas de los comentarios:

(iwr "http://www.stroustrup.com/C++.html").Links.href
(iwr "http://www.stroustrup.com/C++.html").Links.href-match":"
Justin Dunlap
fuente
66
Si alguien se pregunta, iwres un alias para Invoke-WebRequest(PS3 +).
Athari
8
Podrías abusar del afán de PowerShell de aplanar colecciones y hacer: (iwr "http://www.stroustrup.com/C++.html").Links.href(o (iwr "http://www.stroustrup.com/C++.html").Links.href-match":"solo para URIs absolutos)
Mathias R. Jessen
1
Eso es muy útil!
Justin Dunlap
22

re

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]); }
}
Kozzi11
fuente
Para que la lista similar a la original ejemplo, usted podría tubería de salida del programa a través de | sort | uniqo en lugar añadir import std.arrayy cambiar la línea .filter!("a")){ writeln(_.front[1]); }en esto: .filter!("a").map!(a => a.front[1]).array.sort.uniq){ writeln(_); }. Sin embargo, tenga en cuenta que solo he probado este código y no he demostrado que sea correcto o "idiomático". :)
Viernes
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');
});
cPu1
fuente
3
Me pregunto si require('http').getfunciona. Si es así, podemos deshacernos de la declaración var y acortar otra línea.
Unihedron
@Unihedro lo hace.
TimWolla
99
@Unihedro Sí, pero este no es un concurso de golf.
cPu1
No necesita utilizar ningún grupo de captura.
Ry-
Creo que es JavaScript en lugar de un nombre de marco.
mr5
20

Rubí

require 'net/http'
result = Net::HTTP.get(URI.parse('http://www.stroustrup.com/C++.html'))
result.scan(/"((http)s?://.*?)"/)
Yahor Zhylinski
fuente
1
Tu expresión regular fallará, debes usarla %r{"(https?://[^"]+)"}. También puede usar Net::HTTP.get('www.stroustrup.com', '/C++.html')para acortar la solicitud (y mantenerla legible). Así código entero puede estar en una línea (manteniéndolo legible): puts Net::HTTP.get("www.stroustrup.com", "/C++.html").scan(%r{"(https?://[^"]+)"}). Ejecútelo ruby -rnet/httpy ni siquiera necesita require 'net/http'línea.
Hauleth
20

Haskell

Algunos problemas con "\w"en 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
vlastachu
fuente
¿Por qué se resultespecifica el tipo de explícitamente? Debe estar completamente limitado por su uso en unlines.
John Dvorak
1
Esto estirar un poco las reglas, ya que ninguno Network.HTTP, ni TextRegex.Posixestán en el basepaquete. (Aunque están en la Plataforma Haskell, y por supuesto en Hackage, así que ...)
dejó de girar en sentido contrario a las agujas del reloj el
1
@ JanDvorak, empiezo a escribir en ghci (probablemente debería publicarlo sin cambios). Pero tu nota es relevante, gracias.
vlastachu
@leftaroundabout, no lo sabía. Parece que no podría haberlo hecho si hubiera usado el paquete base.
vlastachu
networkno está en baseninguno de los dos, así que ahorre para enrollar sus propios enlaces de socket, no hay una forma práctica de hacerlo con solo base.
Lambda Fairy
18

PHP

Por lo que puedo decir, la mayoría de las instalaciones PHP modernas vienen con procesamiento DOM, así que aquí hay una que realmente atraviesa los anclajes dentro del 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;
    }
}

El circuito interno podría acortarse a:

preg_match('~^https?://~', $url = $a->getAttribute('href')) && printf("%s\n", $url);
Jack
fuente
En realidad quería aparecer con esto (como mi primera respuesta aquí). Lo hiciste primero, ¡así que aquí está tu +1 (por no usar una expresión regular propensa a errores)! Sugerencia: podría usar un cojo en 1lugar de truepara la in_arraybúsqueda estricta. También puede omitir los corchetes. No estoy completamente seguro, pero iirc también podría soltar el httpy solo dejar el ://(ir sin el esquema). .
kaiser
Y: Otra posibilidad sería dejar caer if ( ) {}a favor de in_array() and print $url.PHP_EOL. Pero sí, obtendrías otro +1 (si pudiera) para una mejor legibilidad :)
kaiser
Acabo de probar su ejemplo y obtuve un error de estándares estrictos (PHP 5.4). Parece que en la fuente, hay un enlace dañado o formateado incorrectamente con un punto y coma faltante. Puede desactivar el informe de errores mediante el uso @\DOMDocument. Solo intenté eso y puedo confirmar que funciona.
kaiser
No, es la documentación lo que está mal; técnicamente no debes llamar ::loadHTMLFile()estáticamente, y agregar @solo oculta ese artefacto.
Jack
2
Esta es definitivamente una de las soluciones más "correctas", una de las únicas que pude ver en uso en la producción. buen trabajo
Jordon Biondo
14

Unix Shell

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

Aunque tengo que admitir que esto no funciona si hay más de un enlace en una línea.

Guntram Blohm
fuente
1
curl http://www.stroustrup.com/C++.htmlGuarda algunos caracteres.
l0b0
77
"pero no se permiten bibliotecas de terceros" . Supongo que dado que wgetes GNU (como es bash), se podría argumentar que no es un tercero. Pero curldefinitivamente es un tercero.
Trauma digital
¿Qué hay de ftp://ftp.research.att.com/pub/c++std/WP/CD2y https://www.youtube.com/watch?v=jDqQudbtuqo&feature=youtu.be?
Tobias Kienzler
44
@TobiasKienzler Supongo que el código original de Stroustrup tampoco los encuentra
Ruslan
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());
    }
}
David Xu
fuente
3
¿Podría formatear correctamente el código en sus respuestas? No es competencia por el código menos legible. Puede formatearlo para evitar al menos las barras de desplazamiento horizontales.
Athari
Si usa un Scanner, puede hacer que procese el patrón regex para enlaces directamente e iterar sobre los Scannerresultados de.
Holger
55
Sí ... eso es Java para ti. Usarlo para el golf de código es una empresa valiente.
Java
44
¡Nunca pensé que vería una solución Java que en realidad sea más corta que C ++!
slebetman
2
Corrección a mi último comentario: debo admitir que este es el código más corto y más limpio que se puede escribir en Java. He intentado un enfoque de analizador SAX, que podría hacerse aún más corto con lambdas, pero la página web no es XHTML y el analizador arroja excepciones. Regex es el único camino a seguir.
Señor Smith el
11

Maravilloso

"http://www.stroustrup.com/C++.html".toURL().text.findAll(/https?:\/\/[^"]+/).each{println it}
cfrick
fuente
¿Podría mejorarse usando? operador para evitar NPE?
Chris K
2
@ChrisKaminski y ser el primero (junto a Bjarne) por aquí para verificar si hay errores? ¡Nunca! además de eso: solo veo excepciones relacionadas con IO aquí. ¿Dónde ves un NPE?
cfrick
findAll () podría devolver nulo, ¿no? ¿O devolverá una lista vacía? Todavía un poco nuevo para Groovy. EDITAR: nm, parece que findAll () devuelve una lista vacía. Esos tipos Groovy eran muy inteligentes. :-)
Chris K
11

SQL (SQL en cualquier lugar 16)

Definir un procedimiento almacenado para recuperar la página web.

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

Produzca el conjunto de resultados usando una sola consulta

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;

Limitaciones: Esto produce hasta 256 enlaces. Si existen más enlaces, suba el 256 a un valor apropiado.

Jack en SAP Canada
fuente
2
No creía que hubiera golf en SQL ... hasta ahora.
vaxquis
Lo entiendo ... "enlaces". :-)
Jack en SAP Canadá
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
RobAu
fuente
1
Supongo que esto es CoffeeScript / Node? Supongo que deberías especificar eso ...
John Dvorak
Guau. Eso es muy legible.
slebetman
@slebetman definitivamente es pequeño, sin embargo
John Dvorak
@slebetman Yeah CoffeeScript es mucho más legible que JavaScript :) Me alegré de deshacerme de todas las llaves} :)
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;
primo
fuente
1
El código sería más claro si evitara las variables de separador de campo y de separador de registros y simplemente hiciera: print map {"$ _ \ n"} $ response-> content = ~ m <"(https?: //.+ ?) "> g;
Daniel Ruoso
@DanielRuoso estuvo de acuerdo.
primo
o incluso use v5.10;y say for $response->content...
Mark Reed
A cada uno lo suyo, supongo. Algunas de las características de perl6 con respaldo han sido problemáticas (coincidencia inteligente, te estoy mirando), pero sayes bastante útil y, en mi opinión, más claro aquí. (Además, ha habido muchas mejoras completamente no relacionadas con el perl6ismo en perl5 en los últimos 13 años; podría valer la pena echarle un vistazo.)
Mark Reed
@ MarkReed Estoy de acuerdo en que sayprobablemente sea más legible en este caso, particularmente para aquellos menos familiarizados con Perl.
primo
9

R

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

... aunque R está escrito principalmente en C ... así que probablemente unas pocas líneas de código C detrás de esas 2 líneas de código R.

Rusan Kax
fuente
2
Eso (o algo similar) es cierto para casi todas las respuestas aquí.
JLRishe
8

C objetivo

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]]);
}
David Xu
fuente
3
¿Qué? Por favor escriba la versión Swift. Esa tontería entre corchetes me está lastimando los ojos :)
Señor Smith
2
¡Hurra por []! Además, deberíamos agregar totalmente una versión Smalltalk;)
Bersaelor
La respuesta de @MisterSmith Swift ahora está disponible aquí .
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]
Damkerng T.
fuente
Puede escapar haciendo http :: data dentro de los Put. No es necesario crear una variable temporal. Y también lo formatearía poniendo nuevas líneas y sangría en cada [. Pero esa es una elección de estilo.
slebetman
7

Ir

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))
    }
}

PD: este código lee toda la fuente en la memoria, así que considera usarlo regexp.FindReaderIndexpara buscar en stream, eso hará que la aplicación sea a prueba de balas.

Maxim Kupriianov
fuente
6

CJam

CJam no tiene expresiones regulares, así que tuve que usar un enfoque diferente en este:

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

Primero convierto todo 'a ", luego me divido en todos ", tomo cada cadena alternativa y finalmente filtro esa lista para las cadenas que comienzan con http://o https://. Después de eso, simplemente imprima cada cadena filtrada en una nueva línea.

Pruébelo utilizando el intérprete de Java como

java -jar cjam-0.6.2.jar file.cjam

donde file.cjam tiene el contenido del código anterior.

Optimizador
fuente
99
No sé sobre la parte legible ... no sabía que Cjam tiene funcionalidad web
Def
Si quieres jugar al golf ... ''/'"f/:+para ''/'"*'"/'"f/0f=.
jimmy23013
... espera ¿por qué '"f/0f=hay? ¿Se supone que eso debe hacer algo ( 2%por ejemplo)?
jimmy23013
6

F#

Este código podría ser mucho más corto, pero escribiría algo como esto si alguna vez esperaba tener que leer o usar este código nuevamente, por lo que tiene muchas anotaciones de tipo innecesarias. Demuestra el uso de un patrón MatchValue activo para permitir la coincidencia de patrones con el tipo estándar 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)

Editar hice que getLinks tenga su propia función

SourceSimian
fuente
Realmente me gusta cómo usaste anotaciones de tipo. Creo que nombrar valores para describir lo que devuelve está bien, pero el nombre de la función es lo suficientemente expresivo: valor getHTML y html, valor getLinks y enlaces. Las dos últimas líneas pueden ser enlaces |> Seq.iter (printfn "% s")
MichalMa
@MichalMa Estoy de acuerdo en que el nombre de la función es lo suficientemente expresivo por sí solo, las variables html y links están ahí por razones pragmáticas: por lo tanto, hay un lugar para establecer un punto de interrupción. Usé el bucle for en lugar de List.iter solo porque me gusta la forma en que lee más, aunque en una respuesta probablemente hubiera usado List.iter.
SourceSimian