将每个单词的首字母转换为大写

13

我有一个字符串,需要使用xsl将每个单词的首字母转换为大写,其余为小写。例如:

输入字符串= dInEsh sAchdeV kApil Muk

期望的输出字符串= Dinesh Sachdev Kapil Muk

虽然我知道需要使用translate函数来实现,但我该如何在XSLT 1.0中将每个单词的首字母转换为大写,其余所有字母转换为小写?

谢谢

9个回答

18

以下内容并不是“完美的”,我相信有人(主要是迪米特里)可以想出更简单的东西(尤其是在XSLT 2.0中)…但我已经测试过它,它能够工作

<xsl:template name="CamelCase">
  <xsl:param name="text"/>
  <xsl:choose>
    <xsl:when test="contains($text,' ')">
      <xsl:call-template name="CamelCaseWord">
        <xsl:with-param name="text" select="substring-before($text,' ')"/>
      </xsl:call-template>
      <xsl:text> </xsl:text>
      <xsl:call-template name="CamelCase">
        <xsl:with-param name="text" select="substring-after($text,' ')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="CamelCaseWord">
        <xsl:with-param name="text" select="$text"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="CamelCaseWord">
  <xsl:param name="text"/>
  <xsl:value-of select="translate(substring($text,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" /><xsl:value-of select="translate(substring($text,2,string-length($text)-1),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')" />
</xsl:template>
基本思想是调用CamelCase,如果它找到一个空格,则对空格之前的所有内容(即第一个单词)运行CamelCaseWord,然后再使用剩余的句子(即空格之后的内容)调用CamelCase。否则,如果没有找到空格(因为已经到了句子中的最后一个单词),则只调用CamelCaseWord

CamelCaseWord 模板将第一个字符从小写转换为大写(如果需要),并将所有剩余字符从大写转换为小写(如果需要)。

因此,要调用它,你需要...

<xsl:call-template name="CamelCase">
   <xsl:with-param name="text">dInEsh sAchdeV kApil Muk</xsl:with-param>
</xsl:call-template>

非常感谢。这确实帮助我将第一个字符转换为大写。但是,这个假设字符串之间有空格。我该如何动态查找任何非字母数字字符而不是空格,并将第一个字母大写?例如,O'connel 将变成 O'Connel,dave-manel 将变成 Dave-Manel。 - Patty
@Patty - 我已经很久没有做 XSLT 了。恐怕我无法回答你的补充问题。最好将其作为一个全新的问题进行询问。很抱歉我不能提供更多帮助。 - freefaller
没问题,不用担心,谢谢您的回复。 - Patty

5

补充:

我在问题中错过了1.0版本的要求。这仅适用于2.0版本及以上。

以下是原始答案。

我认为这个曾经对我起作用。声明一个函数:

<xsl:function name="my:titleCase" as="xs:string">
    <xsl:param name="s" as="xs:string"/>
    <xsl:choose>
        <xsl:when test="lower-case($s)=('and','or')">
            <xsl:value-of select="lower-case($s)"/>
        </xsl:when>
        <xsl:when test="$s=upper-case($s)">
            <xsl:value-of select="$s"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="concat(upper-case(substring($s, 1, 1)), lower-case(substring($s, 2)))"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:function>

并使用它:

<xsl:sequence select="string-join(for $x in tokenize($text,'\s') return my:titleCase($x),' ')"/>

感谢samjudson提供的文章 => http://p2p.wrox.com/xslt/80938-title-case-string.html


OP有xslt-1.0标签... lower-caseupper-case在1.0中不可用。 - freefaller
@freefaller 哦,没注意到。将答案更改以反映这一点,供将来参考。谢谢! - Kris

4

这里是一份8年前的FXSL 1.x解决方案(完全使用XSLT 1.0编写的XSLT 1.0库):

test-strSplit-to-Words10.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
>
   <xsl:import href="strSplitWordDel.xsl"/>

   <!-- To be applied on: test-strSplit-to-Words10.xml -->

   <xsl:output indent="yes" omit-xml-declaration="yes"/>
   <xsl:variable name="vLower" 
        select="'abcdefgijklmnopqrstuvwxyz'"/>
   <xsl:variable name="vUpper" 
        select="'ABCDEFGIJKLMNOPQRSTUVWXYZ'"/>

    <xsl:template match="/">
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-word-del">
          <xsl:with-param name="pStr" select="/"/>
          <xsl:with-param name="pDelimiters" 
                          select="', .(&#9;&#10;&#13;'"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
    </xsl:template>

    <xsl:template match="word">
       <xsl:choose>
         <xsl:when test="not(position() = last())">
           <xsl:value-of 
           select="translate(substring(.,1,1),$vLower,$vUpper)"/>
           <xsl:value-of select="substring(.,2)"/>
         </xsl:when>
         <xsl:otherwise>
           <xsl:value-of select="."/>
         </xsl:otherwise>
       </xsl:choose>
    </xsl:template>

    <xsl:template match="delim">
      <xsl:value-of select="."/>
    </xsl:template>
</xsl:stylesheet>

当对以下XML文档(test-strSplit-to-Words10.xml)应用此转换时:
<t>004.lightning crashes (live).mp3</t>

结果是:

004.Lightning Crashes (Live).mp3

当应用于这个XML文档(您提供的示例)时:

dInEsh sAchdeV kApil Muk

结果为:

DInEsh SAchdeV KApil Muk

只需要稍作修改,我们就可以得到以下代码:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
>
   <xsl:import href="strSplitWordDel.xsl"/>

   <!-- To be applied on: test-strSplit-to-Words10.xml -->

   <xsl:output indent="yes" omit-xml-declaration="yes"/>
   <xsl:variable name="vLower" 
        select="'abcdefgijklmnopqrstuvwxyz'"/>
   <xsl:variable name="vUpper" 
        select="'ABCDEFGIJKLMNOPQRSTUVWXYZ'"/>

    <xsl:template match="/">
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-word-del">
          <xsl:with-param name="pStr" select="/"/>
          <xsl:with-param name="pDelimiters" 
                          select="', .(&#9;&#10;&#13;'"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
    </xsl:template>

    <xsl:template match="word">
           <xsl:value-of 
           select="translate(substring(.,1,1),$vLower,$vUpper)"/>
           <xsl:value-of select="translate(substring(.,2), $vUpper, $vLower)"/>
    </xsl:template>

    <xsl:template match="delim">
      <xsl:value-of select="."/>
    </xsl:template>
</xsl:stylesheet>

现在已经产生了期望的结果:

Dinesh Sachdev Kapil Muk

解释:

FXSLstr-split-word-del 模板可以用于将文本按照一个或多个指定的分隔符进行分词。


2

以下是另一种简短的解决方案。它使用纯XSL-T 2.0。我知道OP需要XSL-T 1.0,但由于在2015年这个页面在谷歌上排名第一,所以这似乎更相关:

<xsl:function name="xx:fixCase">
    <xsl:param name="text" />
    <xsl:for-each select="tokenize($text,' ')">
        <xsl:value-of select="upper-case(substring(.,1,1))" />
        <xsl:value-of select="lower-case(substring(.,2))" />
        <xsl:if test="position() ne last()">
            <xsl:text> </xsl:text>
        </xsl:if>
    </xsl:for-each>
</xsl:function>

这里的 'xx' 是您自己的命名空间。


2
你也可以尝试这个: http://www.xsltfunctions.com/xsl/functx_camel-case-to-words.html 它是一个将驼峰式命名转换为单词的工具。
 <xsl:function name="functx:camel-case-to-words" as="xs:string"
                  xmlns:functx="http://www.functx.com">
      <xsl:param name="arg" as="xs:string?"/>
      <xsl:param name="delim" as="xs:string"/>

      <xsl:sequence select="
       concat(substring($arg,1,1),
                 replace(substring($arg,2),'(\p{Lu})',
                            concat($delim, '$1')))
     "/>

</xsl:function>

并且向后:

http://www.xsltfunctions.com/xsl/functx_words-to-camel-case.html
<xsl:function name="functx:words-to-camel-case" as="xs:string"
              xmlns:functx="http://www.functx.com">
  <xsl:param name="arg" as="xs:string?"/>

  <xsl:sequence select="
     string-join((tokenize($arg,'\s+')[1],
       for $word in tokenize($arg,'\s+')[position() > 1]
       return functx:capitalize-first($word))
      ,'')
 "/>

</xsl:function>

0

最终我得到了略微不同的东西。使用另一个答案,我得到了:

调用toddmo:proper-case#1 ("Hey", " ")的结果不允许是多个项目的序列

所以这样就解决了。

<xsl:stylesheet xmlns:toddmo="https://todd.mo"

<xsl:function name="toddmo:proper-case" as="xs:string">
    <xsl:param name="text" />
    <xsl:variable name="words">
        <xsl:for-each select="tokenize($text,' ')">
            <xsl:value-of select="concat(upper-case(substring(.,1,1)),lower-case(substring(.,2)))"/>
            <xsl:if test="position()!=last()">
                <xsl:text> </xsl:text>                    
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>
    <xsl:value-of select="string-join($words,'')"/>
</xsl:function>

测试

<xsl:value-of select="toddmo:proper-case('hI my name is toddmo')"/>

输出

嗨,我的名字是Toddmo


0
一个非常简短的解决方案,利用EXSLT的split()函数:
<xsl:variable name='text' select='"dInEsh sAchdeV kApil Muk"' />
<xsl:variable name='lowers' select='"abcdefghijklmnopqrstuvwxyz"' />
<xsl:variable name='uppers' select='"ABCDEFGHIJKLMNOPQRSTUVWXYZ"' />

<xsl:template match="/">

    <xsl:for-each select='str:split($text, " ")'>
        <xsl:value-of select='concat(
            translate(substring(., 1, 1), $lowers, $uppers),
            translate(substring(., 2), $uppers, $lowers),
            " "
        )' />
    </xsl:for-each>
</xsl:template>

工作演示:http://www.xmlplayground.com/CNmKdF


0

在XQUERY上的同一函数:

将Xquery转换为驼峰命名法的函数。

declare function xf:toCamelCase($text as xs:string?) as xs:string{
    if(contains($text,' ')) then
        fn:concat(xf:CamelCaseWord(substring-before($text,' ')),' ', xf:toCamelCase(substring-after($text,' ')))
    else
        xf:CamelCaseWord($text)
};

declare function xf:CamelCaseWord($text as xs:string?) as xs:string{
    fn:concat( translate(substring($text,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 
                translate(substring($text,2,string-length($text)-1),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'))
};

0

可以使用单行表达式来实现。 试一下

string-join(for $x in tokenize($text,'\s') return concat(upper-case(substring($x, 1, 1)), lower-case(substring($x, 2))),' ')

注意 - 这已经通过Orbeon Forms公式进行了验证。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接