2011-01-02 11 views
11

czy istnieje polecenie, które może pobrać trzeci indeks znaku w ciągu znaków? Na przykład:C# trzeci indeks znaku w ciągu znaków

error: file.ext: line 10: invalid command [test:)] 

W powyższym zdaniu, chcę indeksu 3. okrężnicy, jeden obok 10. Jak można to zrobić? Znam string.IndexOf i string.LastIndexOf, ale w tym przypadku chcę uzyskać indeks znaku, gdy jest używany po raz trzeci.

+2

może niektóre wyrażenia regularne? http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.aspx –

+0

Jeśli jest to zawartość ciągu po trzecim dwukropku, którego szukasz, może powinieneś po prostu podzielić ciąg na dwukropek i dołączyć cokolwiek oprócz trzech pierwszych żetonów ...? – jishi

+0

@jishi: Potrzebowałem również czegoś innego przy użyciu indeksu. – Iceyoshi

Odpowiedz

13

String.IndexOf będzie Ci indeks pierwszego, ale ma przeciążeń dając punkt wyjścia. Możesz więc użyć wyniku pierwszego IndexOf oraz jednego jako punktu wyjścia do następnego. A potem po prostu gromadzić indeksy wystarczającą liczbę razy:

var offset = myString.IndexOf(':'); 
offset = myString.IndexOf(':', offset+1); 
var result = myString.IndexOf(':', offset+1); 

Dodaj obsługi błędów, chyba że wiesz, że myString zawiera co najmniej trzy dwukropka.

+0

Jedyne co mogę dodać to sprawdzenie, że "offset" jest nieujemne po każdym wywołaniu do 'IndexOf' (zauważ, że wpisałeś' Index'), a ja użyłbym innej zmiennej po każdym wywołaniu o nazwie 'firstOffset',' secondOffset' i 'thirdOffset' odpowiednio. Niektórzy uznają tę przesadę, ale po prostu opierają się złym, zakorzenionym nawykom. – jason

+0

@Jason: 'Index' ->' IndexOf': fixing. Zobacz notatkę końcową ponownie. sprawdza wynik -1. I myślę, że trzy oddzielne zmienne tutaj są przesadą (gdybym wiedział, tj. Mógłbym twierdzić, było wystarczająco dużo znaków, abym zrobił to wszystko w jednym wyrażeniu, ale wtedy lubię kod kompaktowy, ponieważ często jest on bardziej czytelny poprzez usunięcie szumu.) – Richard

+0

To jest całkiem dobra metoda na uzyskanie n-tego indeksu. I tak łańcuch zawsze zawiera co najmniej 3 dwukropki, więc nie powinno być problemu z tym kodem. – Iceyoshi

9

można napisać coś takiego:

public static int CustomIndexOf(this string source, char toFind, int position) 
    { 
     int index = -1; 
     for (int i = 0; i < position; i++) 
     { 
      index = source.IndexOf(toFind, index + 1); 

      if (index == -1) 
       break; 
     } 

     return index; 
    } 

EDIT: Oczywiście trzeba go używać w następujący sposób:

int colonPosition = myString.CustomIndexOf(',', 3); 
+1

Co, jeśli w ciągu znaków będzie tylko 1 lub 2 znaki? Powinieneś dodać if (index <0) return index; –

0

Można zadzwonić .IndexOf (char, pozycja) do wyszukiwania spośród żądaną pozycję, dlatego powinieneś ją wywołać 3 razy (ale po każdym połączeniu powinieneś sprawdzić, czy coś jest znalezione).

+0

Tak, właśnie to zrobiłem i działa dobrze (dzięki innemu kawałkowi kodu, który ktoś napisał). – Iceyoshi

0
int pos = -1; 
for (int k = 0; k < 3; ++k) 
{ 
    pos = s.indexOf(':', pos+1); 
    // Check if pos < 0... 
} 
4

Zgaduję, że chcesz przetworzyć ten ciąg na różne części.

public static void Main() { 
    var input = @"error: file.ext: line 10: invalid command [test (: ]"; 
    var splitted = input .Split(separator: new[] {": "}, count: 4, options: StringSplitOptions.None); 

    var severity = splitted[0]; // "error" 
    var filename = splitted[1]; // "file.ext" 
    var line = splitted[2];  // "line 10" 
    var message = splitted[3]; // "invalid command [test (: ]" 
} 
+0

Tak, właśnie to chcę zrobić (dobrze, że zrobiłem to teraz), ale istnieje możliwość, że mogą być tylko 3 dwukropki zamiast 4 (komunikat o błędzie nie zawierał), a także może być ich więcej. W takim przypadku liczba byłaby różna i nie jestem pewien, w jaki sposób udało mi się przezwyciężyć ten problem. – Iceyoshi

+0

count = 4 oznacza, że ​​pobierzesz od _ do 4 ciągów. Ostatni ciąg może zawierać separator bez podziału. Zdaję sobie sprawę, że mój przykład nie pokazuje, że skoro używam ":" jako separatora, ale jeśli wiadomość zawierałaby ":", to nadal nie zostałaby podzielona. – sisve

+0

Zmieniłem przykładowy kod, aby tekst wiadomości zawierał ":", aby pokazać, że nie został podzielony. – sisve

0

Trochę brzydki, ale alternatywne podejście (do pozostałych już napisanych), który działa:

public int FindThirdColonIndex(string msg) 
{  
    for (int i = 0, colonCount = 0; i < msg.Length; i++) 
    { 
     if (msg[i] == ':' && ++colonCount == 3) { return i; } 
    } 

    // Not found 
    return -1; 
} 
2

To już odpowiedział kilka bardzo dobrych sposobów - ale postanowiłem spróbować i napisać za pomocą wyrażeń.

private int? GetNthOccurrance(string inputString, char charToFind, int occurranceToFind) 
{ 
    int totalOccurrances = inputString.ToCharArray().Count(c => c == charToFind); 
    if (totalOccurrances < occurranceToFind || occurranceToFind <= 0) 
    { 
     return null; 
    } 

    var charIndex = 
     Enumerable.Range(0, inputString.Length - 1) 
      .Select(r => new { Position = r, Char = inputString[r], Count = 1 }) 
      .Where(r => r.Char == charToFind); 


    return charIndex 
     .Select(c => new 
     { 
      c.Position, 
      c.Char, 
      Count = charIndex.Count(c2 => c2.Position <= c.Position) 
     }) 
     .Where(r => r.Count == occurranceToFind) 
     .Select(r => r.Position) 
     .First(); 
} 

Testy i udowodnić to zbyt:

Assert.AreEqual(0, GetNthOccurrance(input, 'h', 1)); 
Assert.AreEqual(3, GetNthOccurrance(input, 'l', 2)); 
Assert.IsNull(GetNthOccurrance(input, 'z', 1)); 
Assert.IsNull(GetNthOccurrance(input, 'h', 10)); 
0

Oto rekurencyjna realizacja (na ciąg nie char) - jako metodę rozszerzenia, dokładnie taki efekt formatu metodzie (-ach) ramowej.

Wszystko, co musisz zrobić, to zmienić "wartość ciągu" na "wartość char" w metodzie rozszerzenia i odpowiednio zaktualizować testy, a to zadziała ... Cieszę się, że mogę to zrobić i opublikować, jeśli ktoś jest zainteresowany ?

public static int IndexOfNth(
    this string input, string value, int startIndex, int nth) 
{ 
    if (nth < 1) 
     throw new NotSupportedException("Param 'nth' must be greater than 0!"); 
    if (nth == 1) 
     input.IndexOf(value, startIndex); 
    return 
     input.IndexOfNth(value, input.IndexOf(value, startIndex) + 1, --nth); 
} 

oto niektóre (MbUnit) testy jednostkowe, które mogą pomóc (to udowodnić jest poprawne):

[Test] 
public void TestIndexOfNthWorksForNth1() 
{ 
    const string input = "foo<br />bar<br />baz<br />"; 
    Assert.AreEqual(3, input.IndexOfNth("<br />", 0, 1)); 
} 

[Test] 
public void TestIndexOfNthWorksForNth2() 
{ 
    const string input = "foo<br />whatthedeuce<br />kthxbai<br />"; 
    Assert.AreEqual(21, input.IndexOfNth("<br />", 0, 2)); 
} 

[Test] 
public void TestIndexOfNthWorksForNth3() 
{ 
    const string input = "foo<br />whatthedeuce<br />kthxbai<br />"; 
    Assert.AreEqual(34, input.IndexOfNth("<br />", 0, 3)); 
} 
0

Proszę zobaczyć tę odpowiedź na podobne pytanie: https://stackoverflow.com/a/46460083/7673306
It zapewnia metodę znalezienia indeksu n-tego wystąpienia określonego znaku w wyznaczonym ciągu.

W Twoim konkretnym przypadku będą realizowane tak:

int index = IndexOfNthCharacter("error: file.ext: line 10: invalid command [test:)]", 3, ':'); 
Powiązane problemy