String para Int em java - dados ruins provável, necessidade de evitar exceções

StackOverflow https://stackoverflow.com/questions/174502

  •  05-07-2019
  •  | 
  •  

Pergunta

Vendo como Java não tem tipos anuláveis, nem tem um TryParse (), Como você lida com a validação de entrada sem lançar uma exceção?

A maneira usual:

String userdata = /*value from gui*/
int val;
try
{
   val = Integer.parseInt(userdata);
}
catch (NumberFormatException nfe)
{
   // bad data - set to sentinel
   val = Integer.MIN_VALUE;
}

Eu poderia usar um regex para verificar se ele é analisável, mas que parece ser um monte de sobrecarga também.

Qual é a melhor prática para lidar com esta situação?

EDIT: Justificativa: Tem havido muita conversa sobre SO sobre o tratamento de exceção, ea atitude geral é que as exceções devem ser usados ??apenas para cenários inesperados. No entanto, penso entrada mau utilizador é esperado, não raro. Sim, ele realmente é um ponto de acadêmico.

Outras edições:

Algumas das respostas demonstram exatamente o que está errado com o SO. Você ignorar a questão que se coloca, e responder a outra pergunta que não tem nada a ver com isso. A pergunta não é perguntar sobre a transição entre as camadas. A questão não está pedindo que a retornar se o número é un-analisável. Para todos sabem, val = Integer.MIN_VALUE; é exatamente a opção certa para a aplicação que esta completamente livre de contexto trecho de código foi tomada a partir de.

Foi útil?

Solução

Isso é muito bonito, embora o retorno MIN_VALUE é uma espécie de questionável, a menos que você tem certeza de que é a coisa certa a ser usado para o que você está usando, essencialmente, como um código de erro. Pelo menos eu documentar o comportamento código de erro, no entanto.

também pode ser útil (dependendo da aplicação) para registrar a entrada ruim, então você pode rastrear.

Outras dicas

se houvesse open source bibliotecas de utilitários que tinham métodos para fazer isso de análise para você ea resposta é sim!

A partir Apache Commons Lang você pode usar NumberUtils.toInt :

// returns defaultValue if the string cannot be parsed.
int i = org.apache.commons.lang.math.NumberUtils.toInt(s, defaultValue);

A partir Google Guava você pode usar Ints.tryParse :

// returns null if the string cannot be parsed
// Will throw a NullPointerException if the string is null
Integer i = com.google.common.primitives.Ints.tryParse(s);

Não há necessidade de escrever seus próprios métodos para números de análise sem jogar exceções.

Para os dados fornecidos pelo usuário, Integer.parseInt é geralmente o método errado, porque ele não suporta internationisation. O pacote java.text é o seu (detalhado) amigo.

try {
    NumberFormat format = NumberFormat.getIntegerInstance(locale);
    format.setParseIntegerOnly(true);
    format.setMaximumIntegerDigits(9);
    ParsePosition pos = new ParsePosition(0);
    int val = format.parse(str, pos).intValue();
    if (pos.getIndex() != str.length()) {
        // ... handle case of extraneous characters after digits ...
    }
    // ... use val ...
} catch (java.text.ParseFormatException exc) {
    // ... handle this case appropriately ...
}

Qual é o problema com a sua abordagem? Eu não acho que fazê-lo dessa maneira vai prejudicar o desempenho do seu aplicativo em tudo. Essa é a maneira correta de fazê-lo. não otimizar prematuramente .

Eu tenho certeza que é má forma, mas eu tenho um conjunto de métodos estáticos em uma classe Utilitários que fazer coisas como Utilities.tryParseInt(String value) que retorna 0 se a string é unparseable e Utilities.tryParseInt(String value, int defaultValue) que permite que você especifique um valor para o uso se parseInt() lança uma exceção.

Eu acredito que há momentos em que devolver um valor conhecido na entrada ruim é perfeitamente aceitável. Um exemplo muito artificial: você pede o usuário para uma data no formato AAAAMMDD e eles dão-lhe a entrada ruim. Pode ser perfeitamente aceitável para fazer algo como Utilities.tryParseInt(date, 19000101) ou Utilities.tryParseInt(date, 29991231); dependendo dos requisitos do programa.

Vou reafirmar o ponto que stinkyminky estava fazendo para o fundo do post:

Uma abordagem validar a entrada geralmente bem aceitos usuário (ou a entrada de arquivos de configuração, etc ...) é a validação de uso antes de realmente processar os dados. Em a maioria casos, este é um movimento bom design, embora possa resultar em várias chamadas para analisar algoritmos.

Depois de saber que você validou corretamente a entrada do usuário, então é seguro analisá-lo e ignorar, ingresse ou converter para RuntimeException o NumberFormatException.

Note que esta abordagem exige que você considere seu modelo em duas partes: o modelo de negócio (onde nós realmente se preocupam com os valores em int ou formato float) eo modelo de interface de usuário (onde realmente queremos permitir que o usuário put em tudo o que quiser).

Para que os dados a serem migrados do modelo de interface do usuário para o modelo de negócio, ele deve passar por uma etapa de validação (isso pode ocorrer em um campo por base campo, mas a maioria dos cenários de chamar para validação em todo o objeto que é sendo configurado).

Se a validação falhar, então o usuário é apresentado com feedback informando-os sobre o que eles fizeram errado e dada a oportunidade de corrigi-lo.

Encadernação bibliotecas como JGoodies Encadernação e JSR 295 fazer este tipo de coisa muito mais fácil de implementar do que possa parecer - e muitas frameworks web fornecer construções que a entrada do usuário separado do modelo de negócios real, apenas a preencher objetos de negócios após a validação estiver concluída .

Em termos de validação de arquivos de configuração (o outro caso de uso apresentado em alguns dos comentários), é uma coisa para especificar um padrão, se um determinado valor não é especificado em tudo - mas errado se os dados são formatados (alguém tipos um 'oh' em vez de um 'zero' - ou copiado do MS Word e todos os back-carrapatos tem um caráter funk unicode), em seguida, é necessário algum tipo de feedback do sistema (mesmo que seja apenas falhando o aplicativo jogando um exceção tempo de execução).

Aqui está como eu fazer isso:

public Integer parseInt(String data) {
  Integer val = null;
  try {
    val = Integer.parseInt(userdata);
  } catch (NumberFormatException nfe) { }
  return val;
}

Em seguida, o nulo sinaliza dados inválidos. Se você quiser um valor padrão, você pode alterá-lo para:

public Integer parseInt(String data,int default) {
  Integer val = default;
  try {
    val = Integer.parseInt(userdata);
  } catch (NumberFormatException nfe) { }
  return val;
}

Eu acho que a melhor prática é o código que você mostrar.

Eu não iria para a alternativa regex devido à sobrecarga.

org.apache.commons.lang.math.NumberUtils.createInteger(String s) tentativa. Isso me ajudou muito. Existem métodos semelhantes existem para duplas, anseia etc.

Você pode usar um Integer, que pode ser definido como nulo se você tiver um valor ruim. Se você estiver usando java 1.6, que irá fornecer boxe auto / unboxing para você.

semântica Cleaner (Java 8 OptionalInt)

Para Java 8+, eu provavelmente iria usar RegEx para pré-filtro (para evitar a exceção como você anotou) e em seguida, enrole o resultado em um opcional primitiva (para lidar com o problema "default"):

public static OptionalInt toInt(final String input) {
    return input.matches("[+-]?\\d+") 
            ? OptionalInt.of(Integer.parseInt(input)) 
            : OptionalInt.empty();
}

Se você tiver muitas entradas de corda, você pode considerar retornar um IntStream vez de OptionalInt para que você possa flatMap().

Referências

O código acima é ruim porque é equivalente como o seguinte.

// this is bad
int val = Integer.MIN_VALUE;
try
{
   val = Integer.parseInt(userdata);
}
catch (NumberFormatException ignoreException) { }

A exceção é completamente ignorado. Além disso, o token mágica é ruim porque um usuário pode passar em -2147483648 (Integer.MIN_VALUE).

A questão parse-capazes genérico não é benéfico. Pelo contrário, deve ser relevante para o contexto. A sua aplicação tem uma exigência específica. Você pode definir o seu método como

private boolean isUserValueAcceptable(String userData)
{
   return (    isNumber(userData)    
          &&   isInteger(userData)   
          &&   isBetween(userData, Integer.MIN_VALUE, Integer.MAX_VALUE ) 
          );
}

Onde você pode regras documentação de requisitos e você pode criar bem definidas e testáveis.

Se você pode evitar exceções testando antemão como você disse (isParsable ()) pode ser melhor -., Mas não todas as bibliotecas foram projetados com isso em mente

Eu usei o truque e é uma porcaria porque os rastreamentos de pilha no meu sistema embarcado são impressos independentemente de se você pegá-los ou não: (

O mecanismo de exceção é valioso, pois é a única maneira de obter um indicador de status em combinação com um valor de resposta. Além disso, o indicador de estado é padronizado. Se houver um erro você recebe uma exceção. Dessa forma, você não tem que pensar em um indicador de erro mesmo. A controvérsia não é tanto com exceções, mas com exceções verificadas (por exemplo os que você tem que pegar ou declarar).

Pessoalmente, sinto que você escolheu um dos exemplos em que exceções são realmente valioso. É um problema comum que o usuário digita o valor errado, e normalmente você vai precisar voltar para o usuário para o valor correto. Você normalmente não reverter para o valor padrão, se você perguntar ao usuário; que dá ao usuário a impressão de seus assuntos de entrada.

Se você não quer lidar com a exceção, apenas envolvê-la em um RuntimeException (ou classe derivada) e vai permitir que você ignorar a exceção em seu código (e matar o seu aplicativo quando ele ocorre; tudo bem também, por vezes, ).

Alguns exemplos de como eu iria lidar com exceções NumberFormat: Em dados de configuração aplicativo web:

loadCertainProperty(String propVal) {
  try
  {
    val = Integer.parseInt(userdata);
    return val;
  }
  catch (NumberFormatException nfe)
  { // RuntimeException need not be declared
    throw new RuntimeException("Property certainProperty in your configuration is expected to be " +
                               " an integer, but was '" + propVal + "'. Please correct your " +
                               "configuration and start again");
    // After starting an enterprise application the sysadmin should always check availability
    // and can now correct the property value
  }
}

Em uma GUI:

public int askValue() {
  // TODO add opt-out button; see Swing docs for standard dialog handling
  boolean valueOk = false;
  while(!valueOk) {
    try {
      String val = dialog("Please enter integer value for FOO");
      val = Integer.parseInt(userdata);
      return val; 
    } catch (NumberFormatException nfe) {
      // Ignoring this; I don't care how many typo's the customer makes
    }
  }
}

Em um formulário web: devolver o formulário para o usuário com uma mensagem de erro útil e uma chance de corrigir. A maioria dos quadros oferecem uma maneira padronizada de validação.

Integer.MIN_VALUE como NumberFormatException é má idéia.

Você pode adicionar proposta de Projeto Coin para adicionar este método para Integer

@Nullable public static Integer parseInteger (String src) ... ele irá retornar nulo para a entrada de mau

Em seguida, coloque link para sua proposta aqui e vamos todos votar para ele!

PS: Olhe este http://msdn.microsoft.com/en-us/library/bb397679.aspx este é como feio e inchado poderia ser

Coloque um pouco se as declarações na frente dele. se (null! = userdata)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top