Tengo un NSString así:

@"200hello"

o

@"0 something"

Lo que me gustaría ser capaz de hacer es tomar la primera a la que ocurren en el número de NSString y convertirlo en un int.

De modo que @»200hello» iba a ser int = 200.

y @»0 algo» iba a ser int = 0.

OriginalEl autor Brock Woolf | 2009-07-16

6 Comentarios

  1. 30
    int value;
    BOOL success = [[NSScanner scannerWithString:@"1000safkaj"] scanInteger:&value];

    Si el número no es siempre al principio:

    NSCharacterSet* nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
    int value = [[@"adfsdg1000safkaj" stringByTrimmingCharactersInSet:nonDigits] intValue];
    Esto sólo funciona si el número es la primera parte de la cadena. Concedido, que es lo que el ejemplo parece dar a entender, pero mi respuesta va a trabajar incluso si hay cartas antes de los números. El tuyo no. =)
    Parece una buena respuesta.
    Editado para la respuesta completa.
    Bonita solución para los personajes antes y después. Yo no era consciente de la invertedSet método en NSCharacterSet. +1
    OK, onu-abajo-voto de hecho, usted ha hablado de mí en esto. Pero no vengas a quejarte cuando la gente comience a construir plantas de energía nuclear, cuando su iPhone baterías se agotan.

    OriginalEl autor Nikolai Ruhe

  2. 19

    Steve Ciarcia dijo una vez un solo mide el resultado es un valor de más de un centenar de ingenieros de opiniones. Y así comienza la primera, y la última, «Cómo obtener un valor int de un NSString» cook-off!

    Los siguientes son los contendientes: (microsegundos adoptadas y el número de bytes utilizados por partido usando la increíblemente alta precisión para(x=0; x<100000; x++) {} micro-punto de referencia que ha sido transmitido a través de las generaciones. El tiempo se mide a través de getrusage(), bytes utilizados a través de malloc_size(). La cadena ser igualado fue normalizada para ‘foo 2020hello’ para todos los casos, excepto los que se requiere el número en el inicio. Todas las conversiones se normalizaron a ‘int’. Los dos números después de que el tiempo se normalizan los resultados en relación a los mejor y peor de los artistas intérpretes o ejecutantes.)

    EDITAR: Estos fueron los números originales publicados, ver más abajo para los números actualizados. También, a veces son de un 2.66 Core2 macbook pro.

    characterSet   time: 1.36803us 12.5 / 1.00 memory: 64 bytes (via Nikolai Ruhe)
    original RKL   time: 1.20686us 11.0 / 0.88 memory: 16 bytes (via Dave DeLong)
    modified RKL   time: 1.07631us  9.9 / 0.78 memory: 16 bytes (me, changed regex to \d+)
    scannerScanInt time: 0.49951us  4.6 / 0.36 memory: 32 bytes (via Nikolai Ruhe)
    intValue       time: 0.16739us  1.5 / 0.12 memory:  0 bytes (via zpasternack)
    rklIntValue    time: 0.10925us  1.0 / 0.08 memory:  0 bytes (me, modified RKL example)

    Como he señalado en otro lugar de este mensaje, que originalmente se lanzó en una unidad de pruebas que uso para RegexKitLite. Bien, siendo la prueba de la unidad arnés significaba que yo estaba probando con mi copia privada de RegexKitLite…, que también pasó a tener un montón de depuración cosas agregado, mientras que el seguimiento de un informe de error de un usuario. Los anteriores resultados de tiempo de aproximadamente equivalente a la llamada [valueString flushCachedRegexData]; dentro del for() {} sincronización en bucle (que básicamente era lo que de manera involuntaria depuración de cosas que estaba haciendo). Los siguientes resultados son de compilar en contra de la última, sin modificar, RegexKitLite disponible (3.1):

    characterSet   time: 1.36803us 12.5 / 1.00 memory: 64 bytes (via Nikolai Ruhe)
    original RKL   time: 0.58446us  5.3 / 0.43 memory: 16 bytes (via Dave DeLong)
    modified RKL   time: 0.54628us  5.0 / 0.40 memory: 16 bytes (me, changed regex to \d+)
    scannerScanInt time: 0.49951us  4.6 / 0.36 memory: 32 bytes (via Nikolai Ruhe)
    intValue       time: 0.16739us  1.5 / 0.12 memory:  0 bytes (via zpasternack)
    rklIntValue    time: 0.10925us  1.0 / 0.08 memory:  0 bytes (me, modified RKL example)

    Este es un poco mejor que un 50% de mejora. Si usted está dispuesto a vivir ligeramente peligrosamente, puede engatusar a un poco más de velocidad con el -DRKL_FAST_MUTABLE_CHECK tiempo de compilación opción:

    original RKL   time: 0.51188us  4.7 / 0.37 memory: 16 bytes using intValue
    modified RKL   time: 0.47665us  4.4 / 0.35 memory: 16 bytes using intValue
    original RKL   time: 0.44337us  4.1 / 0.32 memory: 16 bytes using rklIntValue
    modified RKL   time: 0.42128us  3.9 / 0.31 memory: 16 bytes using rklIntValue

    Esta es buena por lo general de alrededor de otro 10% de mejora, y es bastante seguro de usar (para obtener más información, consulte la RKL docs). Y mientras yo estaba en él… ¿por qué no utilizar el más rápido rklIntValue demasiado? ¿Hay algún tipo de premio para derrotar a los nativos, construido en la base de los métodos de uso de terceros externos, no integrado de propósito general regex motor de comparación de patrones? No creas lo que dicen de que «expresiones regulares son lentos».

    EDITAR

    La RegexKitLite ejemplo se puede encontrar en la RegexKitLite Rápido De Conversión De Hexadecimal. Básicamente intercambiado strtoimax para strtol, y añadió una línea de código para saltar principales caracteres que no eran [+-0-9]. (revelación completa: soy el autor de RegexKitLite)

    Tanto ‘scannerScanInt’ y ‘intValue’ sufren el problema de que el número extraído debe estar en el inicio de la cadena. Yo creo que ambos se omita cualquier líder de espacio en blanco.

    He modificado Dave DeLongs expresión regex ‘[^\s]*(\d+)’ sólo ‘\d+’ porque eso es todo lo que realmente se necesita, y se las arregla para librarse de un grupo de captura de uso para el arranque.

    Así, con base en los datos anteriores, os ofrecemos las siguientes recomendaciones:

    Hay, básicamente, dos capacidades diferentes clases: Aquellos que pueden tolerar extra ‘cosas’ y aún así obtener el número (conjunto de caracteres, RegexKitLite los dispositivos de comparación, y rklIntValue), y aquellos que, básicamente, necesitamos el número a ser la primera cosa en la cadena, tolerando en la mayoría de algún espacio en blanco de relleno en el inicio (scannerScanInt y intValue).

    No utilice NSCharacterClass para hacer este tipo de cosas. Para el ejemplo dado, los 16 bytes que se utiliza para instanciar el primer NSCharacterClass, luego de 32 bytes para la versión invertida, y, finalmente, de 16 bytes para la cadena de resultado. El hecho de que el propósito general de la regex motor supera por un doble dígito porcentaje de margen, mientras que el uso de menos memoria prácticamente se cierra el trato.

    (tenga en cuenta que escribí RegexKitLite, así que tome la siguiente con cualquier tamaño de un grano de sal que usted sienta que es apropiado).

    RegexKitLite convierte en los buenos tiempos y utiliza la menor cantidad de memoria posible, teniendo en cuenta el hecho de que es la devolución de un objeto NSString. Puesto que utiliza una LRU caché internamente para todos los de la UCI regex motor de cosas, los costes se amortizan a lo largo del tiempo y repetidos usos. También se toma unos segundos para cambiar el regex si la necesidad surge (valores hexadecimales? hex flota? Monedas? Las fechas? No hay problema).

    Por la sencilla los dispositivos de comparación, debería ser obvio que usted definitivamente NO debe usar NSScanner para hacer este tipo de cosas. El uso de NSScanner a hacer una ‘scanInt:’ no es diferente de simplemente llamar a [aString intValue]. La producen los mismos resultados que con las mismas advertencias. La diferencia es NSScanner lleva CINCO veces más que la misma cosa, mientras que la consunción de 32 bytes de memoria en el proceso…. mientras [aString intValue] (probablemente) no requiere un byte de memoria para realizar su magia – probablemente sólo llamadas strtoimax() (o equivalente) y ya que tiene acceso directo al puntero de la celebración de las cadenas de contenidos….

    El final uno es ‘rklIntValue’, que a su vez es sólo un poco ajustado versión de lo que se puede encontrar en (el ‘RegexKitLite Rápido Hexagonal de Conversión» enlace de arriba, stackoverflow no me deja publicarlo dos veces). Utiliza CoreFoundation para tratar de obtener acceso directo a las cadenas de amortiguamiento, y en su defecto, asigna el espacio de la pila y de las copias de un fragmento de la cadena para que búfer. Esto se lleva a todos, oh, tres instrucciones en la CPU, y es fundamentalmente imposible ‘fuga’ como un malloc() de la asignación. Para que se utiliza el cero de la memoria y va muy, muy rápido. Como un bono adicional, usted pasa a strtoXXX() el número de la base de la cadena para convertir. 10 decimales, 16 hex (automáticamente tragar una de las principales 0x si está presente), o 0 para automagic de detección. Es un trivial, de una sola línea de código para saltar el puntero sobre cualquier ‘interesante’ caracteres hasta llegar a lo que quieres (yo -,+, y 0-9). También trivial para cambiar en algo como strtod() si es necesario analizar el doble de los valores. strtod() convierte casi cualquier válida de punto flotante de texto: NAN, INF, hex carrozas, el nombre.

    EDICIÓN:

    Por solicitud de la OP, he aquí una recortada y record de la versión del código que he utilizado para realizar las pruebas. Una cosa de la nota: Mientras que poner esto juntos, me di cuenta de que Dave DeLongs original regex no acaba de funcionar. El problema está en el negado de conjunto de caracteres – meta-secuencias de caracteres dentro de conjuntos (es decir, [^\s]+) significa que el carácter literal, no el significado especial que tiene fuera del conjunto de caracteres. Reemplazado con [^\p{DecimalNumber}]*, que tiene el efecto deseado.

    Originalmente atornilladas este material a un RegexKitLite unidad de pruebas, así que me fui de algunas partes y piezas para la GC. Se me olvidó todo acerca de esto, pero la versión corta de lo que sucede cuando el GC es activado es que los tiempos de todo, PERO RegexKitLite doble (es decir, lleva el doble de tiempo). RKL, sólo toma alrededor de 75% más de tiempo (y que llevó a una enorme, no trivial la cantidad de esfuerzo que se obtiene cuando yo estaba en desarrollo). El rklIntValue tiempo se mantiene exactamente el mismo.

    Compilar con

    shell% gcc -DNS_BLOCK_ASSERTIONS -mdynamic-no-pic -std=gnu99 -O -o stackOverflow stackOverflow.m RegexKitLite.m -framework Foundation -licucore -lauto

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <limits.h>
    #include <stdint.h>
    #include <sys/time.h>
    #include <sys/resource.h>
    #include <objc/objc-auto.h>
    #include <malloc/malloc.h>
    #import <Foundation/Foundation.h>
    #import "RegexKitLite.h"
    static double cpuTimeUsed(void);
    static double cpuTimeUsed(void) {
    struct rusage currentRusage;
    getrusage(RUSAGE_SELF, &currentRusage);
    double userCPUTime   = ((((double)currentRusage.ru_utime.tv_sec) * 1000000.0) + ((double)currentRusage.ru_utime.tv_usec)) / 1000000.0;
    double systemCPUTime = ((((double)currentRusage.ru_stime.tv_sec) * 1000000.0) + ((double)currentRusage.ru_stime.tv_usec)) / 1000000.0;
    double CPUTime = userCPUTime + systemCPUTime;
    return(CPUTime);
    }
    @interface NSString (IntConversion)
    -(int)rklIntValue;
    @end
    @implementation NSString (IntConversion)
    -(int)rklIntValue
    {
    CFStringRef cfSelf = (CFStringRef)self;
    UInt8 buffer[64];
    const char *cptr, *optr;
    char c;
    if((cptr = optr = CFStringGetCStringPtr(cfSelf, kCFStringEncodingMacRoman)) == NULL) {
    CFRange range     = CFRangeMake(0L, CFStringGetLength(cfSelf));
    CFIndex usedBytes = 0L;
    CFStringGetBytes(cfSelf, range, kCFStringEncodingUTF8, '?', false, buffer, 60L, &usedBytes);
    buffer[usedBytes] = 0U;
    cptr = optr       = (const char *)buffer;
    }
    while(((cptr - optr) < 60) && (!((((c = *cptr) >= '0') && (c <= '9')) || (c == '-') || (c == '+'))) ) { cptr++; }
    return((int)strtoimax(cptr, NULL, 0));
    }
    @end
    int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    #ifdef __OBJC_GC__
    objc_start_collector_thread();
    objc_clear_stack(OBJC_CLEAR_RESIDENT_STACK);
    objc_collect(OBJC_EXHAUSTIVE_COLLECTION | OBJC_WAIT_UNTIL_DONE);
    #endif
    BOOL gcEnabled = ([objc_getClass("NSGarbageCollector") defaultCollector] != NULL) ? YES : NO;
    NSLog(@"Garbage Collection is: %@", gcEnabled ? @"ON" : @"OFF");
    NSLog(@"Architecture: %@", (sizeof(void *) == 4UL) ? @"32-bit" : @"64-bit");
    double      startTime = 0.0, csTime = 0.0, reTime = 0.0, re2Time = 0.0, ivTime = 0.0, scTime = 0.0, rklTime = 0.0;
    NSString   *valueString = @"foo 2020hello", *value2String = @"2020hello";
    NSString   *reRegex = @"[^\\p{DecimalNumber}]*(\\d+)", *re2Regex = @"\\d+";
    int         value = 0;
    NSUInteger  x = 0UL;
    {
    NSCharacterSet *digits      = [NSCharacterSet decimalDigitCharacterSet];
    NSCharacterSet *nonDigits   = [digits invertedSet];
    NSScanner      *scanner     = [NSScanner scannerWithString:value2String];
    NSString       *csIntString = [valueString stringByTrimmingCharactersInSet:nonDigits];
    NSString       *reString    = [valueString stringByMatching:reRegex capture:1L];
    NSString       *re2String   = [valueString stringByMatching:re2Regex];
    [scanner scanInt:&value];
    NSLog(@"digits      : %p, size: %lu", digits, malloc_size(digits));
    NSLog(@"nonDigits   : %p, size: %lu", nonDigits, malloc_size(nonDigits));
    NSLog(@"scanner     : %p, size: %lu, int: %d", scanner, malloc_size(scanner), value);
    NSLog(@"csIntString : %p, size: %lu, '%@' int: %d", csIntString, malloc_size(csIntString), csIntString, [csIntString intValue]);
    NSLog(@"reString    : %p, size: %lu, '%@' int: %d", reString, malloc_size(reString), reString, [reString intValue]);
    NSLog(@"re2String   : %p, size: %lu, '%@' int: %d", re2String, malloc_size(re2String), re2String, [re2String intValue]);
    NSLog(@"intValue    : %d", [value2String intValue]);
    NSLog(@"rklIntValue : %d", [valueString rklIntValue]);
    }
    for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [[valueString stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]] intValue]; } csTime = (cpuTimeUsed() - startTime) / (double)x;
    for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value =  [[valueString stringByMatching:reRegex capture:1L] intValue]; } reTime = (cpuTimeUsed() - startTime) / (double)x;
    for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value =  [[valueString stringByMatching:re2Regex] intValue]; } re2Time = (cpuTimeUsed() - startTime) / (double)x;
    for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value =  [valueString rklIntValue]; } rklTime = (cpuTimeUsed() - startTime) / (double)x;
    for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { value = [value2String intValue]; } ivTime = (cpuTimeUsed() - startTime) / (double)x;
    for(x = 0UL, startTime = cpuTimeUsed(); x < 100000UL; x++) { [[NSScanner scannerWithString:value2String] scanInt:&value]; } scTime = (cpuTimeUsed() - startTime) / (double)x;
    NSLog(@"csTime : %.5lfus", csTime * 1000000.0);
    NSLog(@"reTime : %.5lfus", reTime * 1000000.0);
    NSLog(@"re2Time: %.5lfus", re2Time * 1000000.0);
    NSLog(@"scTime : %.5lfus", scTime * 1000000.0);
    NSLog(@"ivTime : %.5lfus", ivTime * 1000000.0);
    NSLog(@"rklTime: %.5lfus", rklTime * 1000000.0);
    [NSString clearStringCache];
    [pool release]; pool = NULL;
    return(0);
    }
    Wow, excelente, gracias por ir a los esfuerzos para medir el rendimiento! +1
    Tal vez usted podría actualizar con los resultados de mi propia respuesta que me acaba de agregar. También, puede agregar el código que escribió para realizar el punto de referencia por favor.

    OriginalEl autor johne

  3. 8

    Si el valor entero está siempre en el principio de la cadena, puede simplemente usar intValue.

    NSString *string = @"123hello";
    int myInt = [string intValue];
    Una vez más, como @Nikiolai Ruhe la respuesta, esto sólo funcionará si no hay cartas antes del número. Este podría ser el caso, pero la pregunta dice «la primera que ocurren número», lo que significa que no pueden ser letras antes de. =)
    Sí, pero el autor de la pregunta de ejemplos muestran el número siempre al principio. Y técnicamente, el autor de la pregunta es confusa «int» y «Entero», por lo que también se podría utilizar -[NSString integerValue] en su lugar.
    simple y funciona muy bien.

    OriginalEl autor zpasternack

  4. 3

    Probablemente voy a utilizar una expresión regular (implementado con el estelar RegexKitLite). Entonces sería algo así como:

    #import "RegexKitLite.h"
    NSString * original = @"foo 220hello";
    NSString * number = [original stringByMatching:@"[^\\d]*(\\d+)" capture:1];
    return [number integerValue];

    El regex @»[^\s]*(\d+)» significa «cualquier número de caracteres no numéricos, seguido por al menos un carácter numérico».

    downvote para una exageración.
    bueno, yo no voy a voto porque usted puede tener RegexKitLite en su proyecto, pero para mis propósitos, sí, es un poco demasiado. Gracias de todas formas +1
    No downvote aquí, pero estoy de acuerdo en que NSScanner es mucho más limpia de hacerlo. En particular, ya que tendría que escribir un nuevo regex para los distintos tipos de números, en donde, como con NSScanner sólo podría cambiar a scanFloat: o scanDouble: etc.
    Cuando vi esta pregunta, inmediatamente pensé en NSScanner demasiado. Antes de leer las respuestas que es. 😉

    OriginalEl autor Dave DeLong

  5. 0

    Vine con mi propia respuesta, potencialmente, más rápido y más fácil que los otros, siempre.

    Mi respuesta no asuma que usted saber la posición del número comienza y termina, aunque…

    NSString *myString = @"21sss";
    int numberAtStart = [[myString substringToIndex:2] intValue];

    Se puede llegar a trabajar de la otra manera:

    NSString *myString = @"sss22";
    int numberAtEnd = [[myString substringFromIndex:3] intValue];

    OriginalEl autor Brock Woolf

  6. -1
    int i;
    NSString* string;
    i = [string intValue];

    OriginalEl autor

Dejar respuesta

Please enter your comment!
Please enter your name here