2012-02-01 4 views
1

Я хочу использовать регулярное выражение, чтобы найти родительский узел 16 числа цифр и вернуть всю эту секцию, но не могу понять, как, так Дано:Как я могу заставить регулярное выражение работать?

<Details> 
<CreditCard cardnum="123456789" ccv="123" exp="0212" cardType="1" name="joe" /> 
</Details> 

Я хочу вернуться:

<CreditCard cardnum="123456789" ccv="123" exp="0212" cardType="1" name="joe" /> 

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

Я пробовал .*(\d{13,16}).*, но это получает каждый символ.

После того, я:

XElement element = XElement.Parse(xml); // XDocument.Load(xmlFile).Root 

IEnumerable<XElement> elementsWithPossibleCCNumbers = 
     element.Descendants() 
       .Where(d => d.Attributes() 
          .Where(a => a.Value.Length == 16) 
          .Count() == 1); 

Я не могу понять, как цикл через каждый атрибут в elementsWithPossibleCCNumbers, например:

foreach(var x in elementsWithPossibleCCNumbers) 
{ 
//If attribute is number, replace value with empty string 
} 

Примечание: я удалил int.TryParse для Теперь.

Я решил сделать это:

IEnumerable<XElement> elementsWithPossibleCCNumbers = 
     element.Descendants() 
       .Where(d => d.Attributes() 
          .Where(a => a.Value.Length >= 13 && a.Value.Length <= 16) 
          .Count() == 1).Select(x=>x); 


foreach(var x in elementsWithPossibleCCNumbers) 
{ 
    foreach(var a in x.Attributes()) 
    { 

    xml = xml.Replace(a.Value, new String('*',12)); 
    } 
} 

Однако, если у меня есть второй элемент с атрибутом 16 цифр, он заменяет только часть значения атрибутов.

+0

Когда дело доходит до RegEx я обычно полагаются на молитвы, ругань, и волшебство, прежде чем они будут работать правильно. – Yuck

+2

Почему бы не десериализовать XML? Или используйте LINQ to XML, чтобы отбросить именно те XML-элементы, которые вам нужны? –

+2

Если вы не хотите десериализовать, вы также можете посмотреть на использование XPath. – seanzi

ответ

3

Поскольку ваш XML может сильно отличаться, я бы сделал что-то вроде следующего.

Предполагая, что XML, как:

<Details> 
<CreditCard cardnum="123456789" 
      ccv="123" 
      exp="0212" 
      cardType="1" 
      name="joe" /> 
</Details> 

Агностик-иш код:

XElement element = XElement.Parse(xml); // XDocument.Load(xmlFile).Root 
int ccNumber; 

IEnumerable<XElement> elementsWithPossibleCCNumbers = 
     element.Descendants() 
       .Where(d => d.Attributes() 
          .Where(a => a.Value.Length == 16) 
          .Where(a => int.TryParse(a.Value, out ccNumber)) 
          .FirstOrDefault() != null); 

// Do not use ccNumber 
// Use elementsWithPossibleCCNumbers 

Это может быть расширена, чтобы включить ряд атрибутов ...

IEnumerable<XElement> elementsWithPossibleCCNumbers = 
     element.Descendants() 
       .Where(d => d.Attributes() 
          .Where(a => a.Value.Length == 16) 
          .Where(a => int.TryParse(a.Value, out ccNumber)) 
          .FirstOrDefault() != null 
          && d.Attributes().Count() == 5); 

Есть множество возможностей, которые не включают использование Regex или жестких кодовых имен XML-элементов. Я предпочитаю использовать Regex как последнее средство, особенно если есть что-то лучшее, что может проанализировать все данные для меня.

Обновление 1

elementsWithPossibleCCNumbers являются XML-элементы, которые содержат 1 или более атрибуты, которые являются 16 цифр в длину и являются целым числом. В таком случае, вы не можете сказать так, я хотел бы изменить его ..

IEnumerable<XElement> elementsWithPossibleCCNumbers = 
     element.Descendants() 
       .Where(d => d.Attributes() 
          .Where(a => a.Value.Length == 16) 
          .Where(a => int.TryParse(a.Value, out ccNumber)) 
          .Count() == 1); 
          // Where only 1 attribute is 16 length and an int 

Расширение снова ...

IEnumerable<XAttribute> attributesWithPossibleCCNumbers = 
     element.Descendants() 
       .Where(d => d.Attributes() 
          .Where(a => a.Value.Length == 16) 
          .Where(a => int.TryParse(a.Value, out ccNumber)) 
          .Count() == 1) 
       .Select(e => e.Attributes() 
          .Where(a => a.Value.Length == 16) 
          .Where(a => int.TryParse(a.Value, out ccNumber)) 
          .First()); 
+0

Мне нравится твоя лучше, чем моя. +1 –

+0

Я сделаю это. Спасибо за ваше терпение. – Xaisoft

+1

Очень приятно: +1. Должен заглянуть в XElement немного больше. Не понял, что вы так легко можете сделать это! ;-) – Chris

1

Не используйте Regex для анализа XML. Это плохо подходит.

Как насчет использования XmlDocument или XDocument?

+0

Я не хочу использовать Regex, но XML может отличаться. Его можно назвать CreditCard в этом случае, но другие могут назвать это PaymentInfo, CCInfo, CCDetails и т. Д. – Xaisoft

+0

Я бы порекомендовал «XDocument» для производительности. – Aphelion

+0

Я думал, что смогу использовать комбинацию. Получите номер первым и дочерний элемент этого атрибута, а затем используйте XDocument для разбора элемента. – Xaisoft

2

Попробуйте использовать: < [^>] + [0-9] {16} [^>] +>

Edit: Это может быть более efficient- < ([^> 0-9] +) ([0-9] {16}) ([^>] +)>

+0

Nice. Это сработало. Теперь мне просто нужно проанализировать атрибуты. Не могли бы вы объяснить регулярное выражение. – Xaisoft

+0

Частично, '^' и '+' в выражении. – Xaisoft

+0

^- отрицание, + указывает, что оно было найдено более одного раза. Это основные понятия. Почему вы читаете файлы с информацией о кредитной карте в открытом виде? –

3

Я написал еще один способ попробовать. Регулярное выражение теперь проверяет только значение атрибута, а не сам XML. Я понятия не имею, что вы хотите вернуть из этого метода, но по крайней мере вы начнете с того, что не используете Regex для XML.

[Test] 
public void X() 
{ 
    const string xml = "<Details><CreditCard cardnum=\"123456789\" ccv=\"123\" exp=\"0212\" cardType=\"1\" name=\"joe\" /><donotfind>333</donotfind></Details>"; 

    var doc = new XmlDocument(); 
    doc.LoadXml(xml); 

    Console.WriteLine(doc.Name);; 

    foreach(XmlNode x in doc.ChildNodes) 
    { 
     ExploreNode(x); 
    } 
} 

void ExploreNode(XmlNode node) 
{ 
    Console.WriteLine(node.Name); 

    if (node.Attributes != null) 
    { 
     foreach (XmlAttribute attr in node.Attributes) 
     { 
      Console.WriteLine("\t{0} -> {1}", attr.Name, attr.Value); 

      if (attr.Value.Length == 16 && Regex.IsMatch(attr.Value, @"\d{16}")) 
      { 
       Console.WriteLine("\t\tCredit Card # found!"); 
      } 
     } 
    } 

    foreach (XmlNode child in node.ChildNodes) 
    { 
     ExploreNode(child); 
    } 
} 
+0

Метод хороший, но, как я уже говорил, строка xml будет меняться, означая, что элемент и атрибуты будут называться разными именами. – Xaisoft

+0

@Xaisoft: Здесь нет ничего, что ссылается на имена элементов или атрибутов. Все ключи от значения атрибута. –

+0

ah ha, я вижу, вы проверяете атрибут, который имеет значение 16, но вы частично используете regex для этого, по какой-либо причине? – Xaisoft

Смежные вопросы