Интересно, почему никто не упомянул вариант PERLDB_OPTS
, названный RemotePort
; хотя, по общему признанию, в Интернете мало рабочих примеров (RemotePort
даже не упоминается в perldebug ) - и для меня было довольно проблематично придумать это, но здесь оно (это пример Linux).
Чтобы сделать правильный пример, сначала мне понадобилось что-то, что могло бы сделать очень простое моделирование веб-сервера CGI, желательно через одну командную строку. После нахождения Простой веб-сервер командной строки для запуска cgis. (perlmonks.org) , я нашел для этого теста IO :: All - A Tiny Web Server .
Здесь я буду работать в каталог /tmp
; сценарий CGI будет /tmp/test.pl
(см. ниже). Обратите внимание, что сервер IO::All
будет обслуживать только исполняемые файлы в том же каталоге, что и CGI, поэтому здесь требуется chmod +x test.pl
. Итак, чтобы выполнить обычный тестовый прогон CGI, я меняю каталог на /tmp
в терминале и запускаю там один веб-сервер:
$ cd /tmp
$ perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
Команда webserver будет блокироваться в терминале, и в противном случае запустит веб-сервер локально (на 127.0.0.1 или localhost
) - впоследствии я могу перейти в веб-браузер и запросить этот адрес:
http://127.0.0.1:8080/test.pl
... и я должен обратите внимание на print
s, сделанные с помощью test.pl
, загружаемых - и показаны - в веб-браузере.
Теперь, чтобы отладить этот скрипт с RemotePort
, сначала нам понадобится слушателя в сети, через который мы будем взаимодействовать с отладчиком Perl; мы можем использовать инструмент командной строки netcat
(nc
, увиденный здесь: Perl 如何 удаленная отладка? ). Итак, сначала запустите прослушиватель netcat
в одном терминале, где он будет блокировать и ждать подключения на порту 7234 (который будет нашим портом отладки):
$ nc -l 7234
Затем нам нужно perl
для запуска в режиме отладки с RemotePort
, когда test.pl
был вызван (даже в режиме CGI, через сервер). Это можно сделать в Linux, используя следующий сценарий «shebang wrapper», который также должен находиться в /tmp
, а должен быть выполнен :
cd /tmp
cat > perldbgcall.sh <<'EOF'
#!/bin/bash
PERLDB_OPTS="RemotePort=localhost:7234" perl -d -e "do '$@'"
EOF
chmod +x perldbgcall.sh
Это своего рода сложная вещь - см. Сценарий оболочки - Как я могу использовать переменные среды в моем shebang? - Unix & amp; Linux Stack Exchange . Но трюк здесь, кажется, не , чтобы развить интерпретатор perl
, который обрабатывает test.pl
- поэтому, как только мы его ударим, мы не exec
, но вместо этого мы вызываем perl
«прямо» и в основном «источник» нашего скрипта test.pl
с использованием do
(см. . Как запустить скрипт Perl из скрипта Perl? ).
Теперь что у нас есть perldbgcall.sh
в /tmp
- мы можем изменить файл test.pl
, так что он ссылается на этот исполняемый файл на своей строке shebang (вместо обычного интерпретатора Perl) - здесь /tmp/test.pl
изменено таким образом:
#!./perldbgcall.sh
# this is test.pl
use 5.10.1;
use warnings;
use strict;
my $b = '1';
my $a = sub { "hello $b there" };
$b = '2';
print "YEAH " . $a->() . " CMON\n";
$b = '3';
print "CMON " . &$a . " YEAH\n";
$DB::single=1; # BREAKPOINT
$b = '4';
print "STEP " . &$a . " NOW\n";
$b = '5';
print "STEP " . &$a . " AGAIN\n";
Теперь оба test.pl
и его новый обработчик shebang, perldbgcall.sh
, находятся в /tmp
; и мы nc
слушаем отладочные соединения на порту 7234 - поэтому мы можем, наконец, открыть другое окно терминала, сменить каталог на /tmp
и запустить веб-сервер с одним слоем (который будет прослушивать веб-соединения на порту 8080):
cd /tmp
perl -MIO::All -e 'io(":8080")->fork->accept->(sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / })'
После этого мы можем перейти в наш веб-браузер и запросить тот же адрес, http://127.0.0.1:8080/test.pl
. Однако теперь, когда веб-сервер пытается выполнить скрипт, он сделает это через perldbgcall.sh
shebang - который запустит perl
в режиме удаленного отладчика. Таким образом, выполнение скрипта приостанавливается - и поэтому веб-браузер блокируется, ожидая данных. Теперь мы можем переключиться на терминал netcat
, и мы должны увидеть знакомый текст отладчика Perl, однако вывести его через nc
:
$ nc -l 7234
Loading DB routines from perl5db.pl version 1.32
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(-e:1): do './test.pl'
DB<1> r
main::(./test.pl:29): $b = '4';
DB<1>
. Как показывает фрагмент, теперь мы в основном используем nc
как «терминал», поэтому мы можем ввести r
(и Enter) для «run» - и скрипт будет запускаться с помощью инструкции breakpoint (см. также В perl, в чем разница между $ DB: : single = 1 и 2? ), прежде чем снова остановиться (обратите внимание, что в этот момент браузер все равно заблокируется).
Итак, теперь мы можем, скажем, пройти через остальные test.pl
через терминал nc
:
....
main::(./test.pl:29): $b = '4';
DB<1> n
main::(./test.pl:30): print "STEP " . &$a . " NOW\n";
DB<1> n
main::(./test.pl:31): $b = '5';
DB<1> n
main::(./test.pl:32): print "STEP " . &$a . " AGAIN\n";
DB<1> n
Debugged program terminated. Use q to quit or R to restart,
use o inhibit_exit to avoid stopping after program termination,
h q, h R or h o to get additional info.
DB<1>
... однако, также в этот момент браузер блокирует и ждет данных. Только после выхода из отладчика с q
:
DB<1> q
$
... блокирует блокировку браузера и, наконец, отображает (полный) вывод test.pl
:
YEAH hello 2 there CMON
CMON hello 3 there YEAH
STEP hello 4 there NOW
STEP hello 5 there AGAIN
Конечно, такой отладочный процесс можно сделать даже без запуска веб-сервера, однако, в этом случае, мы не трогаем веб-сервер; мы запускаем выполнение «изначально» (для CGI) из веб-браузера - и единственным изменением, необходимым для самого скрипта CGI, является изменение shebang (и, конечно же, наличие скрипта оболочки shebang в качестве исполняемого файла в том же директория).
Ну, надеюсь, это поможет кому-то - я бы наверняка любил бы наткнуться на это, вместо того, чтобы писать сам :)
Cheers!
Я хотел посмотреть, какое из предложенных решений работает лучше всего, поэтому провел несколько сравнительных тестов. Ради интереса я также сравнил методы LINQ с простым старым методом System.Xml , предложенным Грегом. Вариант оказался интересным и не таким, как я ожидал: самые медленные методы были более чем в 3 раза медленнее, чем самый быстрый .
Результаты отсортированы от самого быстрого к самому медленному:
Метод
Я использовал один XML-документ с 20 идентичными узлами (называемый «подсказкой»):
<hint>
<strong>Thinking of using a fake address?</strong>
<br />
Please don't. If we can't verify your address we might just
have to reject your application.
</hint>
Числа, показанные в секундах выше, являются результатом извлечения «внутреннего XML» 20 узлов, 1000 раз подряд и взяв среднее значение из 5 прогонов. Я не включил время, необходимое для загрузки и анализа XML в XmlDocument
(для метода System.Xml ) или XDocument
(для всех другие).
Я использовал следующие алгоритмы LINQ: (C # - все берут XElement
«родительский» и возвращают внутреннюю строку XML)
CreateReader:
var reader = parent.CreateReader();
reader.MoveToContent();
return reader.ReadInnerXml();
Агрегировать со строкой конкатенация:
return parent.Nodes().Aggregate("", (b, node) => b += node.ToString());
StringBuilder:
StringBuilder sb = new StringBuilder();
foreach(var node in parent.Nodes()) {
sb.Append(node.ToString());
}
return sb.ToString();
String.Join в массиве:
return String.Join("", parent.Nodes().Select(x => x.ToString()).ToArray());
String.Concat в массиве:
return String.Concat(parent.Nodes().Select(x => x.ToString()).ToArray());
Я не показал здесь алгоритм «Обычный старый System.Xml» как он ' s просто вызывает .InnerXml на узлах.
Заключение
Если производительность важна (например, много XML, часто анализируемого), я бы использовал метод Дэниела CreateReader
каждый раз . Если вы просто выполняете несколько запросов, вы можете использовать более лаконичный метод Aggregate Майка.
Если вы используете XML для больших элементов с большим количеством узлов (может быть, 100 узлов), вы, вероятно, начнете видеть преимущество использования StringBuilder
над методом Aggregate, но не над CreateReader
. Я не думаю, что методы Join
и Concat
когда-либо были бы более эффективными в этих условиях из-за штрафов за преобразование большого списка в большой массив (даже очевидно здесь с меньшими списками ).
Заключение
Если производительность важна (например, много XML, часто анализируемого), я бы использовал метод Дэниела CreateReader
каждый раз . Если вы просто выполняете несколько запросов, вы можете использовать более краткий метод Майка Aggregate.
Если вы используете XML для больших элементов с большим количеством узлов (может быть, 100 узлов), вы, вероятно, начнете видеть преимущество использования StringBuilder
над методом Aggregate, но не над CreateReader
. Я не думаю, что методы Join
и Concat
когда-либо были бы более эффективными в этих условиях из-за штрафов за преобразование большого списка в большой массив (даже очевидно здесь с меньшими списками ).
Заключение
Если производительность важна (например, много XML, часто анализируемого), я бы использовал метод Дэниела CreateReader
каждый раз . Если вы просто выполняете несколько запросов, вы можете использовать более краткий метод Майка Aggregate.
Если вы используете XML для больших элементов с большим количеством узлов (может быть, 100 узлов), вы, вероятно, начнете видеть преимущество использования StringBuilder
над методом Aggregate, но не над CreateReader
. Я не думаю, что методы Join
и Concat
когда-либо были бы более эффективными в этих условиях из-за потери преобразования большого списка в большой массив (даже очевидно здесь с меньшими списками ).
CreateReader
каждый раз . Если вы просто выполняете несколько запросов, вы можете использовать более лаконичный метод Aggregate Майка.
Если вы используете XML для больших элементов с большим количеством узлов (может быть, 100 узлов), вы, вероятно, начнете видеть преимущество использования StringBuilder
над методом Aggregate, но не над CreateReader
. Я не думаю, что методы Join
и Concat
когда-либо были бы более эффективными в этих условиях из-за штрафов за преобразование большого списка в большой массив (даже очевидно здесь с меньшими списками ).
CreateReader
каждый раз . Если вы просто выполняете несколько запросов, вы можете использовать более лаконичный метод Aggregate Майка.
Если вы используете XML для больших элементов с большим количеством узлов (может быть, 100 узлов), вы, вероятно, начнете видеть преимущество использования StringBuilder
над методом Aggregate, но не над CreateReader
. Я не думаю, что методы Join
и Concat
когда-либо были бы более эффективными в этих условиях из-за потери преобразования большого списка в большой массив (даже очевидно здесь с меньшими списками ).
Если вы используете XML для больших элементов с большим количеством узлов (возможно, 100), вы, вероятно, начнете видеть преимущество использования StringBuilder
по сравнению с методом Aggregate, но не более CreateReader
. Я не думаю, что методы Join
и Concat
когда-либо были бы более эффективными в этих условиях из-за штрафов за преобразование большого списка в большой массив (даже очевидно здесь с меньшими списками ).
Если вы используете XML для больших элементов с большим количеством узлов (возможно, 100), вы, вероятно, начнете видеть преимущество использования StringBuilder
по сравнению с методом Aggregate, но не более CreateReader
. Я не думаю, что методы Join
и Concat
когда-либо были бы более эффективными в этих условиях из-за потери преобразования большого списка в большой массив (даже очевидно здесь с меньшими списками ).
Действительно ли возможно использовать объекты пространства имен System.Xml сделать задание здесь вместо того, чтобы использовать LINQ? Поскольку Вы уже упомянули, XmlNode. InnerXml точно, в чем Вы нуждаетесь.
Я закончил тем, что использовал это:
Body = t.Element("body").Nodes().Aggregate("", (b, node) => b += node.ToString());
Как насчет того, чтобы использовать этот "дополнительный" метод на XElement? работавший для меня!
public static string InnerXml(this XElement element)
{
StringBuilder innerXml = new StringBuilder();
foreach (XNode node in element.Nodes())
{
// append node's xml string to innerXml
innerXml.Append(node.ToString());
}
return innerXml.ToString();
}
ИЛИ использование определенное примечание Linq
public static string InnerXml(this XElement element)
{
StringBuilder innerXml = new StringBuilder();
doc.Nodes().ToList().ForEach( node => innerXml.Append(node.ToString()));
return innerXml.ToString();
}
: код выше должен использовать element.Nodes()
в противоположность element.Elements()
. Очень важная вещь помнить различие между двумя. element.Nodes()
дает Вам все как XText
, XAttribute
и т.д., но XElement
только Элемент.
@Greg: кажется, что Вы отредактировали свой ответ, чтобы быть совершенно другим ответом. К которому мой ответ да, я мог сделать это использование System.Xml, но надеялся намочить ноги с LINQ к XML.
я оставлю свой исходный ответ ниже в случае, если кто-либо еще задается вопросом, почему я не могу только использовать.Value свойство XELEMENT для получения то, в чем я нуждаюсь:
@Greg: свойство Value связывает все текстовое содержание любых дочерних узлов. Таким образом, если элемент тела содержит только текст, это работает, но если это содержит XHTML, я получаю весь текст, связанный вместе, но ни один из тегов.
Удивление, если (замечают, что я избавился от b + = и просто имею b +)
t.Element( "body" ).Nodes()
.Aggregate( "", ( b, node ) => b + node.ToString() );
могло бы быть немного менее эффективным, чем
string.Join( "", t.Element.Nodes()
.Select( n => n.ToString() ).ToArray() );
Не 100%, уверенных..., но глядящий на Агрегат () и строка. Соединение () в Отражателе... Я думаю , я считал его как Агрегат, просто добавляющий значение возврата, поэтому по существу Вы добираетесь:
строка = представляют в виде строки + строка
по сравнению со строкой. Соединение, это имеет некоторое упоминание там о FastStringAllocation или чем-то, которое делает меня вещью, люди в Microsoft, возможно, поместили некоторое дополнительное повышение производительности там. Конечно, мои.ToArray () называют мой инвертировать это, но я просто хотел предложить другое предложение.
Я думаю, что это - намного лучший метод (в VB, не должно быть твердо перевести):
, Учитывая XElement x:
Dim xReader = x.CreateReader
xReader.MoveToContent
xReader.ReadInnerXml
знаете? Лучше всего вернуться к CDATA :( Я смотрю на решения здесь, но я думаю, что CDATA, безусловно, самый простой и дешевый, не самый удобный для разработки с помощью tho
Сохраняйте простоту и эффективность:
String.Concat(node.Nodes().Select(x => x.ToString()).ToArray())
Лично я закончил тем, что написал метод расширения InnerXml
с использованием метода Aggregate:
public static string InnerXml(this XElement thiz)
{
return thiz.Nodes().Aggregate( string.Empty, ( element, node ) => element += node.ToString() );
}
Мой клиентский код будет таким же кратким, как и в случае со старым пространством имен System.Xml:
var innerXml = myXElement.InnerXml();
public static string InnerXml(this XElement xElement)
{
//remove start tag
string innerXml = xElement.ToString().Trim().Replace(string.Format("<{0}>", xElement.Name), "");
////remove end tag
innerXml = innerXml.Trim().Replace(string.Format("</{0}>", xElement.Name), "");
return innerXml.Trim();
}