Per spiegarti questo comportamento devi sapere che le stringhe, in php, altro non sono che array di byte di cui php non conosce la codifica, ne gli importa di conoscerla. Quando tu fai $string = "ab"; allora è vero che $string[0] contiene "a" e $string[1] contiene "b", ma solo perché il carattere "a" e il carattere "b" sono codificati utilizzando un solo byte. In utf-8 tutti i caratteri che non occupa le prime 128 posizioni sono vengono codificati utilizzando due o più byte. (Gli unici caratteri che occupano un solo byte sono quindi solo i primi 128 caratteri ascii, che è un sottoinsieme di utf-8, e che contengono i "comuni caratteri inglesi"). Ecco quindi che parliamo di codifiche multibyte. Cosa significa questo per te? Che il carattere accentato che vuoi recuperare dalla stringa è composto da due (fino a quattro) byte, non uno. Ecco perché se accedi ai singoli byte con $stringa[$indice] ottieni quei "punti interrogativi in sfondo nero": perché quell'insieme di byte che rappresentano quel carattere hanno senso solo se presi insieme. Presi singolarmente potrebbero non puntare a nulla, o peggio, puntare ad altro.ma non funziona perché le vocali accentate in $stringa vengono trasformate nei classici "punti interrogativi in sfondo nero".
Sapendo questo prova a ripensare alla tua strategia, se non ti viene in mente niente ti posso dare qualche indicazione.

Rispondi quotando