def doit(str) r = /\A#{"(#\\d)\\s*"*str.count('#')}\z/ str.match(r)&.captures end doit "#1#2 #3 " #=> ["#1", "#2", "#3"] doit " #1#2 #3 " #=> nil
Обратите внимание, что регулярные выражения зависят только от количества экземпляров символа
'#'
в строке. Поскольку в обоих примерах это число равно трем, соответствующие регулярные выражения равны, а именно:/\A(#\d)\s*(#\d)\s*(#\d)\s*\z/
Это регулярное выражение было построено следующим образом.
str = "#1#2 #3 " n = str.count('#') #=> 3 s = "(#\\d)\\s*"*n #=> "(#\\d)\\s*(#\\d)\\s*(#\\d)\\s*" /\A#{s}\z/ #=> /\A(#\d)\s*(#\d)\s*(#\d)\s*\z/
Регулярное выражение гласит: «соответствует началу строки, за которым следуют три идентичные группы захвата, за каждой из которых необязательно следуют пробелы, за которыми следует конец строки. Поэтому регулярное выражение проверяет правильность строки» и извлекает нужные совпадения из групп захвата.
Оператор безопасной навигации ,
&
необходим в случае отсутствия совпадения (match
возвращаетnil
).Комментарий ОП относится к обобщению вопроса, в котором символ фунта (
'#'
) является необязательным. Это можно решить, изменив регулярное выражение следующим образом.def doit(str) r = /\A#{"(?:#?(\\d)(?=#|\\s+|\\z)\\s*)"*str.count('0123456789')}\z/ str.match(r)&.captures end doit "1 2 #3 " #=> ["1", "2", "3"] doit "1 2 #3 " #=> ["1", "2", "3"] doit "1#2" #=> ["1", "2"] doit " #1 2 #3 " #=> nil doit "#1 2# 3 " #=> nil doit " #1 23 #3 " #=> nil
Для строк, содержащих три цифры, регулярное выражение имеет вид:
/\A(?:#?(\d)(?=#|\s+|\z)\s*)(?:#?(\d)(?=#|\s+|\z)\s*)(?:#?(\d)(?=#|\s+|\z)\s*)\z/
[1120]
Хотя это верно что это регулярное выражение потенциально может быть довольно длинным, это не обязательно означает, что оно будет относительно неэффективным, так как предугадывание довольно локализовано.
Вы можете создать XmlReader
с XmlParserContext
, который знает о пространствах имен; следующие действия работают для XmlDocument
и XDocument
:
class SimpleNameTable : XmlNameTable {
List<string> cache = new List<string>();
public override string Add(string array) {
string found = cache.Find(s => s == array);
if (found != null) return found;
cache.Add(array);
return array;
}
public override string Add(char[] array, int offset, int length) {
return Add(new string(array, offset, length));
}
public override string Get(string array) {
return cache.Find(s => s == array);
}
public override string Get(char[] array, int offset, int length) {
return Get(new string(array, offset, length));
}
}
static void Main() {
XmlNamespaceManager mgr = new XmlNamespaceManager(new SimpleNameTable());
mgr.AddNamespace("a", "http://foo/bar");
XmlParserContext ctx = new XmlParserContext(null, mgr, null,
XmlSpace.Default);
using (XmlReader reader = XmlReader.Create(
new StringReader(@"<root><a:element/></root>"), null, ctx)) {
XDocument doc = XDocument.Load(reader);
//XmlDocument doc = new XmlDocument();
//doc.Load(reader);
}
}
Основываясь на предыдущем ответе, вы можете сохранить префиксы пространства имен, сначала загрузив его в XmlDocument и проанализировав OuterXml XmlDocument в XDocument
XDocument LoadWithPrefix(Stream stream)
{
XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
mgr.AddNamespace("a", "http://foo/bar");
XmlParserContext ctx = new XmlParserContext(null, mgr, null, XmlSpace.Default);
using (XmlReader reader = XmlReader.Create(stream, null, ctx))
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
return XDocument.Parse(doc.OuterXml);
}
}