Vraag someString.IndexOf (someString) geeft 1 terug in plaats van 0 onder .NET 4


We hebben onlangs al onze projecten geüpgraded van .NET 3.5 naar .NET 4. Ik ben een nogal vreemd probleem tegengekomen met betrekking tot string.IndexOf().

Mijn code doet duidelijk iets anders, maar tijdens het onderzoeken van het probleem, vond ik die roeping IndexOf() op een string met zichzelf terug 1 in plaats van 0. Met andere woorden:

string text = "\xAD\x2D";          // problem happens with "­-dely N.China", too;
int index = text.IndexOf(text);    // see update note below.

Gaf me een index van 1, in plaats van 0. Een paar dingen om op te merken over dit probleem:

  • De problemen lijken verband te houden met deze koppeltekens (het eerste teken is het zachte koppelteken Unicode, het tweede is een normaal koppelteken).

  • Ik heb dit dubbel gecontroleerd, dit gebeurt niet in .NET 3.5 maar doet dat in .NET 4.

  • De. Wijzigen IndexOf() om een ​​ordinale vergelijking te maken, wordt het probleem opgelost, dus om een ​​of andere reden wordt het eerste teken genegeerd met de standaardwaarde IndexOf.

Weet iemand waarom dit gebeurt?

BEWERK

Sorry jongens, maakte een beetje een spul op de originele post en kreeg het verborgen streepje daar twee keer. Ik heb de string bijgewerkt, dit zou de index van 1 in plaats van 2 moeten retourneren, zolang je het maar in de juiste editor plakt.

Bijwerken:

De oorspronkelijke probleemreeks gewijzigd in een tekenreeks waarin elk actueel teken duidelijk zichtbaar is (met behulp van escaping). Dit vereenvoudigt de vraag een beetje.


56
2017-07-13 09:08


oorsprong


antwoorden:


Je string bestaat uit twee karakters: a zacht koppelteken (Unicode-codepunt 173) en a koppelteken (Unicode-codepunt 45).

wiki: Volgens de Unicode-standaard wordt een zacht afbreekstreepje niet weergegeven als de lijn op dat moment niet wordt verbroken.

Tijdens gebruik "\xAD\x2D".IndexOf("\xAD\x2D") in .NET 4 lijkt het erop te letten dat je op zoek bent naar het zachte koppelteken, en een startindex van 1 (de index van \x2D). In .NET 3.5 wordt 0 geretourneerd.

Meer plezier, als je deze code uitvoert (dus wanneer enkel en alleen op zoek naar het zachte koppelteken):

string text = "\xAD\x2D";
string shy = "\xAD";
int i1 = text.IndexOf(shy);

dan i1 wordt 0, ongeacht de gebruikte .NET-versie. Het resultaat van text.IndexOf(text); varieert inderdaad, wat in een oogopslag voor mij als een insect lijkt.

Voor zover ik kan terugvinden door het framework, gebruiken oudere .NET-versies een InternalCall naar IndexOfString() (Ik kan niet achterhalen tot welke API-aanroep dat gaat), terwijl van .NET 4 a QCall naar InternalFindNLSStringEx() is gemaakt, die op zijn beurt roept FindNLSStringEx().

Het probleem (ik kan echt niet achterhalen of dit bedoeld gedrag is) komt inderdaad voor tijdens het bellen FindNLSStringEx:

LPCWSTR lpStringSource = L"\xAD\x2D";
LPCWSTR lpStringValue = L"\xAD";

int length;

int i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringValue,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringSource,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

Console::ReadLine();

Drukt 0 af en vervolgens 1. Merk op dat length, een uit-parameter die de lengte van de gevonden reeks aangeeft, is 0 na de eerste oproep en 1 na de tweede; het zachte koppelteken wordt geteld als een lengte van 0.

De oplossing is om te gebruiken text.IndexOf(text, StringComparison.OrdinalIgnoreCase);, zoals je hebt opgemerkt. Dit maakt een QCall naar InternalCompareStringOrdinalIgnoreCase() die op zijn beurt roept FindStringOrdinal(), die in beide gevallen 0 retourneert.


32
2017-07-13 09:20



Het lijkt een bug te zijn .NET4en er zijn nieuwe wijzigingen doorgevoerd .NET4 Beta1 naar de vorige versie hetzelfde als .NET 2.0 / 3.0 / 3.5.

Wat is nieuw in de BCL in .NET 4.0 CTP(MSDN-blogs):

Stringbeveiligingswijzigingen in .NET 4 

De standaard overbelasting van gedeeltelijke koppelingen op System.String (StartsWith, EndsWith, IndexOf en LastIndexOf) is standaard gewijzigd in cultuuragent (ordinaal).

Deze wijziging beïnvloedde het gedrag van de String.IndexOf methode door ze te veranderen om standaard een standaard (byte-voor-byte) vergelijking uit te voeren en zal worden gewijzigd om te gebruiken CultureInfo.InvariantCulture in plaats van CultureInfo.CurrentCulture.

UPDATE voor .NET 4 Beta 1 

Om de hoge compatibiliteit tussen .NET 4 en eerdere releases te behouden, hebben we besloten om deze wijziging ongedaan te maken. Het gedrag van de standaard partial matching overloads van String en de ToUpper- en ToLower-methoden van String en Char gedragen zich nu hetzelfde als in .NET 2.0 / 3.0 / 3.5. De verandering naar het oorspronkelijke gedrag is aanwezig in .NET 4 Beta 1.


Om dit te repareren, verander de stringvergelijkingsmethode in een overbelasting die de System.StringComparison opsomming als een parameter, en geef een van beide op Ordinal of OrdinalIgnoreCase.

// string contains 'unicode dash' \x2D
string text = "\xAD\x2D"; 

// woks in .NET 2.0/3.0/3.5 and .NET 4 Beta 1 and later
// but seems be buggy in .NET 4 because of 'culture-sensitive' comparison        
int index = text.IndexOf(text); 

// fixed version
index = text.IndexOf(text, StringComparison.Ordinal); 

20
2017-07-13 09:21



Van de documentatie (mijn nadruk):

Deze methode voert een woord uit (hoofdlettergevoelig en cultuurgevoelig) zoeken met behulp van de huidige cultuur.

D.w.z. sommige afzonderlijke codepunten worden als gelijk behandeld.

Wat gebeurt er als u een overbelasting gebruikt die een StringComparison waarde en doorgeven StringComparison.Ordinal  om de culturele afhankelijkheden te vermijden?


0
2017-07-13 09:44