sprintf

06/07/2003 - 05:07 por Leonardo Azpurua | Informe spam
Hola.

Estoy adaptando un punto de ventas a varios idiomas. Para ello he tenido que
utilizar un archivo de recursos; hasta aquí no hay problemas.

Pero con frecuencia me encuentro en el texto original, mensajes conformados
según un patrón de texto siempre igual, por ejemplo:

ADVERTENCIA: el Codigo del Producto no puede quedar vacio
ADVERTENCIA: el precio del articulo no puede ser cero
WARNING: Item Code may not be blank
WARNING: Item Price may not be zero

es decir:

ADVERTENCIA: ??? no puede ???
WARNING: ??? may not ???

y hay al menos un patrón de este tipo para casi cada control de cada forma.
Muchos de ellos son repetitivos, pues se refieren al mismo dato en
diferentes contextos. Pero aun así, quedamos con un patrón repetitivo para
cada dato validable de cada clase.

Una solución podría ser armar cada frase utilizando encadenamiento de
cadenas, así:

sMensaje = LoadResString(STRRS_ADVERT) & _
LoadResString(STRRS_CODIGOITEM) & " " & _
LoadResString(STRRS_NOPUEDE) & " " & _
LoadResString(STRRS_SERVACIO)

pero en "C" había una función preciosa: sprintf, que simplemente rellenaba
marcadores en el primer argumento con cada uno de los argumentos siguientes
en el ordenen que se iban presentando, por ejemplo:

sprintf(sdest, LoadResString(STRRS_ADVERT),
LoadResString(STRRS_CODIGOITEM),
LoadResString(STRRS_NOPUEDE),
LoadResString(STRRS_SERVACIO));

si el recurso STRRS_ERR, estuviera definido como "ERROR: el %s %s %s", el
resultado de la instrucción anterior sería copiar en sdest, la cadena
"ERROR: el Codigo del Producto no puede quedar vacio".

Me gusta más la sintaxis de "C": es una función que asigna contenidos a
componentes dentro de una plantilla, no una secuencia de encadenamientos,
como la versión original en BASIC.

De manera que me escribí una función en VB, llamada sprintf, cuya definición
es la siguiente:

Public Function sprintf(sTemplate As String, ParamArray Args() As Variant)
As String
Dim sRetString As String
Dim nUpTo As Long, nLast As Long
Dim nIndex As Integer, sIndex As String
Dim v As Variant

nUpTo = 1: nLast = 1
Do
nUpTo = InStr(nUpTo, sTemplate, "%")
If nUpTo = 0 Then
If Len(sTemplate) >= nLast Then
sRetString = sRetString & Right(sTemplate, Len(sTemplate) - nLast +
1)
End If
Else
nUpTo = nUpTo + 1
If Mid(sTemplate, nUpTo, 1) = "%" Then
sRetString = sRetString & Mid(sTemplate, nLast, nUpTo - nLast)
nUpTo = nUpTo + 1
nLast = nUpTo
Else
' Agregar el ultimo segmento
sRetString = sRetString & Mid(sTemplate, nLast, nUpTo - nLast - 1)
nLast = nUpTo
Do While InStr(1, "0123456789", Mid(sTemplate, nUpTo, 1)) <> 0 And
nUpTo - nLast < 3
nUpTo = nUpTo + 1
Loop
If nUpTo = nLast Then
v = "¡ERR!"
Else
sIndex = Mid(sTemplate, nLast, nUpTo - nLast)
nIndex = Val(sIndex) - 1
If nIndex < LBound(Args) Or nIndex > UBound(Args) Then
v = "¡INDEX!"
Else
v = Args(nIndex)
End If
End If
sRetString = sRetString & v
nLast = nUpTo
End If
End If
Loop While nUpTo <> 0
sprintf = sRetString
End Function

y que devuelve un string, combinando los argumentos contenidos en Args con
la plantilla contenida en sTemplate.

Es diferente de sprintf en "C". En primer lugar, no escribe el resultado en
un bufer pasado por referencia, sino que lo devuelve como resultado de la
función. En segundo lugar, en "C" se especifica el tipo de dato y el formato
a aplicar a cada argumento en orden de aparición. En este sprintf se
especifica la posición del argumento a insertar en el arreglo Args, mediante
un número precedido de un signo de porcentaje.

Por ejemplo:

sprintf("Esto %1 y vuelve a %1r", "aparece"), devolverá: "Esto aparece y
vuelve a aparecer"

si se quiere insertar un signo de porcentaje, se debe escribir %%. Por
ejemplo (a = 15.5):
sprintf("Esta factura incluye un descuento del %1%%", a), devolverá: "Esta
factura incluye un descuento del 15,5%"

si el signo de porcentaje no va seguido de un número, se insertará el código
"¡ERR!" en la posición correspondiente:
sprintf("Esta factura incluye un descuento del %1%", a), devolverá: "Esta
factura incluye un descuento del 15,5¡ERR!"

por último, una referencia a un argumento inexistente (menor que uno omayor
que la cantidad de argumentos) insertrá el codigo ¡INDEX! en esa posición:
sprintf("Esta factura incluye un descuento del %10%%", a), devolverá: "Esta
factura incluye un descuento del ¡INDEX!%"

sprintf es una de esas funciones casi triviales cuya utilidad no se reconoce
hasta que no se pierde. Ahora, tres años despues, la tengo de nuevo.

Por supuesto que no la presento como una "innovación tecnológica". A los
aprendices les puede servir como ejemplo de una técnica "sucia y rápida" de
análisis sintáctico, uso de argumentos variables y proceso de cadenas en
general. Y a los más avanzados como un recurso adicional al cual echar mano
cuando se presente la necesidad.

Salud!

Leonardo
MS MVP

Preguntas similare

Leer las respuestas

#1 Eduardo A. Morcillo [MS MVP]
06/07/2003 - 06:54 | Informe spam
Leonardo,

Lindo ejercicio. Quizas no sabias la existencia de la API wvsprintf del
windows:

Private Declare Function wvsprintf Lib "user32" Alias "wvsprintfA" ( _
ByVal lpOutput As String, _
ByVal lpFmt As String, _
arglist As Long) As Long

Function sprintf(ByVal Fmt As String, ParamArray Params()) As String
Dim alArgList() As Long
Dim asStrs() As String
Dim lIdx As Long
Dim lStr As Long

ReDim alArgList(0 To UBound(Params()))

For lIdx = 0 To UBound(Params())

If VarType(Params(lIdx)) = vbString Then
ReDim asStrs(0 To lStr)
asStrs(lStr) = StrConv(Params(lIdx), vbFromUnicode)
alArgList(lIdx) = StrPtr(asStrs(lStr))
lStr = lStr + 1
Else
alArgList(lIdx) = Params(lIdx)
End If

Next

sprintf = Space$(1024)
sprintf = Left$(sprintf, wvsprintf(sprintf, Fmt, alArgList(0)))

End Function

MsgBox sprintf("La letra %c es %s(%d)", 65, "Chr$", 65)

Eduardo A. Morcillo [MS MVP - VB]
http://www.domaindlx.com/e_morcillo
Respuesta Responder a este mensaje
#2 Leonardo Azpurua
06/07/2003 - 16:08 | Informe spam
"Eduardo A. Morcillo [MS MVP]" <emorcilloATmvps.org> escribió en el mensaje
news:
Leonardo,

Lindo ejercicio.



Gracias!

Quizas no sabias la existencia de la API wvsprintf del
windows:



A causa de un trauma atávico, tengo asociado el uso de cualquier recurso de
bajo nivel con altos costos de mantenimiento. Por eso, sólo recurro a las
API cuando he intentado resolver el problema dentro de los límites del
lenguaje y no lo he logrado. Hay tareas que por su naturaleza me llevan
directamente a buscar en las API, pero en realidad, nunca me he sentido
inclinado a ejecutar el proceso inverso (examinar las API y buscarles un
uso).

Supongo que llegó el día de estudiar más a fondo las diferentes plataformas
(eso es parte importante del compromiso, tal como yo lo veo), y de
esforzarme por superar el atávico trauma.

Por otra parte, mi sprintf tiene ciertas ventajas:

1.- No tienes que incluir dos veces un elemento que se repita (basta con
repetir el índice)
2.- No tienes que conocer los especificadores de formato de "C"
3.- Está escrita en BASIC.

Supongo que tambien tendrá sus desventajas: wvsprintf tiene intrínsecas
ciertas capacidades de formateo sobre los datos individuales (por ejemplo
"%-20s" ajusta la longitud de un parámetro tipo string a 20 caracteres
exactos, cortando o añadiendo espacios según sea necesario); y supongo que
en general será mucho menos eficiente.

Salud!

Leonardo
MS MVP
email Siga el debate Respuesta Responder a este mensaje
Ads by Google
Help Hacer una preguntaRespuesta Tengo una respuesta
Search Busqueda sugerida