国际化

什么是国际化

指软件在开发时就应该具备支持多种语言和地区的功能,当应对不同国家和地区的用户访问,针对不同国家和地区的用户,提供相应的、符合来访者阅读习惯的页面和数据。





由于国际化internationalization这个单词的首字母“i”和尾字母“n”之间有18个字符,因此国际化被简称为i18n。

实现国际化的API


Locale类


几乎所有对国际化的支持都需要依赖这个类

如何创建Locale实例对象


使用Locale类的构造方法





有三个重载的构造方法

public Locale(String language)

public Locale(String language,String country)

public Locale(String language,String country,String variant)

如果Locale对象仅仅用于说明当地的语言信息,则使用第一个构造方法:

Locale enLocale = new Locale("en");





如果要创建一个标识当地语言信息以及国家信息的Locale对象时,使用第二个构造方法即可:

Locale enLocale = new Locale("en","US");





如果要创建一个带有附加变量的Locale对象时,则使用第三个构造方法即可,例如,创建一个标识传统西班牙排序的Local对象,具体示例如下:

Locale enLocale = new Locale("es","ES","Traditional_WIN");

使用Locale类的常量



如Locale.ENGLISH、Locale.GERMAN、Locale.US、Locale.HK等,这些常量分别对应一些提前创建好的表示不同语言和国家的Locale对象。

Locale locale=Locale.CHINESE;

常用方法

String getCountry()
获取Locale实例对象的ISO国家代码

String getLanguage()
获取Locale实例对象的ISO语言代码

String getVariant()
获取Locale实例对象的变量编码

String getDisplayCountry()
获取Locale实例对象适合显示给用户的国家名称

String getDisplayCountry(Locale inLocale)

String getDisplayLanguage()
获取Locale实例对象适合显示给用户的语言名称

String getDisplayLanguage(Locale inLocale) 

String getDisplayName()
获取Local实例对象显示的名称

String getDisplayName(Locale inLocale)

ResourceBundle类


在开发一个国际化的Web应用时,通常会存储许多用于保存各个国家语言的资源文件,这些资源文件都需要使用类加载器来加载,这样的加载方式比较麻烦。





为了方便获取这些资源文件,JDK提供了一个ResourceBundle类,该类用于描述一个资源包,对于不同的本地环境,可以有不同的ResourceBundle对象与之关联。

资源包简介


在设计一个国际化的应用时,应该把程序显示的文本内容(例如,菜单和按钮的标题)从源文件中分离出来,放在独立的资源文件(扩展名为.properties的文件)中,并针对不同的本地环境编写不同的资源文件。





一个资源包中每个资源文件都必须拥有共同的基名。除了基名,每个资源文件的名称中还必须有标识其本地信息的附加部分。

例如:一个资源包的基名是myproperties,对应资源文件的名称如下:

默认资源文件名:myproperites.properties

对应的中文资源文件名为:myproperites_zh.properties

对应的英文资源文件名为:myproperites_en.properties

资源文件格式


username=itcast

创建ResourceBundle对象,读取资源文件


ResourceBundle类提供了两个用于创建ResourceBundle对象的静态方法,该方法用于装载资源文件,并创建ResourceBundle实例





getBundle(String baseName)

getBundle(String baseName,Locale locale)

参数baseName用于指定资源文件的名称,参数Locale用于指定使用的Locale对象,如果没有指定Locale参数,则使用本地默认的Locale

Locale locale=Locale.US;

ResourceBundle myResources= ResourceBundle.getBundle("MyResources",locale);

DateFormat类


可以将一个日期/时间对象格式化为表示某个特定地区的日期/时间字符串,也可以将某个地区的日期/时间的字符串解析为相应的Date对象。





DateFormat中的常量

包括SHORT 、DEFAULT、MEDIUM、LONG、FULL,在实例化DateFormat对象时,可以使用这些常量控制日期/时间的显示长度。

SHORT模式完全是数字的,这个日期/时间显示的格式为“13-12-11 下午4:41”;

MEDIUM模式比SHORT模式长些,这个日期/时间显示的格式为“2013-12-11 16:41:20”

LONG模式比MEDIUM模式更长一些,这个日期/时间显示的格式为“2013年12月11日 下午04时41分20秒”

FULL模式指定日期/时间的完整格式,这个日期/时间显示的格式为“2013年12月11日 星期三 下午04时41分20秒 CST”

DEFAULT表示默认的显示模式,它的值为MEDIUM。







获取DateFormat类的实例对象

由于DateFormat是一个抽象类,不能使用构造方法创建实例对象,因此,JDK提供了一些用于获取DateFormat实例对象的静态方法





getDateInstance(int style, Locale aLocale):以指定的日期显示模式和本地信息来获取DateFormat对象,该对象不处理时间值部分。

getTimeInstance(int style, Locale aLocale):以指定的时间显示模式和本地信息来获取DateFormat对象,该对象不处理日期值部分。

getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale):以单独指定的日期显示模式、时间显示模式和本地信息来获得DateFormat实例对象。





日期/时间的格式化和解析

format()方法

将日期/时间对象格式化为符合本地习惯的字符串

String dateTime=DateFormat.getDateTimeInstance().format(new Date());

parse()方法

将某个本地习惯的日期/时间字符串解析为Date对象

DateFormat df = DateFormat.getDateInstance(DateFormat.Long,Locale.US);

Date date = df.parse("September 15,2013");

NumberFormat类


可以将一个数值格式化为本地格式的字符串,也可以将某个本地格式的数值字符串解析为对应的数值

获取NumberFormat类的实例对象

实例化NumberFormat类时,可以使用Locale对象作为参数,也可以不使用,接下来列举使用Locale对象作为参数的方法

getNumberInstance(Locale locale):以参数Locale对象所标识的本地信息来获取具有多种用途的NumberFormat实例对象

getIntegerInstance(Locale locale):以参数Locale对象所标识的本地信息来获取处理整数的NumberFormat实例对象

getCurrencyInstance(Locale locale):以参数Locale对象所标识的本地信息来获取处理货币的NumberFormat实例对象

getPercentInstance(Locale locale):以参数Locale对象所标识的本地信息来获取处理百分比数值的NumberFormat实例对象

数值的格式化和解析





format()方法

将一个数值格式化为符合某个国家或地区习惯的数值字符串

String numberString = NumberFormat.getInstance().format(12345);





parse()方法

将符合某个国家或地区习惯的数值字符串解析为对应的Number对象

NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);

Number number=nf.parse("12.345");

MessageFormat类


用参数替换模式字符串中的占位符的方式,它将根据模式字符串中包含的占位符产生一系列的格式化对象,然后调用这些格式化对象对参数进行格式化,并将格式化后的结果字符串插入到模式字符串中的适当位置

模式化字符串与占位符

On {0},there was {1} on planet {2}.

花括号以及花括号内的数字被称为占位符,如{0}、{1},这些占位符都会被MessageFormat格式化的参数所代替

MessageFormat类格式化模式字符串

创建MessageFormat对象

两个构造方法

public MessageFormat(String pattern)

public MessageFormat(String pattern, Locale locale)

调用MessageFormat对象的format()方法

用于执行模式字符串的格式化操作,在调用format()方法时,需要传递一个Object类型的参数数组,数组中的每个元素分别用于替换模式字符串中与其索引对应的占位符。

加强对MessageFormat的学习

占位符总共有三种形式

{ ArgumentIndex }

{ ArgumentIndex , FormatType }

{ ArgumentIndex, FormatType, FormatStyle }

开发国际化的Web应用


获取Web应用中的本地信息


要实现Web应用的国际化,首先需要获取客户端浏览器的本地信息,根据客户端浏览器的本地信息来访问相应的资源文件。

大多数Web浏览器通常会在HTTP请求消息中通过Accept-Language消息头附带本地信息,Web容器则可以根据Accept-Language消息头创建标识客户端本地信息的Locale对象。

HttpServletRequest对象提供了两个方法





getLocale()方法

用于返回代表客户端的首选本地信息的Locale对象

getLocales()方法

用于返回一个Enumeration集合

国际化标签库


为了简化Web应用的国际化开发,JSTL中提供了一个用于实现国际化和格式化功能的标签库,简称为国际化标签库,其前缀名为fmt。





国际化标签库封装了java.util和java.text这两个包中与国际化相关的API类的功能,并提供了绑定资源包和从资源包中的本地资源文件内读取文本内容的标签、对数值和日期等本地敏感的数据按本地化信息进行显示和解析的标签、按本地特定的时区来调整时间的标签。

如果要在JSP页面中使用格式化的标签,需要用taglib指令指明这个标签库的路径为

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

设置全局信息标签


<fmt:setLocale>标签



设定语言地区

用于在JSP页面中显式地设置用户的本地化信息,并将设置的本地化信息以Locale对象的形式保存在某个Web域中。

使用<fmt:setLocale>标签设置本地化信息后,国际化标签库中的其他标签将使用该本地化信息,而忽略客户端浏览器传递过来的本地信息。

用来设定用户的语言地区代码,即设置格式化或解析日期/时间、数值时所使用的语言环境。

<fmt:setLocale value="locale" 

[variant="variant"] 

[scope="{page|request|session|application}"]/>

variant:特定web浏览器或供应商代码

可以从http://www.w3.org/查询语言代码,在http://unicode.org找到国家和地区代码。例如en表示英文、en_US表示英文(美国)、zh_CN表示中文(中国)。

<fmt:requestEncoding>标签



设定请求的字符编码集合

用于设置统一的请求消息的字符集编码

用于向JSP容器指定请求(request)的字符编码

<fmt:requestEncoding [value="charsetName"]/>

value属性用于指定请求消息的字符集编码,其类型为String,支持动态属性值

设置请求编码:<fmt:requestEncoding value="gb2312"/>





相当于JSP内置对象中的setCharacterEncoding()方法

几点说明:

调用<fmt:requestEncoding>标签能够正确解码请求参数值中的非ISO-8859-1编码的字符,但是必须在获取任何请求参数之前进行调用。

有的浏览器没有完全遵守HTTP规范,在请求消息中没有包含Content-Type请求头,这时需要使用<fmt:requestEncoding>标签来设置请求编码。

如果不设置value属性,首先会采用请求消息的Content-Type头中定义的字符集编码,其次会采用session域中的javax.servlet.jsp.jstl.fmt.request.charest属性的值,再次会采用ISO-8859-1字符集编码。

信息显示标签


获取系统设定的语言资源,从而可以轻易地做到多国化信息

<fmt:message>标签

用于从一个资源包中读取信息并进行格式化输出

语法1:没有标签体的情况

<fmt:message key="messageKey" 

[bundle="resourceBundle"]

[var="varName"]

scope="{page|request|session|application}"/>





语法2:在标签体中指定格式化文本串中的占位符参数的情况

<fmt:message key="messageKey" 

[bundle="resourceBundle"]

[var="varName"]

scope="{page|request|session|application}">

<fmt:param>subtags

</fmt:message>





语法3:在标签体重指定消息关键字和可选择的占位符参数

<fmt:message key="messageKey" 

[bundle="resourceBundle"]

[var="varName"]

scope="{page|request|session|application}">

key

optional <fmt:param>subtags

</fmt:message>





<fmt:Bundle>标签





用于根据<fmt:setLocale>标签设置的本地化信息创建一个资源包实例对象,但它创建的ResourceBundle实例对象只在其标签内有效。

用来根据本地化环境来选择所需的资源包

<fmt:Bundle basename="basename" [prefix="prefix"]>

body content

</fmt:Bundle>





<fmt:setBundle>标签





用于创建一个资源包实例对象,并将其绑定到一个Web域的属性上。用来为本地化环境设置一个缺省的资源包,在<fmt:message>标签的特定作用域内起作用<fmt:setBundle basename="basename" [var="varName"] [scope="{page|request|session|application}"]/>

basename属性:用于指定创建ResourceBundle实例对象的基名。

var属性:用于指定将创建出的ResourceBundle实例对象保存到  Web域中的属性名称。

scope属性:用于指定将创建出的ResourceBundle实例对象保存在哪个Web作用域中。

一些特性:

如果basename属性的值为null、空字符串或找不到basename属性指定的资源,<fmt:setBundle>标签保存到Web域中的属性的值为null。

如果没有指定var属性,<fmt:setBundle>标签将把ResourceBundle实例对象以域属性名javax.servlet.jsp.jstl.fmt.localizationContext保存到Web域中。所有没有嵌套在<fmt:Bundle>标签中且未指定bundle属性的<fmt:formatDate>都将使用该标签创建的资源包。

<fmt:param>标签



用于为格式化文本串中的占位符设置参数值,它只能嵌套在<fmt:message>标签内使用

用来为<fmt:message>标签指定文本消息参数值,动态的设定参数

语法1:用value属性指定参数值

<fmt:param value="messageParameter"/>





语法2:在标签体重指定参数的值的情况:

<fmt:param>

       Body content

</fmt:param>

在使用消息标签之前,必须首先创建资源文件,其扩展名为properties,资源文件内容必须按照“key=value”的格式,也就是一个索引对应一个值。资源文件需要放置在应用程序的WEB-INF/classes目录下。

数字及日期格式化标签


<fmt:timeZone>标签





用于设置时区,但它的设置值只对其标签体部分有效

<fmt:timeZone value="timeZone">

Body content

</fmt:timeZone>

value属性支持动态属性值,它的值可以是一个命名时区的字符串,也可以是java.util.TimeZone类的一个实例对象。如果value属性的值为null或空字符串,标签体重的内容就使用GMT的0基准时区。如果value属性的值是表示时区名称的字符串,这个字符串通过java.util.TimeZone.getTimeZone()静态方法被解析为java.util.TimeZone类的实例对象。





<fmt:setTimeZone>标签





用于在JSP页面中显式的设置时区,并将设置的时区信息以TimeZone对象的形式保存在某个Web域中

<fmt:setTimeZone value="timeZone" 

[var="varName"]

[scope="{page|request|session|application}"]/> 

  value属性用于指定表示时区的ID字符串或TimeZone对象。其值的设置与<fmt:timeZone>标签相同。<fmt:setTimeZone>标签将创建出的TimeZone实例对象保存在scope属性指定的Web域中,如果没有指定var属性,其在Web域中的属性名称为javax.servlet.jsp.jstl.fmt.timeZone,所有没有嵌套在其他<fmt:timeZone>标签中且未指定timezone属性的<fmt:formatDate>标签都将使用该属性名关联的时区。





<fmt:formatDate>标签





用于对日期和时间按本地化信息进行格式化,或对日期和时间按JSP页面作者自定义的格式进行格式化。

<fmt:formatDate value="date" 

[type="{time|date|both}"]

[dateStyle="{dafault|short|medium|long|full}"]

[timeStyle="{dafault|short|medium|long|full}"]

[pattern="customPattern"]

[timeZone="timeZone"]

[var="varName"]

[scope="{page|request|session|application}"]/>





value
java.util.Date类
指定要进行格式化的日期/时间

type
String
指定要设置格式的Date实例的部分,可为time/date/both

dateStyle
String
设置使用于特定语言环境的日期格式,可为default/short/medium/long/full

timeStyle
String
设置使用于特定语言环境的时间格式,可为default/short/medium/long/full

pattern
String
用户自定义日期/时间格式

timeZone
String或java.util.TimeZone
需格式化的时间所在时区

var
String
用来存储格式化后结果的范围变量名

scope
String
范围变量var的作用域





<fmt:parseDate>标签





与<fmt:formatDate>标签的作用相反,它用于把已经格式化的标准日期格式解析成java.util.Date实例对象

<fmt:parseDate value="dateString" 

[type="{time|date|both}"]

[dateStyle="{dafault|short|medium|long|full}"]

[timeStyle="{dafault|short|medium|long|full}"]

[pattern="customPattern"]

[timeZone="timeZone"]

[parseLocale="parseLocale"]

[var="varName"]

[scope="{page|request|session|application}"]>

date value to be parsed

  </fmt:parseDate>





value
java.util.Date类
指定要进行解析的日期/时间

type
String
指定需要解析的Date实例的部分,可为time/date/both

dateStyle
String
设置Date实例日期部分解析方式,可为default/short/medium/long/full

timeStyle
String
设置Date实例时间部分解析方式,可为default/short/medium/long/full

pattern
String
用户自定义日期/时间的格式以确定解析方式

timeZone
String或java.util.TimeZone
需解析的时间所在时区

parseLocale
String或为java.util.Locale类
指定一种语言环境,根据这种语言环境来解析日期/时间值

var
String
用来存储解析结果的范围变量名

scope
String
范围变量var的作用域





需要注意的是,value属性的值必须是合法的日期/时间字符串,否则会抛出异常。





<fmt:formatNumber>标签





用于将数值、货币或百分数按本地化信息进行格式化

<fmt:formatNumber [type="{number|currency|percent}"] 

[pattern="customPattern"]

[currencyCode="currencyCode"]

[currencySymbol="currencySymbol"]

[groupingUsed="true|false"]

[maxIntegerDigits="maxIntegerDigits"]

[minIntegerDigits="minIntegerDigits"]

[maxFractionDigits="maxFractionDigits"]

[minFractionDigits="minFractionDigits"]

[var="varName"]

[scope="{page|request|session|application}"]>







Number 

</fmt:formatNumber>





value
String或为Number
指定需格式化的数值

type
String
指定格式化方式,其取值有number、currency、percent

pattern
String
用户自定义格式化方式

currencyCode
String
设置所显示数值的货币单位

currencySymbol
String
设置所显示数值的货币符号

maxintegerDigits
int
设置格式化后数值的最大整数位数

minintegerDigits
int
设置格式化后数值的最小整数位数

maxFractionDigits
int
设置格式化后数值的最大小数位数

minFractionDigits
int
设置格式化后数值的最小小数位数

groupingUsed
Boolean
指定格式化后是否要对小数点前面的数字分组

var
String
用来存储格式化后数值的范围变量名称

scope
String
范围变量var的作用域





需要注意的是,如果<fmt:formatNumber>标签不能确定格式化的本地环境,就使用Number.toString()作为输出格式。





<fmt:parseNumber>标签





与<fmt:formatNumber>标签的作用相反,它用于将一个按本地化方式被格式化后的数值、货币或百分数解析为数值

<fmt:parseNumber [type="{number|currency|percent}"] 

    [pattern="customPattern"]

    [parseLocale="parseLocale"]

    [intergerOnly="true|false"]

    [var="varName"]

    [scope="{page|request|session|application}"]>





    Numberic value to be parsed 

</fmt:parseNumber>

parseLocale属性用于指定解析字符串时所用的本地环境。intergerOnly属性用于指定是否只解析数值字符串的整数部分。在使用<fmt:parseNumber>标签解析值时要特别注意,它执行的解析非常严格,要解析的数值字符串必须严格符合特定的本地环境及pattern属性设置的自定义格式。





value
String
需解析的数值字符串

type
String
指定数值字符串,其取值有number、currency、percentage

pattern
String
用户自定义数值字符串的格式以确定解析方式

parseLocale
String或为java.util.Locale类
指定一种语言环境,根据这种语言环境来解析数值字符串

integerOnly
Boolean
设置要解析的部分是否仅为数值字符串的整数部分

var
String
存储数值解析结果的范围变量名

scope
String
范围变量var的作用域

Java精选笔记_国际化的更多相关文章

  1. Java精选笔记_自定义标签

    自定义标签 自定义标签入门 什么是自定义标签 自定义标签可以有效地将HTML代码与Java代码分离,从而使不懂Java编程的HTML设计人员也可以编写出功能强大的JSP页面 JSP规范中定义了多个用于 ...

  2. Java精选笔记_文件上传与下载

    文件上传与下载 如何实现文件上传 在Web应用中,由于大多数文件的上传都是通过表单的形式提交给服务器的,因此,要想在程序中实现文件上传的功能,首先得创建一个用于提交上传文件的表单页面. 为了使Serv ...

  3. Java精选笔记_会话技术

    会话及其会话技术 会话概述 指的是一个客户端(浏览器)与Web服务器之间连续发生的一系列请求和响应过程. 会话:从浏览器开启到浏览器关闭.会话技术:用来保存在会话期间 浏览器和服务器所产生的数据. 在 ...

  4. Java精选笔记_网络编程

    网络编程 概述 现在的网络编程基本上都是基于请求/响应方式的,也就是一个设备发送请求数据给另外一个,然后接收另一个设备的反馈. 在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(C ...

  5. Java精选笔记_其他IO流(ObjectInputStream、DataInputStream、PrintStream、标准输入输出流)

    其他IO流 ObjectInputStream和ObjectOutputStream 如果希望永久将对象转为字节数据写入到硬盘上,即对象序列化,可以使用ObjectOutputStream(对象输出流 ...

  6. Java精选笔记_集合概述(Collection接口、Collections工具类、Arrays工具类)

    集合概述 集合有时又称为容器,简单地说,它是一个对象,能将具有相同性质的多个元素汇聚成一个整体.集合被用于存储.获取.操纵和传输聚合的数据. 使用集合的技巧 看到Array就是数组结构,有角标,查询速 ...

  7. Java精选笔记_多线程(创建、生命周期及状态转换、调度、同步、通信)

    线程概述 在应用程序中,不同的程序块是可以同时运行的,这种多个程序块同时运行的现象被称作并发执行. 多线程可以使程序在同一时间内完成很多操作. 多线程就是指一个应用程序中有多条并发执行的线索,每条线索 ...

  8. Java精选笔记_面向对象(多态、异常)

    多态 概述 可以理解为事物存在的多种体现形态.同样的引用调用同样的方法却做了不同的事情 多态的本质是:一个程序中同名的不同方法. 多态的体现 父类的引用指向子类的对象,父类的引用接收子类的对象. 多态 ...

  9. Java精选笔记_面向对象(包、访问控制、内存机制、垃圾回收机制)

    包 包的定义与使用 专门用来存放类的,通常功能相同的类存放在相同的包中. 包的声明只能位于Java源文件的第一行 Java语言中的常用包 java.lang:包含Java语言的核心类,如String. ...

随机推荐

  1. Python的模块调用

    目前运维的Python脚本,是用于同步数据的,分别有n个不同的脚本同步不同的数据,而不同的脚本连接的数据库是一致的,每个脚本都重复写这个数据库连接信息. 这导致测试时,从生产环境切换到测试环境时,需多 ...

  2. LeetCode: Median of Two Sorted Arrays 解题报告

    Median of Two Sorted Arrays There are two sorted arrays A and B of size m and n respectively. Find t ...

  3. c++之—— lambda表达式(有个未能解决的问题等待大佬解答)——(在stack overflow找到了答案)

    谓词: 谓词是一个可调用的表达式,其返回结果是一个能用作条件的值.标准库算法所使用的谓词分为两类:一元谓词,意味着它只接受单一参数:二元谓词,意味着它有两个参数.接受谓词参数的算法对输入序列中的元素调 ...

  4. JAVA-JSP运行机制

    相关资料: <21天学通Java Web开发> 实例操作: 1.调用结束之前的实例“HelloWorld.JSP”页面.2.打开“D:\Ruanjian\apache-tomcat-8.5 ...

  5. JDK1.6.0+Tomcat6.0的安装配置

    JDK1.6.0+Tomcat6.0的安装配置是如何进行的呢?我们按照下面几个步骤来: 1.安装JDK 这是进行JSP开发的重要一步,也是安装JSP引擎(Tomcat.Resin.Weblogic等) ...

  6. Python+Django+SAE系列教程12-----配置MySQL数据库

    由于SAE上支持的是Mysql,首先我们要在本地配置一个Mysql的环境 ,我在网上找到MySQL-python-1.2.4b4.win32-py2.7.exe,并双击 安装 选择典型安装 安装结束后 ...

  7. 【Unity】用代码给按钮动态添加点击事件

    问题:多数情况下用UGUI的Button控件身上的OnClick()列表可以指明该按钮点击后触发的回调.现在想要调用自定义脚本里的方法,当这个脚本挂在Button所属的Canvas身上时,传入Canv ...

  8. git学习(三):git暂存区

    回顾之前学过的命令: git init // 初始化一个项目 git add // 将文件交给工作区 git commit // 提交修改 查看提交日志: git log // 查看提交日志 git ...

  9. C中结构体的存储分配

    C中结构体的存储分配 对于C语言中结构体所占的存储空间的大小,也一直是笔试面试的常客,今天好好看了一下这方面,以前一直以为很清楚了,今天通过各种实际测试举例,发现原来还是没有搞透彻,好在现在是彻底懂了 ...

  10. R语言实战实现基于用户的简单的推荐系统(数量较少)

    R语言实战实现基于用户的简单的推荐系统(数量较少) a<-c(1,1,1,1,2,2,2,2,3,3,3,4,4,4,5,5,5,5,6,6,7,7) b<-c(1,2,3,4,2,3,4 ...