Es mucho más potente (y sobre todo cómodo cuando la gente no domina las expresiones regulares), realizar un automáta que haga justo la parte que se precisa...
Podremos reconocer algo tan complejo como: " 22 -134 +45 - 087 +170 voy a beber +44j82k"
cuya salida sería: 22 -134 +45 -87 +170 +44 +82 = lo que sume
" + 232"= 232
"+23" = 23
"23 " = 23
" 23" = 23
" 2323tf" = 2323
" ++++23" = 23
" +-+-+23" = 23
" +-+-23" = -23
" --- 23ffhj"= -23
" 23.34" = +23 +34 (OJO: no se ha contemplado reconocer decimales, ni con punto ni con coma).
...etc... con negativos...
...cosa que para una expresión regular sería más complejo, dada una falta de uniformidad en el texto...
Nótese que si hay números en el ejemplo que no debieran ser reconocidos, debe ser expresado en las reglas más abajo de la forma conveniente... Como es un ejemplo, no voy a abordarlo en profundidad, lo dejo simple pero que se entienda y el interesado que lo amplíe a sus necesidades. Es decir, no considero 3 casos: decimales ni separadores de miles, tampoco otras bases numéricas que la decimal, una vez entendido el ejemplo, no debería resultar complicado al interesado ampliarlo para admitir tales casos.
Las reglas para número son (uno debe ternerlas claro y hacer los cambos precisos):
regla "=" componentes // comentario
-------------------------------------------- entero = [signo] [spcs] digitos // el signo es opcional y puede haber o no espacios entre el signo y los digitos
signo = (positivo|negativo) positivo = "+" // en ausencia de '-' se entenderá que es positivo.
negativo = "-" spcs = espacio [spcs] // uno o más espacios
espacio = " " digitos = digito [digitos] // uno o más dígitos
digito = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" // los digitos decimales, aunque si fuera preciso podrían separararse por bases numéricas, y reconocer números hexadecimales, binarios, octal, etc...
* Como es un ejemplo, no contemplo números con signo (en el pseudocódigo, aquí sería modificar numero y reconocer el caracter separador del decimal. Se ha elegido como separador decimal, la comilla simple como usamos en español (cambiese al gusto).
numero = [signo][spcs] digitos ["'" digitos] // un número es un entero o un decimal.
* Tampoco contemplo separadores de miles, con las mismas consideraciones que para decimales, se ha elgido el punto como se acostumbra a usar en español:
entero = [signo][spcs] gruposDig
gruposDig = primeGrpDig [gruposDigitos]
primeGrpDig = digito [[digito] digito] //esto es 1, 2 ó 3 digitos
gruposDigitos = grupoDigitos [gruposDigitos] // uno o más grupos de 3 digitos
grupoDigitos = ["."] digito digito digito // 3 digitos antecedidos o no, por un punto.
El peudocodigo pués empieza por una enumeración y luego un array que contenga a dicha enumeración.
enumeracion TokenDeNumero //tipo byte
TOKEN_DESCONOCIDO = 0
TOKEN_SEP_ESPACIO = 1
TOKEN_DIGITO = 2
'
TOKEN_SIGNO = 4 // para no disitnguir positivo de negativo, si no solo un signo.
TOKEN_SIGNO_POS = 5
TOKEN_SIGNO_NEG = 6
// otros token separadores que se quieran considerar.
fin enumeracion
Array CharsToken(0 a 255) // uno por cada byte...
funcion Inicializar
byte k
CharsToken(32) = TOKEN_SEP_ESPACIO // el espacio es el 32, el espacio duro el 160, aquí se ignora...
bucle para k desde 48 a 57
CharsToken(k) = TOKEN_DIGITO // dígitos del 0 al 9.
siguiente
CharsToken(43) = TOKEN_SIGNO_POS
CharsToken(45) = TOKEN_SIGNO_NEG
// y eso es todo, el resto de caracteres son ignorados... tienen valor 0 en el array.
fin funcion
Ahora las funciones de reconocimeinto del número:
// Si devuelve true, valor contiene un número en caso contrario, se debe ignorar
Buleano = Funcion SiguienteToken(arraybytes Data(), porreferencia entero Indice, porreferencia entero Valor)
TokenDeNumero tkn
buleano negativo // false de entrada
Si SaltarEspaciosEIgnorarDesconocidos(data, indice) = FALSE devolver FALSE
tkn = CharsToken(data(indice))
Hacer mientras ( tkn AND TOKEN_SIGNO)
si (tkn AND TOKEN_SIGNO) luego
negativo = (tkn = TOKEN_SIGNO_NEG) //true si el signo es negativo
indice += 1
Si SaltarEspacios(data, indice) = FALSE devolver FALSE
tkn = CharsToken(data(indice))
si (tkn = TOKEN_DIGITO)
salir del bucle
sino
negativo = FALSE // se invalida el resultado previo, hay caracteres desconodicos tras el signo que se encontró.
Si SaltarEspaciosEIgnorarDesconocidos(data, indice) = FALSE devolver FALSE
fin si
fin si
repetir ' es un bucle porque podría ser falsos positivos, casos como: "+ qwrt +asd -zxc ... pero saltarían aquí + 23"
' si no es negativo, se asume que es positivo.
si (negativo = TRUE)
valor = - Data(indice) // [b]ojo: si el valor es 0[/b]... dejo a tu esfuerzo resolver el caso.
Sino
valor = Data(indice)
fin si
si (indice < Data.Length)
indice += 1
Hacer mientras (data(indice) = TOKEN_DIGITO)
indice += 1
valor *= 10 + data(indice)
si (indice = Data.Length) Salir del bucle
repetir
fin si
Devolver TRUE //y valor contiene el valor correcto, si devuelve false, se ignora el valor que contenga...
fin funcion
// Esta función y la siguiente son casi iguales, y no es baladí asignar el valor 0 a desconocidos y 1 al espacio...
// solo salta espacios,
// Este caso está permitido solo entre signo y digitos: entero = [signo][spcs] digitos
Buleano = Funcion SaltarEspacios(arraybytes Data(), porreferencia entero Indice)
Hacer mientras (CharsToken(Data(indice)) = TOKEN_SEP_ESPACIO)
indice += 1
si (indice = Data.Length) Devolver FALSE //final del array salimos d ela función.
repetir
Devolver TRUE
fin funcion
// esta funcion ignora espacios y caracteres desconocidos.
// Este caso está permitido cuando termina un número y hasta que comience otro.
Buleano = Funcion SaltarEspaciosEIgnorarDesconocidos(arraybytes Data(), porreferencia entero Indice)
Hacer mientras (CharsToken(Data(indice)) <= TOKEN_SEP_ESPACIO)
indice += 1
si (indice = Data.Length) Devolver FALSE //final del array salimos de la función.
repetir
Devolver TRUE
fin funcion
y finalmente la función que interesada en ello:
entero = funcion ParseStringToSumasYRestas(arrayBytes Data() )
entero total, valor, indice
Hacer mientras SiguienteToken(Data, Indice, valor ) = TRUE
total += valor
indice +=1
si (indice = Data.Length) salir dle bucle
repetir
Devolver total
fin funcion
Esto siempre será más veloz que una expresión regular, además recoge, casos muy distintos de lo que se considera números, que contemplarlos en una expresión regular complicacría y por tanto ralentizaría su resultado. Claro que usarlo o no, depende de si es muy necesario o no, a veces un código menos eficiente (sin llegar a ser deficiente), expresado en 4 líeas e spreferible a otro más eficiente exresado en 200 líneas:
" + 232"=232
"+23" = 23
"23 " = 23
" 23" = 23
" 2323tf" = 2323
" ++++23" = 23
" +-+-+23" = 23
" +-+-23" = -23
" --- 23ffhj"= -23
" 23.34" = +23 +34 (OJO: no se ha contemplado reconocer decimales, ni con punto ni con coma).
...etc... con negativos...
Por último, notar que en el pseudocódigo tampoco se ha contemplado la posibiidad de desbordamiento, evidentemente un número declarado de tipo entero, dará desbordamiento si se intenta parsear con "987654321987654321987654321".
Se supone que son valores coherentes 'sumables', en un rango fijado a ser aceptado en el tipo de datos numérico apropiado.
Si fuera el caso, podría llevarse una cuenta de los dígitos que se van hallando y cuando se alcance uno más de lo permitido devolver un error... si se contempla posibles errore,s la funció debería llamarse TryParse... y no Parse...
p.d.: Añado nota, para despistados: "//
ojo: si el valor es 0... dejo a tu esfuerzo resolver el caso."